1 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify %s
2 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true -DTEMPORARIES -verify %s
3 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++17 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true -DTEMPORARIES %s
4 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -DMOVES -verify %s
5 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true -DTEMPORARIES -DMOVES -verify %s
6 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++17 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true -DTEMPORARIES -DMOVES %s
7 
8 // Note: The C++17 run-lines don't -verify yet - it is a no-crash test.
9 
10 void clang_analyzer_eval(bool);
11 void clang_analyzer_checkInlined(bool);
12 
13 namespace pr17001_call_wrong_destructor {
14 bool x;
15 struct A {
16   int *a;
17   A() {}
18   ~A() {}
19 };
20 struct B : public A {
21   B() {}
22   ~B() { x = true; }
23 };
24 
25 void f() {
26   {
27     const A &a = B();
28   }
29   clang_analyzer_eval(x); // expected-warning{{TRUE}}
30 }
31 } // end namespace pr17001_call_wrong_destructor
32 
33 namespace pr19539_crash_on_destroying_an_integer {
34 struct A {
35   int i;
36   int j[2];
37   A() : i(1) {
38     j[0] = 2;
39     j[1] = 3;
40   }
41   ~A() {}
42 };
43 
44 void f() {
45   const int &x = A().i; // no-crash
46   const int &y = A().j[1]; // no-crash
47   const int &z = (A().j[1], A().j[0]); // no-crash
48 
49   // FIXME: All of these should be TRUE, but constructors aren't inlined.
50   clang_analyzer_eval(x == 1); // expected-warning{{UNKNOWN}}
51   clang_analyzer_eval(y == 3); // expected-warning{{UNKNOWN}}
52   clang_analyzer_eval(z == 2); // expected-warning{{UNKNOWN}}
53 }
54 } // end namespace pr19539_crash_on_destroying_an_integer
55 
56 namespace maintain_original_object_address_on_lifetime_extension {
57 class C {
58   C **after, **before;
59 
60 public:
61   bool x;
62 
63   C(bool x, C **after, C **before) : x(x), after(after), before(before) {
64     *before = this;
65   }
66 
67   // Don't track copies in our tests.
68   C(const C &c) : x(c.x), after(nullptr), before(nullptr) {}
69 
70   ~C() { if (after) *after = this; }
71 
72   operator bool() const { return x; }
73 
74   static C make(C **after, C **before) { return C(false, after, before); }
75 };
76 
77 void f1() {
78   C *after, *before;
79   {
80     const C &c = C(true, &after, &before);
81   }
82   clang_analyzer_eval(after == before);
83 #ifdef TEMPORARIES
84   // expected-warning@-2{{TRUE}}
85 #else
86   // expected-warning@-4{{UNKNOWN}}
87 #endif
88 }
89 
90 void f2() {
91   C *after, *before;
92   C c = C(1, &after, &before);
93   clang_analyzer_eval(after == before);
94 #ifdef TEMPORARIES
95   // expected-warning@-2{{TRUE}}
96 #else
97   // expected-warning@-4{{UNKNOWN}}
98 #endif
99 }
100 
101 void f3(bool coin) {
102   C *after, *before;
103   {
104     const C &c = coin ? C(true, &after, &before) : C(false, &after, &before);
105   }
106   clang_analyzer_eval(after == before);
107 #ifdef TEMPORARIES
108   // expected-warning@-2{{TRUE}}
109 #else
110   // expected-warning@-4{{UNKNOWN}}
111 #endif
112 }
113 
114 void f4(bool coin) {
115   C *after, *before;
116   {
117     // no-crash
118     const C &c = C(coin, &after, &before) ?: C(false, &after, &before);
119   }
120   // FIXME: Add support for lifetime extension through binary conditional
121   // operator. Ideally also add support for the binary conditional operator in
122   // C++. Because for now it calls the constructor for the condition twice.
123   if (coin) {
124     clang_analyzer_eval(after == before);
125 #ifdef TEMPORARIES
126   // expected-warning@-2{{The left operand of '==' is a garbage value}}
127 #else
128   // expected-warning@-4{{UNKNOWN}}
129 #endif
130   } else {
131     clang_analyzer_eval(after == before);
132 #ifdef TEMPORARIES
133     // Seems to work at the moment, but also seems accidental.
134     // Feel free to break.
135   // expected-warning@-4{{TRUE}}
136 #else
137   // expected-warning@-6{{UNKNOWN}}
138 #endif
139   }
140 }
141 
142 void f5() {
143   C *after, *before;
144   {
145     const bool &x = C(true, &after, &before).x; // no-crash
146   }
147   // FIXME: Should be TRUE. Should not warn about garbage value.
148   clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
149 }
150 
151 struct A { // A is an aggregate.
152   const C &c;
153 };
154 
155 void f6() {
156   C *after, *before;
157   {
158     A a{C(true, &after, &before)};
159   }
160   // FIXME: Should be TRUE. Should not warn about garbage value.
161   clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
162 }
163 
164 void f7() {
165   C *after, *before;
166   {
167     A a = {C(true, &after, &before)};
168   }
169   // FIXME: Should be TRUE. Should not warn about garbage value.
170   clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
171 }
172 
173 void f8() {
174   C *after, *before;
175   {
176     A a[2] = {C(false, nullptr, nullptr), C(true, &after, &before)};
177   }
178   // FIXME: Should be TRUE. Should not warn about garbage value.
179   clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
180 }
181 } // end namespace maintain_original_object_address_on_lifetime_extension
182 
183 namespace maintain_original_object_address_on_move {
184 class C {
185   int *x;
186 
187 public:
188   C() : x(nullptr) {}
189   C(int *x) : x(x) {}
190   C(const C &c) = delete;
191   C(C &&c) : x(c.x) { c.x = nullptr; }
192   C &operator=(C &&c) {
193     x = c.x;
194     c.x = nullptr;
195     return *this;
196   }
197   ~C() {
198     // This was triggering the division by zero warning in f1() and f2():
199     // Because move-elision materialization was incorrectly causing the object
200     // to be relocated from one address to another before move, but destructor
201     // was operating on the old address, it was still thinking that 'x' is set.
202     if (x)
203       *x = 0;
204   }
205 };
206 
207 void f1() {
208   int x = 1;
209   // &x is replaced with nullptr in move-constructor before the temporary dies.
210   C c = C(&x);
211   // Hence x was not set to 0 yet.
212   1 / x; // no-warning
213 }
214 void f2() {
215   int x = 1;
216   C c;
217   // &x is replaced with nullptr in move-assignment before the temporary dies.
218   c = C(&x);
219   // Hence x was not set to 0 yet.
220   1 / x; // no-warning
221 }
222 } // end namespace maintain_original_object_address_on_move
223 
224 namespace maintain_address_of_copies {
225 class C;
226 
227 struct AddressVector {
228   C *buf[10];
229   int len;
230 
231   AddressVector() : len(0) {}
232 
233   void push(C *c) {
234     buf[len] = c;
235     ++len;
236   }
237 };
238 
239 class C {
240   AddressVector &v;
241 
242 public:
243   C(AddressVector &v) : v(v) { v.push(this); }
244   ~C() { v.push(this); }
245 
246 #ifdef MOVES
247   C(C &&c) : v(c.v) { v.push(this); }
248 #endif
249 
250   // Note how return-statements prefer move-constructors when available.
251   C(const C &c) : v(c.v) {
252 #ifdef MOVES
253     clang_analyzer_checkInlined(false); // no-warning
254 #else
255     v.push(this);
256 #endif
257   } // no-warning
258 
259   static C make(AddressVector &v) { return C(v); }
260 };
261 
262 void f1() {
263   AddressVector v;
264   {
265     C c = C(v);
266   }
267   // 0. Create the original temporary and lifetime-extend it into variable 'c'
268   //    construction argument.
269   // 1. Construct variable 'c' (elidable copy/move).
270   // 2. Destroy the temporary.
271   // 3. Destroy variable 'c'.
272   clang_analyzer_eval(v.len == 4);
273   clang_analyzer_eval(v.buf[0] == v.buf[2]);
274   clang_analyzer_eval(v.buf[1] == v.buf[3]);
275 #ifdef TEMPORARIES
276   // expected-warning@-4{{TRUE}}
277   // expected-warning@-4{{TRUE}}
278   // expected-warning@-4{{TRUE}}
279 #else
280   // expected-warning@-8{{UNKNOWN}}
281   // expected-warning@-8{{UNKNOWN}}
282   // expected-warning@-8{{UNKNOWN}}
283 #endif
284 }
285 
286 void f2() {
287   AddressVector v;
288   {
289     const C &c = C::make(v);
290   }
291   // 0. Construct the original temporary within make(),
292   // 1. Construct the return value of make() (elidable copy/move) and
293   //    lifetime-extend it via reference 'c',
294   // 2. Destroy the temporary within make(),
295   // 3. Destroy the temporary lifetime-extended by 'c'.
296   clang_analyzer_eval(v.len == 4);
297   clang_analyzer_eval(v.buf[0] == v.buf[2]);
298   clang_analyzer_eval(v.buf[1] == v.buf[3]);
299 #ifdef TEMPORARIES
300   // expected-warning@-4{{TRUE}}
301   // expected-warning@-4{{TRUE}}
302   // expected-warning@-4{{TRUE}}
303 #else
304   // expected-warning@-8{{UNKNOWN}}
305   // expected-warning@-8{{UNKNOWN}}
306   // expected-warning@-8{{UNKNOWN}}
307 #endif
308 }
309 
310 void f3() {
311   AddressVector v;
312   {
313     C &&c = C::make(v);
314   }
315   // 0. Construct the original temporary within make(),
316   // 1. Construct the return value of make() (elidable copy/move) and
317   //    lifetime-extend it via reference 'c',
318   // 2. Destroy the temporary within make(),
319   // 3. Destroy the temporary lifetime-extended by 'c'.
320   clang_analyzer_eval(v.len == 4);
321   clang_analyzer_eval(v.buf[0] == v.buf[2]);
322   clang_analyzer_eval(v.buf[1] == v.buf[3]);
323 #ifdef TEMPORARIES
324   // expected-warning@-4{{TRUE}}
325   // expected-warning@-4{{TRUE}}
326   // expected-warning@-4{{TRUE}}
327 #else
328   // expected-warning@-8{{UNKNOWN}}
329   // expected-warning@-8{{UNKNOWN}}
330   // expected-warning@-8{{UNKNOWN}}
331 #endif
332 }
333 
334 C doubleMake(AddressVector &v) {
335   return C::make(v);
336 }
337 
338 void f4() {
339   AddressVector v;
340   {
341     C c = doubleMake(v);
342   }
343   // 0. Construct the original temporary within make(),
344   // 1. Construct the return value of make() (elidable copy/move) and
345   //    lifetime-extend it into the return value constructor argument within
346   //    doubleMake(),
347   // 2. Destroy the temporary within make(),
348   // 3. Construct the return value of doubleMake() (elidable copy/move) and
349   //    lifetime-extend it into the variable 'c' constructor argument,
350   // 4. Destroy the return value of make(),
351   // 5. Construct variable 'c' (elidable copy/move),
352   // 6. Destroy the return value of doubleMake(),
353   // 7. Destroy variable 'c'.
354   clang_analyzer_eval(v.len == 8);
355   clang_analyzer_eval(v.buf[0] == v.buf[2]);
356   clang_analyzer_eval(v.buf[1] == v.buf[4]);
357   clang_analyzer_eval(v.buf[3] == v.buf[6]);
358   clang_analyzer_eval(v.buf[5] == v.buf[7]);
359 #ifdef TEMPORARIES
360   // expected-warning@-6{{TRUE}}
361   // expected-warning@-6{{TRUE}}
362   // expected-warning@-6{{TRUE}}
363   // expected-warning@-6{{TRUE}}
364   // expected-warning@-6{{TRUE}}
365 #else
366   // expected-warning@-12{{UNKNOWN}}
367   // expected-warning@-12{{UNKNOWN}}
368   // expected-warning@-12{{UNKNOWN}}
369   // expected-warning@-12{{UNKNOWN}}
370   // expected-warning@-12{{UNKNOWN}}
371 #endif
372 }
373 } // end namespace maintain_address_of_copies
374