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