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