1 | // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -analyzer-config eagerly-assume=false %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 -analyzer-config eagerly-assume=false %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 -analyzer-config eagerly-assume=false %s |
4 | // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -DMOVES -verify -analyzer-config eagerly-assume=false %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 -analyzer-config eagerly-assume=false %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 -analyzer-config eagerly-assume=false %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 | clang_analyzer_eval(x == 1); |
50 | clang_analyzer_eval(y == 3); |
51 | clang_analyzer_eval(z == 2); |
52 | #ifdef TEMPORARIES |
53 | // expected-warning@-4{{TRUE}} |
54 | // expected-warning@-4{{TRUE}} |
55 | // expected-warning@-4{{TRUE}} |
56 | #else |
57 | // expected-warning@-8{{UNKNOWN}} |
58 | // expected-warning@-8{{UNKNOWN}} |
59 | // expected-warning@-8{{UNKNOWN}} |
60 | #endif |
61 | } |
62 | } // end namespace pr19539_crash_on_destroying_an_integer |
63 | |
64 | namespace maintain_original_object_address_on_lifetime_extension { |
65 | class C { |
66 | C **after, **before; |
67 | |
68 | public: |
69 | bool x; |
70 | |
71 | C(bool x, C **after, C **before) : x(x), after(after), before(before) { |
72 | *before = this; |
73 | } |
74 | |
75 | // Don't track copies in our tests. |
76 | C(const C &c) : x(c.x), after(nullptr), before(nullptr) {} |
77 | |
78 | ~C() { if (after) *after = this; } |
79 | |
80 | operator bool() const { return x; } |
81 | |
82 | static C make(C **after, C **before) { return C(false, after, before); } |
83 | }; |
84 | |
85 | void f1() { |
86 | C *after, *before; |
87 | { |
88 | const C &c = C(true, &after, &before); |
89 | } |
90 | clang_analyzer_eval(after == before); |
91 | #ifdef TEMPORARIES |
92 | // expected-warning@-2{{TRUE}} |
93 | #else |
94 | // expected-warning@-4{{UNKNOWN}} |
95 | #endif |
96 | } |
97 | |
98 | void f2() { |
99 | C *after, *before; |
100 | { |
101 | C c = C(1, &after, &before); |
102 | } |
103 | clang_analyzer_eval(after == before); // expected-warning{{TRUE}} |
104 | } |
105 | |
106 | void f3(bool coin) { |
107 | C *after, *before; |
108 | { |
109 | const C &c = coin ? C(true, &after, &before) : C(false, &after, &before); |
110 | } |
111 | clang_analyzer_eval(after == before); |
112 | #ifdef TEMPORARIES |
113 | // expected-warning@-2{{TRUE}} |
114 | #else |
115 | // expected-warning@-4{{UNKNOWN}} |
116 | #endif |
117 | } |
118 | |
119 | void f4(bool coin) { |
120 | C *after, *before; |
121 | { |
122 | // no-crash |
123 | const C &c = C(coin, &after, &before) ?: C(false, &after, &before); |
124 | } |
125 | // FIXME: Add support for lifetime extension through binary conditional |
126 | // operator. Ideally also add support for the binary conditional operator in |
127 | // C++. Because for now it calls the constructor for the condition twice. |
128 | if (coin) { |
129 | // FIXME: Should not warn. |
130 | clang_analyzer_eval(after == before); |
131 | #ifdef TEMPORARIES |
132 | // expected-warning@-2{{The left operand of '==' is a garbage value}} |
133 | #else |
134 | // expected-warning@-4{{UNKNOWN}} |
135 | #endif |
136 | } else { |
137 | // FIXME: Should be TRUE. |
138 | clang_analyzer_eval(after == before); |
139 | #ifdef TEMPORARIES |
140 | // expected-warning@-2{{FALSE}} |
141 | #else |
142 | // expected-warning@-4{{UNKNOWN}} |
143 | #endif |
144 | } |
145 | } |
146 | |
147 | void f5() { |
148 | C *after, *before; |
149 | { |
150 | const bool &x = C(true, &after, &before).x; // no-crash |
151 | } |
152 | clang_analyzer_eval(after == before); |
153 | #ifdef TEMPORARIES |
154 | // expected-warning@-2{{TRUE}} |
155 | #else |
156 | // expected-warning@-4{{UNKNOWN}} |
157 | #endif |
158 | } |
159 | |
160 | struct A { // A is an aggregate. |
161 | const C &c; |
162 | }; |
163 | |
164 | void f6() { |
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 f7() { |
174 | C *after, *before; |
175 | { |
176 | A a = {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 | |
182 | void f8() { |
183 | C *after, *before; |
184 | { |
185 | A a[2] = {C(false, nullptr, nullptr), C(true, &after, &before)}; |
186 | } |
187 | // FIXME: Should be TRUE. Should not warn about garbage value. |
188 | clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}} |
189 | } |
190 | } // end namespace maintain_original_object_address_on_lifetime_extension |
191 | |
192 | namespace maintain_original_object_address_on_move { |
193 | class C { |
194 | int *x; |
195 | |
196 | public: |
197 | C() : x(nullptr) {} |
198 | C(int *x) : x(x) {} |
199 | C(const C &c) = delete; |
200 | C(C &&c) : x(c.x) { c.x = nullptr; } |
201 | C &operator=(C &&c) { |
202 | x = c.x; |
203 | c.x = nullptr; |
204 | return *this; |
205 | } |
206 | ~C() { |
207 | // This was triggering the division by zero warning in f1() and f2(): |
208 | // Because move-elision materialization was incorrectly causing the object |
209 | // to be relocated from one address to another before move, but destructor |
210 | // was operating on the old address, it was still thinking that 'x' is set. |
211 | if (x) |
212 | *x = 0; |
213 | } |
214 | }; |
215 | |
216 | void f1() { |
217 | int x = 1; |
218 | // &x is replaced with nullptr in move-constructor before the temporary dies. |
219 | C c = C(&x); |
220 | // Hence x was not set to 0 yet. |
221 | 1 / x; // no-warning |
222 | } |
223 | void f2() { |
224 | int x = 1; |
225 | C c; |
226 | // &x is replaced with nullptr in move-assignment before the temporary dies. |
227 | c = C(&x); |
228 | // Hence x was not set to 0 yet. |
229 | 1 / x; // no-warning |
230 | } |
231 | } // end namespace maintain_original_object_address_on_move |
232 | |
233 | namespace maintain_address_of_copies { |
234 | class C; |
235 | |
236 | struct AddressVector { |
237 | C *buf[10]; |
238 | int len; |
239 | |
240 | AddressVector() : len(0) {} |
241 | |
242 | void push(C *c) { |
243 | buf[len] = c; |
244 | ++len; |
245 | } |
246 | }; |
247 | |
248 | class C { |
249 | AddressVector &v; |
250 | |
251 | public: |
252 | C(AddressVector &v) : v(v) { v.push(this); } |
253 | ~C() { v.push(this); } |
254 | |
255 | #ifdef MOVES |
256 | C(C &&c) : v(c.v) { v.push(this); } |
257 | #endif |
258 | |
259 | // Note how return-statements prefer move-constructors when available. |
260 | C(const C &c) : v(c.v) { |
261 | #ifdef MOVES |
262 | clang_analyzer_checkInlined(false); // no-warning |
263 | #else |
264 | v.push(this); |
265 | #endif |
266 | } // no-warning |
267 | |
268 | static C make(AddressVector &v) { return C(v); } |
269 | }; |
270 | |
271 | void f1() { |
272 | AddressVector v; |
273 | { |
274 | C c = C(v); |
275 | } |
276 | // 0. Construct variable 'c' (copy/move elided). |
277 | // 1. Destroy variable 'c'. |
278 | clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}} |
279 | clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}} |
280 | } |
281 | |
282 | void f2() { |
283 | AddressVector v; |
284 | { |
285 | const C &c = C::make(v); |
286 | } |
287 | // 0. Construct the return value of make() (copy/move elided) and |
288 | // lifetime-extend it directly via reference 'c', |
289 | // 1. Destroy the temporary lifetime-extended by 'c'. |
290 | clang_analyzer_eval(v.len == 2); |
291 | clang_analyzer_eval(v.buf[0] == v.buf[1]); |
292 | #ifdef TEMPORARIES |
293 | // expected-warning@-3{{TRUE}} |
294 | // expected-warning@-3{{TRUE}} |
295 | #else |
296 | // expected-warning@-6{{UNKNOWN}} |
297 | // expected-warning@-6{{UNKNOWN}} |
298 | #endif |
299 | } |
300 | |
301 | void f3() { |
302 | AddressVector v; |
303 | { |
304 | C &&c = C::make(v); |
305 | } |
306 | // 0. Construct the return value of make() (copy/move elided) and |
307 | // lifetime-extend it directly via reference 'c', |
308 | // 1. Destroy the temporary lifetime-extended by 'c'. |
309 | clang_analyzer_eval(v.len == 2); |
310 | clang_analyzer_eval(v.buf[0] == v.buf[1]); |
311 | #ifdef TEMPORARIES |
312 | // expected-warning@-3{{TRUE}} |
313 | // expected-warning@-3{{TRUE}} |
314 | #else |
315 | // expected-warning@-6{{UNKNOWN}} |
316 | // expected-warning@-6{{UNKNOWN}} |
317 | #endif |
318 | } |
319 | |
320 | C doubleMake(AddressVector &v) { |
321 | return C::make(v); |
322 | } |
323 | |
324 | void f4() { |
325 | AddressVector v; |
326 | { |
327 | C c = doubleMake(v); |
328 | } |
329 | // 0. Construct variable 'c' (all copies/moves elided), |
330 | // 1. Destroy variable 'c'. |
331 | clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}} |
332 | clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}} |
333 | } |
334 | } // end namespace maintain_address_of_copies |
335 | |