1 // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 \
2 // RUN:    -analyzer-config eagerly-assume=false -verify %s
3 // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 \
4 // RUN:    -analyzer-config eagerly-assume=false -verify %s
5 // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 \
6 // RUN:    -analyzer-config elide-constructors=false -DNO_ELIDE_FLAG              \
7 // RUN:    -analyzer-config eagerly-assume=false -verify=expected,no-elide %s
8 // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 \
9 // RUN:    -analyzer-config elide-constructors=false                              \
10 // RUN:    -analyzer-config eagerly-assume=false -verify %s
11 
12 // Copy elision always occurs in C++17, otherwise it's under
13 // an on-by-default flag.
14 #if __cplusplus >= 201703L
15   #define ELIDE 1
16 #else
17   #ifndef NO_ELIDE_FLAG
18     #define ELIDE 1
19   #endif
20 #endif
21 
22 void clang_analyzer_eval(bool);
23 
24 namespace variable_functional_cast_crash {
25 
26 struct A {
Avariable_functional_cast_crash::A27   A(int) {}
28 };
29 
foo()30 void foo() {
31   A a = A(0);
32 }
33 
34 struct B {
35   A a;
Bvariable_functional_cast_crash::B36   B(): a(A(0)) {}
37 };
38 
39 } // namespace variable_functional_cast_crash
40 
41 
42 namespace ctor_initializer {
43 
44 struct S {
45   int x, y, z;
46 };
47 
48 struct T {
49   S s;
50   int w;
Tctor_initializer::T51   T(int w): s(), w(w) {}
52 };
53 
54 class C {
55   T t;
56 public:
C()57   C() : t(T(4)) {
58     S s = {1, 2, 3};
59     t.s = s;
60     // FIXME: Should be TRUE regardless of copy elision.
61     clang_analyzer_eval(t.w == 4);
62 #ifdef ELIDE
63     // expected-warning@-2{{TRUE}}
64 #else
65     // expected-warning@-4{{UNKNOWN}}
66 #endif
67   }
68 };
69 
70 
71 struct A {
72   int x;
Actor_initializer::A73   A(): x(0) {}
~Actor_initializer::A74   ~A() {}
75 };
76 
77 struct B {
78   A a;
Bctor_initializer::B79   B() : a(A()) {}
80 };
81 
foo()82 void foo() {
83   B b;
84   clang_analyzer_eval(b.a.x == 0); // expected-warning{{TRUE}}
85 }
86 
87 } // namespace ctor_initializer
88 
89 
90 namespace elision_on_ternary_op_branches {
91 class C1 {
92   int x;
93 public:
C1(int x)94   C1(int x): x(x) {}
getX() const95   int getX() const { return x; }
96   ~C1();
97 };
98 
99 class C2 {
100   int x;
101   int y;
102 public:
C2(int x,int y)103   C2(int x, int y): x(x), y(y) {}
getX() const104   int getX() const { return x; }
getY() const105   int getY() const { return y; }
106   ~C2();
107 };
108 
foo(int coin)109 void foo(int coin) {
110   C1 c1 = coin ? C1(1) : C1(2);
111   if (coin) {
112     clang_analyzer_eval(c1.getX() == 1); // expected-warning{{TRUE}}
113   } else {
114     clang_analyzer_eval(c1.getX() == 2); // expected-warning{{TRUE}}
115   }
116   C2 c2 = coin ? C2(3, 4) : C2(5, 6);
117   if (coin) {
118     clang_analyzer_eval(c2.getX() == 3); // expected-warning{{TRUE}}
119     clang_analyzer_eval(c2.getY() == 4); // expected-warning{{TRUE}}
120   } else {
121     clang_analyzer_eval(c2.getX() == 5); // expected-warning{{TRUE}}
122     clang_analyzer_eval(c2.getY() == 6); // expected-warning{{TRUE}}
123   }
124 }
125 } // namespace elision_on_ternary_op_branches
126 
127 
128 namespace address_vector_tests {
129 
130 template <typename T> struct AddressVector {
131   T *buf[20];
132   int len;
133 
AddressVectoraddress_vector_tests::AddressVector134   AddressVector() : len(0) {}
135 
pushaddress_vector_tests::AddressVector136   void push(T *t) {
137     buf[len] = t;
138     ++len;
139   }
140 };
141 
142 class ClassWithoutDestructor {
143   AddressVector<ClassWithoutDestructor> &v;
144 
145 public:
ClassWithoutDestructor(AddressVector<ClassWithoutDestructor> & v)146   ClassWithoutDestructor(AddressVector<ClassWithoutDestructor> &v) : v(v) {
147     push();
148   }
149 
ClassWithoutDestructor(ClassWithoutDestructor && c)150   ClassWithoutDestructor(ClassWithoutDestructor &&c) : v(c.v) { push(); }
ClassWithoutDestructor(const ClassWithoutDestructor & c)151   ClassWithoutDestructor(const ClassWithoutDestructor &c) : v(c.v) { push(); }
152 
push()153   void push() { v.push(this); }
154 };
155 
make1(AddressVector<ClassWithoutDestructor> & v)156 ClassWithoutDestructor make1(AddressVector<ClassWithoutDestructor> &v) {
157   return ClassWithoutDestructor(v);
158   // no-elide-warning@-1 {{Address of stack memory associated with temporary \
159 object of type 'address_vector_tests::ClassWithoutDestructor' is still \
160 referred to by the stack variable 'v' upon returning to the caller}}
161 }
make2(AddressVector<ClassWithoutDestructor> & v)162 ClassWithoutDestructor make2(AddressVector<ClassWithoutDestructor> &v) {
163   return make1(v);
164   // no-elide-warning@-1 {{Address of stack memory associated with temporary \
165 object of type 'address_vector_tests::ClassWithoutDestructor' is still \
166 referred to by the stack variable 'v' upon returning to the caller}}
167 }
make3(AddressVector<ClassWithoutDestructor> & v)168 ClassWithoutDestructor make3(AddressVector<ClassWithoutDestructor> &v) {
169   return make2(v);
170   // no-elide-warning@-1 {{Address of stack memory associated with temporary \
171 object of type 'address_vector_tests::ClassWithoutDestructor' is still \
172 referred to by the stack variable 'v' upon returning to the caller}}
173 }
174 
testMultipleReturns()175 void testMultipleReturns() {
176   AddressVector<ClassWithoutDestructor> v;
177   ClassWithoutDestructor c = make3(v);
178 
179 #if ELIDE
180   clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
181   clang_analyzer_eval(v.buf[0] == &c); // expected-warning{{TRUE}}
182 #else
183   clang_analyzer_eval(v.len == 5); // expected-warning{{TRUE}}
184   clang_analyzer_eval(v.buf[0] != v.buf[1]); // expected-warning{{TRUE}}
185   clang_analyzer_eval(v.buf[1] != v.buf[2]); // expected-warning{{TRUE}}
186   clang_analyzer_eval(v.buf[2] != v.buf[3]); // expected-warning{{TRUE}}
187   clang_analyzer_eval(v.buf[3] != v.buf[4]); // expected-warning{{TRUE}}
188   clang_analyzer_eval(v.buf[4] == &c); // expected-warning{{TRUE}}
189 #endif
190 }
191 
consume(ClassWithoutDestructor c)192 void consume(ClassWithoutDestructor c) {
193   c.push();
194   // expected-warning@-1 {{Address of stack memory associated with local \
195 variable 'c' is still referred to by the stack variable 'v' upon returning \
196 to the caller}}
197 }
198 
testArgumentConstructorWithoutDestructor()199 void testArgumentConstructorWithoutDestructor() {
200   AddressVector<ClassWithoutDestructor> v;
201 
202   consume(make3(v));
203 
204 #if ELIDE
205   clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
206   clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
207 #else
208   clang_analyzer_eval(v.len == 6); // expected-warning{{TRUE}}
209   clang_analyzer_eval(v.buf[0] != v.buf[1]); // expected-warning{{TRUE}}
210   clang_analyzer_eval(v.buf[1] != v.buf[2]); // expected-warning{{TRUE}}
211   clang_analyzer_eval(v.buf[2] != v.buf[3]); // expected-warning{{TRUE}}
212   clang_analyzer_eval(v.buf[3] != v.buf[4]); // expected-warning{{TRUE}}
213   // We forced a push() in consume(), let's see if the address here matches
214   // the address during construction.
215   clang_analyzer_eval(v.buf[4] == v.buf[5]); // expected-warning{{TRUE}}
216 #endif
217 }
218 
219 class ClassWithDestructor {
220   AddressVector<ClassWithDestructor> &v;
221 
222 public:
ClassWithDestructor(AddressVector<ClassWithDestructor> & v)223   ClassWithDestructor(AddressVector<ClassWithDestructor> &v) : v(v) {
224     push();
225   }
226 
ClassWithDestructor(ClassWithDestructor && c)227   ClassWithDestructor(ClassWithDestructor &&c) : v(c.v) { push(); }
ClassWithDestructor(const ClassWithDestructor & c)228   ClassWithDestructor(const ClassWithDestructor &c) : v(c.v) { push(); }
229 
~ClassWithDestructor()230   ~ClassWithDestructor() { push(); }
231 
push()232   void push() { v.push(this); }
233 };
234 
testVariable()235 void testVariable() {
236   AddressVector<ClassWithDestructor> v;
237   {
238     ClassWithDestructor c = ClassWithDestructor(v);
239     // Check if the last destructor is an automatic destructor.
240     // A temporary destructor would have fired by now.
241 #if ELIDE
242     clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
243 #else
244     clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}
245 #endif
246   }
247 #if ELIDE
248   // 0. Construct the variable.
249   // 1. Destroy the variable.
250   clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
251   clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
252 #else
253   // 0. Construct the temporary.
254   // 1. Construct the variable.
255   // 2. Destroy the temporary.
256   // 3. Destroy the variable.
257   clang_analyzer_eval(v.len == 4); // expected-warning{{TRUE}}
258   clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}
259   clang_analyzer_eval(v.buf[1] == v.buf[3]); // expected-warning{{TRUE}}
260 #endif
261 }
262 
263 struct TestCtorInitializer {
264   ClassWithDestructor c;
TestCtorInitializeraddress_vector_tests::TestCtorInitializer265   TestCtorInitializer(AddressVector<ClassWithDestructor> &v)
266     : c(ClassWithDestructor(v)) {}
267   // no-elide-warning@-1 {{Address of stack memory associated with temporary \
268 object of type 'address_vector_tests::ClassWithDestructor' is still referred \
269 to by the stack variable 'v' upon returning to the caller}}
270 };
271 
testCtorInitializer()272 void testCtorInitializer() {
273   AddressVector<ClassWithDestructor> v;
274   {
275     TestCtorInitializer t(v);
276     // Check if the last destructor is an automatic destructor.
277     // A temporary destructor would have fired by now.
278 #if ELIDE
279     clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
280 #else
281     clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}
282 #endif
283   }
284 #if ELIDE
285   // 0. Construct the member variable.
286   // 1. Destroy the member variable.
287   clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
288   clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
289 #else
290   // 0. Construct the temporary.
291   // 1. Construct the member variable.
292   // 2. Destroy the temporary.
293   // 3. Destroy the member variable.
294   clang_analyzer_eval(v.len == 4); // expected-warning{{TRUE}}
295   clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}
296   clang_analyzer_eval(v.buf[1] == v.buf[3]); // expected-warning{{TRUE}}
297 #endif
298 }
299 
300 
make1(AddressVector<ClassWithDestructor> & v)301 ClassWithDestructor make1(AddressVector<ClassWithDestructor> &v) {
302   return ClassWithDestructor(v);
303   // no-elide-warning@-1 {{Address of stack memory associated with temporary \
304 object of type 'address_vector_tests::ClassWithDestructor' is still referred \
305 to by the stack variable 'v' upon returning to the caller}}
306 }
make2(AddressVector<ClassWithDestructor> & v)307 ClassWithDestructor make2(AddressVector<ClassWithDestructor> &v) {
308   return make1(v);
309   // no-elide-warning@-1 {{Address of stack memory associated with temporary \
310 object of type 'address_vector_tests::ClassWithDestructor' is still referred \
311 to by the stack variable 'v' upon returning to the caller}}
312 }
make3(AddressVector<ClassWithDestructor> & v)313 ClassWithDestructor make3(AddressVector<ClassWithDestructor> &v) {
314   return make2(v);
315   // no-elide-warning@-1 {{Address of stack memory associated with temporary \
316 object of type 'address_vector_tests::ClassWithDestructor' is still referred \
317 to by the stack variable 'v' upon returning to the caller}}
318 }
319 
testMultipleReturnsWithDestructors()320 void testMultipleReturnsWithDestructors() {
321   AddressVector<ClassWithDestructor> v;
322   {
323     ClassWithDestructor c = make3(v);
324     // Check if the last destructor is an automatic destructor.
325     // A temporary destructor would have fired by now.
326 #if ELIDE
327     clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
328 #else
329     clang_analyzer_eval(v.len == 9); // expected-warning{{TRUE}}
330 #endif
331   }
332 
333 #if ELIDE
334   // 0. Construct the variable. Yes, constructor in make1() constructs
335   //    the variable 'c'.
336   // 1. Destroy the variable.
337   clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
338   clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
339 #else
340   // 0. Construct the temporary in make1().
341   // 1. Construct the temporary in make2().
342   // 2. Destroy the temporary in make1().
343   // 3. Construct the temporary in make3().
344   // 4. Destroy the temporary in make2().
345   // 5. Construct the temporary here.
346   // 6. Destroy the temporary in make3().
347   // 7. Construct the variable.
348   // 8. Destroy the temporary here.
349   // 9. Destroy the variable.
350   clang_analyzer_eval(v.len == 10); // expected-warning{{TRUE}}
351   clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}
352   clang_analyzer_eval(v.buf[1] == v.buf[4]); // expected-warning{{TRUE}}
353   clang_analyzer_eval(v.buf[3] == v.buf[6]); // expected-warning{{TRUE}}
354   clang_analyzer_eval(v.buf[5] == v.buf[8]); // expected-warning{{TRUE}}
355   clang_analyzer_eval(v.buf[7] == v.buf[9]); // expected-warning{{TRUE}}
356 #endif
357 }
358 
consume(ClassWithDestructor c)359 void consume(ClassWithDestructor c) {
360   c.push();
361   // expected-warning@-1 {{Address of stack memory associated with local \
362 variable 'c' is still referred to by the stack variable 'v' upon returning \
363 to the caller}}
364 }
365 
testArgumentConstructorWithDestructor()366 void testArgumentConstructorWithDestructor() {
367   AddressVector<ClassWithDestructor> v;
368 
369   consume(make3(v));
370 
371 #if ELIDE
372   // 0. Construct the argument.
373   // 1. Forced push() in consume().
374   // 2. Destroy the argument.
375   clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}
376   clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
377   clang_analyzer_eval(v.buf[1] == v.buf[2]); // expected-warning{{TRUE}}
378 #else
379   // 0. Construct the temporary in make1().
380   // 1. Construct the temporary in make2().
381   // 2. Destroy the temporary in make1().
382   // 3. Construct the temporary in make3().
383   // 4. Destroy the temporary in make2().
384   // 5. Construct the temporary here.
385   // 6. Destroy the temporary in make3().
386   // 7. Construct the argument.
387   // 8. Forced push() in consume().
388   // 9. Destroy the argument. Notice the reverse order!
389   // 10. Destroy the temporary here.
390   clang_analyzer_eval(v.len == 11); // expected-warning{{TRUE}}
391   clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}
392   clang_analyzer_eval(v.buf[1] == v.buf[4]); // expected-warning{{TRUE}}
393   clang_analyzer_eval(v.buf[3] == v.buf[6]); // expected-warning{{TRUE}}
394   clang_analyzer_eval(v.buf[5] == v.buf[10]); // expected-warning{{TRUE}}
395   clang_analyzer_eval(v.buf[7] == v.buf[8]); // expected-warning{{TRUE}}
396   clang_analyzer_eval(v.buf[8] == v.buf[9]); // expected-warning{{TRUE}}
397 #endif
398 }
399 
400 struct Foo {
Fooaddress_vector_tests::Foo401   Foo(Foo **q) {
402     *q = this;
403   }
404 };
405 
make1(Foo ** r)406 Foo make1(Foo **r) {
407   return Foo(r);
408   // no-elide-warning@-1 {{Address of stack memory associated with temporary \
409 object of type 'address_vector_tests::Foo' is still referred to by the stack \
410 variable 'z' upon returning to the caller}}
411 }
412 
test_copy_elision()413 void test_copy_elision() {
414   Foo *z;
415   // If the copy elided, 'z' points to 'tmp', otherwise it's a dangling pointer.
416   Foo tmp = make1(&z);
417   (void)tmp;
418 }
419 
420 } // namespace address_vector_tests
421