xref: /llvm-project/clang/test/Analysis/lambdas.cpp (revision 84a3aadf0f2483dde0acfc4e79f2a075a5f35bd1)
1 // RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,deadcode,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s
2 // RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core -analyzer-config inline-lambdas=false -DNO_INLINING=1 -verify %s
3 // RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,debug.DumpCFG -analyzer-config inline-lambdas=true %s > %t 2>&1
4 // RUN: FileCheck --input-file=%t %s
5 
6 #include "Inputs/system-header-simulator-cxx.h"
7 
8 void clang_analyzer_warnIfReached();
9 void clang_analyzer_eval(int);
10 
11 #ifdef NO_INLINING
12 
13 // expected-no-diagnostics
14 
invalidate_static_on_unknown_lambda()15 int& invalidate_static_on_unknown_lambda() {
16   static int* z;
17   auto f = [] {
18     z = nullptr;
19   }; // should invalidate "z" when inlining is disabled.
20   f();
21   return *z; // no-warning
22 }
23 
24 #else
25 
26 struct X { X(const X&); };
f(X x)27 void f(X x) { (void) [x]{}; }
28 
29 
30 // Lambda semantics tests.
31 
basicCapture()32 void basicCapture() {
33   int i = 5;
34   [i]() mutable {
35     // clang_analyzer_eval does nothing in inlined functions.
36     if (i != 5)
37       clang_analyzer_warnIfReached();
38     ++i;
39   }();
40   [&i] {
41     if (i != 5)
42       clang_analyzer_warnIfReached();
43   }();
44   [&i] {
45     if (i != 5)
46       clang_analyzer_warnIfReached();
47     i++;
48   }();
49   clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
50 }
51 
deferredLambdaCall()52 void deferredLambdaCall() {
53   int i = 5;
54   auto l1 = [i]() mutable {
55     if (i != 5)
56       clang_analyzer_warnIfReached();
57     ++i;
58   };
59   auto l2 = [&i] {
60     if (i != 5)
61       clang_analyzer_warnIfReached();
62   };
63   auto l3 = [&i] {
64     if (i != 5)
65       clang_analyzer_warnIfReached();
66     i++;
67   };
68   l1();
69   l2();
70   l3();
71   clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
72 }
73 
multipleCaptures()74 void multipleCaptures() {
75   int i = 5, j = 5;
76   [i, &j]() mutable {
77     if (i != 5 && j != 5)
78       clang_analyzer_warnIfReached();
79     ++i;
80     ++j;
81   }();
82   clang_analyzer_eval(i == 5); // expected-warning{{TRUE}}
83   clang_analyzer_eval(j == 6); // expected-warning{{TRUE}}
84   [=]() mutable {
85     if (i != 5 && j != 6)
86       clang_analyzer_warnIfReached();
87     ++i;
88     ++j;
89   }();
90   clang_analyzer_eval(i == 5); // expected-warning{{TRUE}}
91   clang_analyzer_eval(j == 6); // expected-warning{{TRUE}}
92   [&]() mutable {
93     if (i != 5 && j != 6)
94       clang_analyzer_warnIfReached();
95     ++i;
96     ++j;
97   }();
98   clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
99   clang_analyzer_eval(j == 7); // expected-warning{{TRUE}}
100 }
101 
testReturnValue()102 void testReturnValue() {
103   int i = 5;
104   auto l = [i] (int a) {
105     return i + a;
106   };
107   int b = l(3);
108   clang_analyzer_eval(b == 8); // expected-warning{{TRUE}}
109 }
110 
testAliasingBetweenParameterAndCapture()111 void testAliasingBetweenParameterAndCapture() {
112   int i = 5;
113 
114   auto l = [&i](int &p) {
115     i++;
116     p++;
117   };
118   l(i);
119   clang_analyzer_eval(i == 7); // expected-warning{{TRUE}}
120 }
121 
122 // Nested lambdas.
123 
testNestedLambdas()124 void testNestedLambdas() {
125   int i = 5;
126   auto l = [i]() mutable {
127     [&i]() {
128       ++i;
129     }();
130     if (i != 6)
131       clang_analyzer_warnIfReached();
132   };
133   l();
134   clang_analyzer_eval(i == 5); // expected-warning{{TRUE}}
135 }
136 
137 // Captured this.
138 
139 class RandomClass {
140   int i;
141 
captureFields()142   void captureFields() {
143     i = 5;
144     [this]() {
145       // clang_analyzer_eval does nothing in inlined functions.
146       if (i != 5)
147         clang_analyzer_warnIfReached();
148       ++i;
149     }();
150     clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
151   }
152 };
153 
154 
155 // Nested this capture.
156 
157 class RandomClass2 {
158   int i;
159 
captureFields()160   void captureFields() {
161     i = 5;
162     [this]() {
163       // clang_analyzer_eval does nothing in inlined functions.
164       if (i != 5)
165         clang_analyzer_warnIfReached();
166       ++i;
167       [this]() {
168         // clang_analyzer_eval does nothing in inlined functions.
169         if (i != 6)
170           clang_analyzer_warnIfReached();
171         ++i;
172       }();
173     }();
174     clang_analyzer_eval(i == 7); // expected-warning{{TRUE}}
175   }
176 };
177 
178 
179 // Captured function pointers.
180 
inc(int & x)181 void inc(int &x) {
182   ++x;
183 }
184 
testFunctionPointerCapture()185 void testFunctionPointerCapture() {
186   void (*func)(int &) = inc;
187   int i = 5;
188   [&i, func] {
189     func(i);
190   }();
191   clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
192 }
193 
194 // Captured variable-length array.
195 
testVariableLengthArrayCaptured()196 void testVariableLengthArrayCaptured() {
197   int n = 2;     // expected-note {{declared here}}
198   int array[n];  // expected-warning {{variable length arrays in C++ are a Clang extension}} \
199                     expected-note {{read of non-const variable 'n' is not allowed in a constant expression}}
200   array[0] = 7;
201 
202   int i = [&]{
203     return array[0];
204   }();
205 
206   clang_analyzer_eval(i == 7); // expected-warning{{TRUE}}
207 }
208 
209 // Test inline defensive checks
210 int getNum();
211 
inlineDefensiveChecks()212 void inlineDefensiveChecks() {
213   int i = getNum();
214   [=]() {
215     if (i == 0)
216       ;
217   }();
218   int p = 5/i;
219   (void)p;
220 }
221 
222 
223 template<typename T>
callLambda(T t)224 void callLambda(T t) {
225   t();
226 }
227 
228 struct DontCrash {
229   int x;
fDontCrash230   void f() {
231     callLambda([&](){ ++x; });
232     callLambdaFromStatic([&](){ ++x; });
233   }
234 
235   template<typename T>
callLambdaFromStaticDontCrash236   static void callLambdaFromStatic(T t) {
237     t();
238   }
239 };
240 
241 
242 // Capture constants
243 
captureConstants()244 void captureConstants() {
245   const int i = 5;
246   [=]() {
247     if (i != 5)
248       clang_analyzer_warnIfReached();
249   }();
250   [&] {
251     if (i != 5)
252       clang_analyzer_warnIfReached();
253   }();
254 }
255 
captureReferenceByCopy(int & p)256 void captureReferenceByCopy(int &p) {
257   int v = 7;
258   p = 8;
259 
260   // p is a reference captured by copy
261   [&v,p]() mutable {
262     v = p;
263     p = 22;
264   }();
265 
266   clang_analyzer_eval(v == 8); // expected-warning{{TRUE}}
267   clang_analyzer_eval(p == 8); // expected-warning{{TRUE}}
268 }
269 
captureReferenceByReference(int & p)270 void captureReferenceByReference(int &p) {
271   int v = 7;
272   p = 8;
273 
274   // p is a reference captured by reference
275   [&v,&p]() {
276     v = p;
277     p = 22;
278   }();
279 
280   clang_analyzer_eval(v == 8); // expected-warning{{TRUE}}
281   clang_analyzer_eval(p == 22); // expected-warning{{TRUE}}
282 }
283 
callMutableLambdaMultipleTimes(int & p)284 void callMutableLambdaMultipleTimes(int &p) {
285   int v = 0;
286   p = 8;
287 
288   auto l = [&v, p]() mutable {
289     v = p;
290     p++;
291   };
292 
293   l();
294 
295   clang_analyzer_eval(v == 8); // expected-warning{{TRUE}}
296   clang_analyzer_eval(p == 8); // expected-warning{{TRUE}}
297 
298   l();
299 
300   clang_analyzer_eval(v == 9); // expected-warning{{TRUE}}
301   clang_analyzer_eval(p == 8); // expected-warning{{TRUE}}
302 }
303 
304 // PR 24914
305 struct StructPR24914{
306   int x;
307 };
308 
309 void takesConstStructArgument(const StructPR24914&);
captureStructReference(const StructPR24914 & s)310 void captureStructReference(const StructPR24914& s) {
311   [s]() {
312     takesConstStructArgument(s);
313   }();
314 }
315 
316 // Lambda capture counts as use for dead-store checking.
317 
318 int returnsValue();
319 
captureByCopyCausesUse()320 void captureByCopyCausesUse() {
321   int local1 = returnsValue(); // no-warning
322   int local2 = returnsValue(); // no-warning
323   int local3 = returnsValue(); // expected-warning{{Value stored to 'local3' during its initialization is never read}}
324 
325   (void)[local1, local2]() { }; // Explicit capture by copy counts as use.
326 
327   int local4 = returnsValue(); // no-warning
328   int local5 = returnsValue(); // expected-warning{{Value stored to 'local5' during its initialization is never read}}
329 
330   (void)[=]() {
331     (void)local4; // Implicit capture by copy counts as use
332   };
333 }
334 
captureByReference()335 void captureByReference() {
336   int local1 = returnsValue(); // no-warning
337 
338   auto lambda1 = [&local1]() { // Explicit capture by reference
339     local1++;
340   };
341 
342   // Don't treat as a dead store because local1 was captured by reference.
343   local1 = 7; // no-warning
344 
345   lambda1();
346 
347   int local2 = returnsValue(); // no-warning
348 
349   auto lambda2 = [&]() {
350     local2++; // Implicit capture by reference
351   };
352 
353   // Don't treat as a dead store because local2 was captured by reference.
354   local2 = 7; // no-warning
355 
356   lambda2();
357 }
358 
testCapturedConstExprFloat()359 void testCapturedConstExprFloat() {
360   constexpr float localConstant = 4.0;
361   auto lambda = []{
362     // Don't treat localConstant as containing a garbage value
363     float copy = localConstant; // no-warning
364     (void)copy;
365   };
366 
367   lambda();
368 }
369 
370 void escape(void*);
371 
invalidate_static_on_unknown_lambda()372 int& invalidate_static_on_unknown_lambda() {
373   static int* z;
374   auto lambda = [] {
375     static float zz;
376     z = new int(120);
377   };
378   escape(&lambda);
379   return *z; // no-warning
380 }
381 
382 
383 static int b = 0;
384 
f()385 int f() {
386   b = 0;
387   auto &bm = b;
388   [&] {
389     bm++;
390     bm++;
391   }();
392   if (bm != 2) {
393     int *y = 0;
394     return *y; // no-warning
395   }
396   return 0;
397 }
398 
399 #endif
400 
401 // CHECK: [B2 (ENTRY)]
402 // CHECK:   Succs (1): B1
403 // CHECK: [B1]
404 // CHECK:   1: x
405 // CHECK:   2: [B1.1] (ImplicitCastExpr, NoOp, const X)
406 // CHECK:   3: [B1.2] (CXXConstructExpr[B1.4]+0, X)
407 // CHECK:   4: [x]     {
408 // CHECK:    }
409 // CHECK:   5: (void)[B1.4] (CStyleCastExpr, ToVoid, void)
410 // CHECK:   Preds (1): B2
411 // CHECK:   Succs (1): B0
412 // CHECK: [B0 (EXIT)]
413 // CHECK:   Preds (1): B1
414