xref: /llvm-project/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp (revision 1374aa35a3f62c774548361276a87eb472893262)
1 // RUN: %clang_cc1 --std=c++20 -fsyntax-only -verify -Wdangling-capture %s
2 
3 #include "Inputs/lifetime-analysis.h"
4 
5 // ****************************************************************************
6 // Capture an integer
7 // ****************************************************************************
8 namespace capture_int {
9 struct X {} x;
10 void captureInt(const int &i [[clang::lifetime_capture_by(x)]], X &x);
11 void captureRValInt(int &&i [[clang::lifetime_capture_by(x)]], X &x);
12 void noCaptureInt(int i [[clang::lifetime_capture_by(x)]], X &x);
13 
14 void use() {
15   int local;
16   captureInt(1, // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
17             x);
18   captureRValInt(1, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
19   captureInt(local, x);
20   noCaptureInt(1, x);
21   noCaptureInt(local, x);
22 }
23 } // namespace capture_int
24 
25 // ****************************************************************************
26 // Capture std::string (gsl owner types)
27 // ****************************************************************************
28 namespace capture_string {
29 struct X {} x;
30 void captureString(const std::string &s [[clang::lifetime_capture_by(x)]], X &x);
31 void captureRValString(std::string &&s [[clang::lifetime_capture_by(x)]], X &x);
32 
33 void use() {
34   std::string local_string;
35   captureString(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
36   captureString(local_string, x);
37   captureRValString(std::move(local_string), x);
38   captureRValString(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
39 }
40 } // namespace capture_string
41 
42 // ****************************************************************************
43 // Capture std::string_view (gsl pointer types)
44 // ****************************************************************************
45 namespace capture_string_view {
46 struct X {} x;
47 void captureStringView(std::string_view s [[clang::lifetime_capture_by(x)]], X &x);
48 void captureRValStringView(std::string_view &&sv [[clang::lifetime_capture_by(x)]], X &x);
49 void noCaptureStringView(std::string_view sv, X &x);
50 
51 std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
52 std::string_view getNotLifetimeBoundView(const std::string& s);
53 const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]);
54 const std::string& getLifetimeBoundString(std::string_view sv [[clang::lifetimebound]]);
55 
56 void use() {
57   std::string_view local_string_view;
58   std::string local_string;
59   captureStringView(local_string_view, x);
60   captureStringView(std::string(), // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
61             x);
62 
63   captureStringView(getLifetimeBoundView(local_string), x);
64   captureStringView(getNotLifetimeBoundView(std::string()), x);
65   captureRValStringView(std::move(local_string_view), x);
66   captureRValStringView(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
67   captureRValStringView(std::string_view{"abcd"}, x);
68 
69   noCaptureStringView(local_string_view, x);
70   noCaptureStringView(std::string(), x);
71 
72   // With lifetimebound functions.
73   captureStringView(getLifetimeBoundView(
74   std::string() // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
75   ), x);
76   captureRValStringView(getLifetimeBoundView(local_string), x);
77   captureRValStringView(getLifetimeBoundView(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
78   captureRValStringView(getNotLifetimeBoundView(std::string()), x);
79   noCaptureStringView(getLifetimeBoundView(std::string()), x);
80   captureStringView(getLifetimeBoundString(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
81   captureStringView(getLifetimeBoundString(getLifetimeBoundView(std::string())), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
82   captureStringView(getLifetimeBoundString(getLifetimeBoundString(
83     std::string()  // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
84     )), x);
85 }
86 } // namespace capture_string_view
87 
88 // ****************************************************************************
89 // Capture pointer (eg: std::string*)
90 // ****************************************************************************
91 const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]);
92 const std::string* getNotLifetimeBoundPointer(const std::string &s);
93 
94 namespace capture_pointer {
95 struct X {} x;
96 void capturePointer(const std::string* sp [[clang::lifetime_capture_by(x)]], X &x);
97 void use() {
98   capturePointer(getLifetimeBoundPointer(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
99   capturePointer(getLifetimeBoundPointer(*getLifetimeBoundPointer(
100     std::string()  // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
101     )), x);
102   capturePointer(getNotLifetimeBoundPointer(std::string()), x);
103 
104 }
105 } // namespace capture_pointer
106 
107 // ****************************************************************************
108 // Arrays and initializer lists.
109 // ****************************************************************************
110 namespace init_lists {
111 struct X {} x;
112 void captureVector(const std::vector<int> &a [[clang::lifetime_capture_by(x)]], X &x);
113 void captureArray(int array [[clang::lifetime_capture_by(x)]] [2], X &x);
114 void captureInitList(std::initializer_list<int> abc [[clang::lifetime_capture_by(x)]], X &x);
115 
116 
117 std::initializer_list<int> getLifetimeBoundInitList(std::initializer_list<int> abc [[clang::lifetimebound]]);
118 
119 void use() {
120   captureVector({1, 2, 3}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
121   captureVector(std::vector<int>{}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
122   std::vector<int> local_vector;
123   captureVector(local_vector, x);
124   int local_array[2];
125   captureArray(local_array, x);
126   captureInitList({1, 2}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
127   captureInitList(getLifetimeBoundInitList({1, 2}), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
128 }
129 } // namespace init_lists
130 
131 // ****************************************************************************
132 // Implicit object param 'this' is captured
133 // ****************************************************************************
134 namespace this_is_captured {
135 struct X {} x;
136 struct S {
137   void capture(X &x) [[clang::lifetime_capture_by(x)]];
138 };
139 void use() {
140   S{}.capture(x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
141   S s;
142   s.capture(x);
143 }
144 } // namespace this_is_captured
145 
146 namespace temporary_capturing_object {
147 struct S {
148   void add(const int& x [[clang::lifetime_capture_by(this)]]);
149 };
150 
151 void test() {
152   // We still give an warning even the capturing object is a temoprary.
153   // It is possible that the capturing object uses the captured object in its
154   // destructor.
155   S().add(1); // expected-warning {{object whose reference is captured}}
156   S{}.add(1); // expected-warning {{object whose reference is captured}}
157 }
158 } // namespace ignore_temporary_class_object
159 
160 // ****************************************************************************
161 // Capture by Global and Unknown.
162 // ****************************************************************************
163 namespace capture_by_global_unknown {
164 void captureByGlobal(std::string_view s [[clang::lifetime_capture_by(global)]]);
165 void captureByUnknown(std::string_view s [[clang::lifetime_capture_by(unknown)]]);
166 
167 std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
168 
169 void use() {
170   std::string_view local_string_view;
171   std::string local_string;
172   // capture by global.
173   captureByGlobal(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
174   captureByGlobal(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
175   captureByGlobal(local_string);
176   captureByGlobal(local_string_view);
177 
178   // capture by unknown.
179   captureByUnknown(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
180   captureByUnknown(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
181   captureByUnknown(local_string);
182   captureByUnknown(local_string_view);
183 }
184 } // namespace capture_by_global_unknown
185 
186 // ****************************************************************************
187 // Member functions: Capture by 'this'
188 // ****************************************************************************
189 namespace capture_by_this {
190 struct S {
191   void captureInt(const int& x [[clang::lifetime_capture_by(this)]]);
192   void captureView(std::string_view sv [[clang::lifetime_capture_by(this)]]);
193 };
194 std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
195 std::string_view getNotLifetimeBoundView(const std::string& s);
196 const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]);
197 
198 void use() {
199   S s;
200   s.captureInt(1); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}
201   s.captureView(std::string()); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}
202   s.captureView(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}
203   s.captureView(getLifetimeBoundString(std::string()));  // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}
204   s.captureView(getNotLifetimeBoundView(std::string()));
205 }
206 } // namespace capture_by_this
207 
208 // ****************************************************************************
209 // Struct with field as a reference
210 // ****************************************************************************
211 namespace reference_field {
212 struct X {} x;
213 struct Foo {
214   const int& b;
215 };
216 void captureField(Foo param [[clang::lifetime_capture_by(x)]], X &x);
217 void use() {
218   captureField(Foo{
219     1 // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
220   }, x);
221   int local;
222   captureField(Foo{local}, x);
223 }
224 } // namespace reference_field
225 
226 // ****************************************************************************
227 // Capture default argument.
228 // ****************************************************************************
229 namespace default_arg {
230 struct X {} x;
231 void captureDefaultArg(X &x, std::string_view s [[clang::lifetime_capture_by(x)]] = std::string());
232 
233 std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
234 
235 void useCaptureDefaultArg() {
236   X x;
237   captureDefaultArg(x); // FIXME: Diagnose temporary default arg.
238   captureDefaultArg(x, std::string("temp")); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
239   captureDefaultArg(x, getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
240   std::string local;
241   captureDefaultArg(x, local);
242 }
243 } // namespace default_arg
244 
245 // ****************************************************************************
246 // Container: *No* distinction between pointer-like and other element type
247 // ****************************************************************************
248 namespace containers_no_distinction {
249 template<class T>
250 struct MySet {
251   void insert(T&& t [[clang::lifetime_capture_by(this)]]);
252   void insert(const T& t [[clang::lifetime_capture_by(this)]]);
253 };
254 void user_defined_containers() {
255   MySet<int> set_of_int;
256   set_of_int.insert(1); // expected-warning {{object whose reference is captured by 'set_of_int' will be destroyed at the end of the full-expression}}
257   MySet<std::string_view> set_of_sv;
258   set_of_sv.insert(std::string());  // expected-warning {{object whose reference is captured by 'set_of_sv' will be destroyed at the end of the full-expression}}
259   set_of_sv.insert(std::string_view());
260 }
261 } // namespace containers_no_distinction
262 
263 // ****************************************************************************
264 // Container: Different for pointer-like and other element type.
265 // ****************************************************************************
266 namespace conatiners_with_different {
267 template<typename T> struct IsPointerLikeTypeImpl : std::false_type {};
268 template<> struct IsPointerLikeTypeImpl<std::string_view> : std::true_type {};
269 template<typename T> concept IsPointerLikeType = std::is_pointer<T>::value || IsPointerLikeTypeImpl<T>::value;
270 
271 template<class T> struct MyVector {
272   void push_back(T&& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>;
273   void push_back(const T& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>;
274 
275   void push_back(T&& t) requires (!IsPointerLikeType<T>);
276   void push_back(const T& t) requires (!IsPointerLikeType<T>);
277 };
278 
279 std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
280 
281 void use_container() {
282   std::string local;
283 
284   MyVector<std::string> vector_of_string;
285   vector_of_string.push_back(std::string()); // Ok.
286 
287   MyVector<std::string_view> vector_of_view;
288   vector_of_view.push_back(std::string()); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}}
289   vector_of_view.push_back(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}}
290 
291   MyVector<const std::string*> vector_of_pointer;
292   vector_of_pointer.push_back(getLifetimeBoundPointer(std::string())); // expected-warning {{object whose reference is captured by 'vector_of_pointer' will be destroyed at the end of the full-expression}}
293   vector_of_pointer.push_back(getLifetimeBoundPointer(*getLifetimeBoundPointer(std::string()))); // expected-warning {{object whose reference is captured by 'vector_of_pointer' will be destroyed at the end of the full-expression}}
294   vector_of_pointer.push_back(getLifetimeBoundPointer(local));
295   vector_of_pointer.push_back(getNotLifetimeBoundPointer(std::string()));
296 }
297 
298 // ****************************************************************************
299 // Container: For user defined view types
300 // ****************************************************************************
301 struct [[gsl::Pointer()]] MyStringView : public std::string_view {
302   MyStringView();
303   MyStringView(std::string_view&&);
304   MyStringView(const MyStringView&);
305   MyStringView(const std::string&);
306 };
307 template<> struct IsPointerLikeTypeImpl<MyStringView> : std::true_type {};
308 
309 std::optional<std::string_view> getOptionalSV();
310 std::optional<std::string> getOptionalS();
311 std::optional<MyStringView> getOptionalMySV();
312 MyStringView getMySV();
313 
314 class MyStringViewNotPointer : public std::string_view {};
315 std::optional<MyStringViewNotPointer> getOptionalMySVNotP();
316 MyStringViewNotPointer getMySVNotP();
317 
318 std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
319 std::string_view getNotLifetimeBoundView(const std::string& s);
320 const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]);
321 const std::string& getLifetimeBoundString(std::string_view sv [[clang::lifetimebound]]);
322 
323 void use_my_view() {
324   std::string local;
325   MyVector<MyStringView> vector_of_my_view;
326   vector_of_my_view.push_back(getMySV());
327   vector_of_my_view.push_back(MyStringView{});
328   vector_of_my_view.push_back(std::string_view{});
329   vector_of_my_view.push_back(std::string{}); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}}
330   vector_of_my_view.push_back(getLifetimeBoundView(std::string{})); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}}
331   vector_of_my_view.push_back(getLifetimeBoundString(getLifetimeBoundView(std::string{}))); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}}
332   vector_of_my_view.push_back(getNotLifetimeBoundView(getLifetimeBoundString(getLifetimeBoundView(std::string{}))));
333 
334   // Use with container of other view types.
335   MyVector<std::string_view> vector_of_view;
336   vector_of_view.push_back(getMySV());
337   vector_of_view.push_back(getMySVNotP());
338 }
339 
340 // ****************************************************************************
341 // Container: Use with std::optional<view> (owner<pointer> types)
342 // ****************************************************************************
343 void use_with_optional_view() {
344   MyVector<std::string_view> vector_of_view;
345 
346   std::optional<std::string_view> optional_of_view;
347   vector_of_view.push_back(optional_of_view.value());
348   vector_of_view.push_back(getOptionalS().value()); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}}
349 
350   vector_of_view.push_back(getOptionalSV().value());
351   vector_of_view.push_back(getOptionalMySV().value());
352   vector_of_view.push_back(getOptionalMySVNotP().value());
353 }
354 } // namespace conatiners_with_different
355 
356 // ****************************************************************************
357 // Capture 'temporary' views
358 // ****************************************************************************
359 namespace temporary_views {
360 void capture1(std::string_view s [[clang::lifetime_capture_by(x)]], std::vector<std::string_view>& x);
361 
362 // Intended to capture the "string_view" itself
363 void capture2(const std::string_view& s [[clang::lifetime_capture_by(x)]], std::vector<std::string_view*>& x);
364 // Intended to capture the pointee of the "string_view"
365 void capture3(const std::string_view& s [[clang::lifetime_capture_by(x)]], std::vector<std::string_view>& x);
366 
367 void use() {
368   std::vector<std::string_view> x1;
369   capture1(std::string(), x1); // expected-warning {{object whose reference is captured by 'x1' will be destroyed at the end of the full-expression}}
370   capture1(std::string_view(), x1);
371 
372   std::vector<std::string_view*> x2;
373   // Clang considers 'const std::string_view&' to refer to the owner
374   // 'std::string' and not 'std::string_view'. Therefore no diagnostic here.
375   capture2(std::string_view(), x2);
376   capture2(std::string(), x2); // expected-warning {{object whose reference is captured by 'x2' will be destroyed at the end of the full-expression}}
377 
378   std::vector<std::string_view> x3;
379   capture3(std::string_view(), x3);
380   capture3(std::string(), x3); // expected-warning {{object whose reference is captured by 'x3' will be destroyed at the end of the full-expression}}
381 }
382 } // namespace temporary_views
383 
384 // ****************************************************************************
385 // Inferring annotation for STL containers
386 // ****************************************************************************
387 namespace inferred_capture_by {
388 const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]);
389 const std::string* getNotLifetimeBoundPointer(const std::string &s);
390 
391 std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
392 std::string_view getNotLifetimeBoundView(const std::string& s);
393 void use() {
394   std::string local;
395   std::vector<std::string_view> views;
396   views.push_back(std::string()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}
397   views.insert(views.begin(),
398             std::string()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}
399   views.push_back(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}
400   views.push_back(getNotLifetimeBoundView(std::string()));
401   views.push_back(local);
402   views.insert(views.end(), local);
403 
404   std::vector<std::string> strings;
405   strings.push_back(std::string());
406   strings.insert(strings.begin(), std::string());
407 
408   std::vector<const std::string*> pointers;
409   pointers.push_back(getLifetimeBoundPointer(std::string()));
410   pointers.push_back(&local);
411 }
412 
413 namespace with_span {
414 // Templated view types.
415 template<typename T>
416 struct [[gsl::Pointer]] Span {
417   Span(const std::vector<T> &V);
418 };
419 
420 void use() {
421   std::vector<Span<int>> spans;
422   spans.push_back(std::vector<int>{1, 2, 3}); // expected-warning {{object whose reference is captured by 'spans' will be destroyed at the end of the full-expression}}
423   std::vector<int> local;
424   spans.push_back(local);
425 }
426 } // namespace with_span
427 } // namespace inferred_capture_by
428 
429 namespace on_constructor {
430 struct T {
431   T(const int& t [[clang::lifetime_capture_by(this)]]);
432 };
433 struct T2 {
434   T2(const int& t [[clang::lifetime_capture_by(x)]], int& x);
435 };
436 struct T3 {
437   T3(const T& t [[clang::lifetime_capture_by(this)]]);
438 };
439 
440 int foo(const T& t);
441 int bar(const T& t[[clang::lifetimebound]]);
442 
443 void test() {
444   auto x = foo(T(1)); // OK. no diagnosic
445   T(1); // OK. no diagnostic
446   T t(1); // expected-warning {{temporary whose address is used}}
447   auto y = bar(T(1)); // expected-warning {{temporary whose address is used}}
448   T3 t3(T(1)); // expected-warning {{temporary whose address is used}}
449 
450   int a;
451   T2(1, a); // expected-warning {{object whose reference is captured by}}
452 }
453 } // namespace on_constructor
454 
455 namespace GH121391 {
456 
457 struct Foo {};
458 
459 template <typename T>
460 struct Container {
461   const T& tt() [[clang::lifetimebound]];
462 };
463 template<typename T>
464 struct StatusOr {
465    T* get() [[clang::lifetimebound]];
466 };
467 StatusOr<Container<const Foo*>> getContainer();
468 
469 void test() {
470   std::vector<const Foo*> vv;
471   vv.push_back(getContainer().get()->tt()); // OK
472 }
473 
474 } // namespace GH121391
475