Mercurial > hg > CbC > CbC_llvm
comparison clang/test/Analysis/dtor.cpp @ 150:1d019706d866
LLVM10
author | anatofuz |
---|---|
date | Thu, 13 Feb 2020 15:10:13 +0900 |
parents | |
children | c4bab56944e8 |
comparison
equal
deleted
inserted
replaced
147:c2174574ed3a | 150:1d019706d866 |
---|---|
1 // RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,debug.ExprInspection,cplusplus -analyzer-config c++-inlining=destructors -Wno-null-dereference -Wno-inaccessible-base -verify -analyzer-config eagerly-assume=false %s | |
2 | |
3 void clang_analyzer_eval(bool); | |
4 void clang_analyzer_checkInlined(bool); | |
5 | |
6 class A { | |
7 public: | |
8 ~A() { | |
9 int *x = 0; | |
10 *x = 3; // expected-warning{{Dereference of null pointer}} | |
11 } | |
12 }; | |
13 | |
14 int main() { | |
15 A a; | |
16 } | |
17 | |
18 | |
19 typedef __typeof(sizeof(int)) size_t; | |
20 void *malloc(size_t); | |
21 void free(void *); | |
22 | |
23 class SmartPointer { | |
24 void *X; | |
25 public: | |
26 SmartPointer(void *x) : X(x) {} | |
27 ~SmartPointer() { | |
28 free(X); | |
29 } | |
30 }; | |
31 | |
32 void testSmartPointer() { | |
33 char *mem = (char*)malloc(4); | |
34 { | |
35 SmartPointer Deleter(mem); | |
36 // destructor called here | |
37 } | |
38 *mem = 0; // expected-warning{{Use of memory after it is freed}} | |
39 } | |
40 | |
41 | |
42 void doSomething(); | |
43 void testSmartPointer2() { | |
44 char *mem = (char*)malloc(4); | |
45 { | |
46 SmartPointer Deleter(mem); | |
47 // Remove dead bindings... | |
48 doSomething(); | |
49 // destructor called here | |
50 } | |
51 *mem = 0; // expected-warning{{Use of memory after it is freed}} | |
52 } | |
53 | |
54 | |
55 class Subclass : public SmartPointer { | |
56 public: | |
57 Subclass(void *x) : SmartPointer(x) {} | |
58 }; | |
59 | |
60 void testSubclassSmartPointer() { | |
61 char *mem = (char*)malloc(4); | |
62 { | |
63 Subclass Deleter(mem); | |
64 // Remove dead bindings... | |
65 doSomething(); | |
66 // destructor called here | |
67 } | |
68 *mem = 0; // expected-warning{{Use of memory after it is freed}} | |
69 } | |
70 | |
71 | |
72 class MultipleInheritance : public Subclass, public SmartPointer { | |
73 public: | |
74 MultipleInheritance(void *a, void *b) : Subclass(a), SmartPointer(b) {} | |
75 }; | |
76 | |
77 void testMultipleInheritance1() { | |
78 char *mem = (char*)malloc(4); | |
79 { | |
80 MultipleInheritance Deleter(mem, 0); | |
81 // Remove dead bindings... | |
82 doSomething(); | |
83 // destructor called here | |
84 } | |
85 *mem = 0; // expected-warning{{Use of memory after it is freed}} | |
86 } | |
87 | |
88 void testMultipleInheritance2() { | |
89 char *mem = (char*)malloc(4); | |
90 { | |
91 MultipleInheritance Deleter(0, mem); | |
92 // Remove dead bindings... | |
93 doSomething(); | |
94 // destructor called here | |
95 } | |
96 *mem = 0; // expected-warning{{Use of memory after it is freed}} | |
97 } | |
98 | |
99 void testMultipleInheritance3() { | |
100 char *mem = (char*)malloc(4); | |
101 { | |
102 MultipleInheritance Deleter(mem, mem); | |
103 // Remove dead bindings... | |
104 doSomething(); | |
105 // destructor called here | |
106 // expected-warning@28 {{Attempt to free released memory}} | |
107 } | |
108 } | |
109 | |
110 | |
111 class SmartPointerMember { | |
112 SmartPointer P; | |
113 public: | |
114 SmartPointerMember(void *x) : P(x) {} | |
115 }; | |
116 | |
117 void testSmartPointerMember() { | |
118 char *mem = (char*)malloc(4); | |
119 { | |
120 SmartPointerMember Deleter(mem); | |
121 // Remove dead bindings... | |
122 doSomething(); | |
123 // destructor called here | |
124 } | |
125 *mem = 0; // expected-warning{{Use of memory after it is freed}} | |
126 } | |
127 | |
128 | |
129 struct IntWrapper { | |
130 IntWrapper() : x(0) {} | |
131 ~IntWrapper(); | |
132 int *x; | |
133 }; | |
134 | |
135 void testArrayInvalidation() { | |
136 int i = 42; | |
137 int j = 42; | |
138 | |
139 { | |
140 IntWrapper arr[2]; | |
141 | |
142 // There should be no undefined value warnings here. | |
143 // Eventually these should be TRUE as well, but right now | |
144 // we can't handle array constructors. | |
145 clang_analyzer_eval(arr[0].x == 0); // expected-warning{{UNKNOWN}} | |
146 clang_analyzer_eval(arr[1].x == 0); // expected-warning{{UNKNOWN}} | |
147 | |
148 arr[0].x = &i; | |
149 arr[1].x = &j; | |
150 clang_analyzer_eval(*arr[0].x == 42); // expected-warning{{TRUE}} | |
151 clang_analyzer_eval(*arr[1].x == 42); // expected-warning{{TRUE}} | |
152 } | |
153 | |
154 // The destructors should have invalidated i and j. | |
155 clang_analyzer_eval(i == 42); // expected-warning{{UNKNOWN}} | |
156 clang_analyzer_eval(j == 42); // expected-warning{{UNKNOWN}} | |
157 } | |
158 | |
159 | |
160 | |
161 // Don't crash on a default argument inside an initializer. | |
162 struct DefaultArg { | |
163 DefaultArg(int x = 0) {} | |
164 ~DefaultArg(); | |
165 }; | |
166 | |
167 struct InheritsDefaultArg : DefaultArg { | |
168 InheritsDefaultArg() {} | |
169 virtual ~InheritsDefaultArg(); | |
170 }; | |
171 | |
172 void testDefaultArg() { | |
173 InheritsDefaultArg a; | |
174 // Force a bug to be emitted. | |
175 *(char *)0 = 1; // expected-warning{{Dereference of null pointer}} | |
176 } | |
177 | |
178 | |
179 namespace DestructorVirtualCalls { | |
180 class A { | |
181 public: | |
182 int *out1, *out2, *out3; | |
183 | |
184 virtual int get() { return 1; } | |
185 | |
186 ~A() { | |
187 *out1 = get(); | |
188 } | |
189 }; | |
190 | |
191 class B : public A { | |
192 public: | |
193 virtual int get() { return 2; } | |
194 | |
195 ~B() { | |
196 *out2 = get(); | |
197 } | |
198 }; | |
199 | |
200 class C : public B { | |
201 public: | |
202 virtual int get() { return 3; } | |
203 | |
204 ~C() { | |
205 *out3 = get(); | |
206 } | |
207 }; | |
208 | |
209 void test() { | |
210 int a, b, c; | |
211 | |
212 // New scope for the C object. | |
213 { | |
214 C obj; | |
215 clang_analyzer_eval(obj.get() == 3); // expected-warning{{TRUE}} | |
216 | |
217 // Sanity check for devirtualization. | |
218 A *base = &obj; | |
219 clang_analyzer_eval(base->get() == 3); // expected-warning{{TRUE}} | |
220 | |
221 obj.out1 = &a; | |
222 obj.out2 = &b; | |
223 obj.out3 = &c; | |
224 } | |
225 | |
226 clang_analyzer_eval(a == 1); // expected-warning{{TRUE}} | |
227 clang_analyzer_eval(b == 2); // expected-warning{{TRUE}} | |
228 clang_analyzer_eval(c == 3); // expected-warning{{TRUE}} | |
229 } | |
230 } | |
231 | |
232 | |
233 namespace DestructorsShouldNotAffectReturnValues { | |
234 class Dtor { | |
235 public: | |
236 ~Dtor() { | |
237 clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} | |
238 } | |
239 }; | |
240 | |
241 void *allocate() { | |
242 Dtor d; | |
243 return malloc(4); // no-warning | |
244 } | |
245 | |
246 void test() { | |
247 // At one point we had an issue where the statements inside an | |
248 // inlined destructor kept us from finding the return statement, | |
249 // leading the analyzer to believe that the malloc'd memory had leaked. | |
250 void *p = allocate(); | |
251 free(p); // no-warning | |
252 } | |
253 } | |
254 | |
255 namespace MultipleInheritanceVirtualDtors { | |
256 class VirtualDtor { | |
257 protected: | |
258 virtual ~VirtualDtor() { | |
259 clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} | |
260 } | |
261 }; | |
262 | |
263 class NonVirtualDtor { | |
264 protected: | |
265 ~NonVirtualDtor() { | |
266 clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} | |
267 } | |
268 }; | |
269 | |
270 class SubclassA : public VirtualDtor, public NonVirtualDtor { | |
271 public: | |
272 virtual ~SubclassA() {} | |
273 }; | |
274 class SubclassB : public NonVirtualDtor, public VirtualDtor { | |
275 public: | |
276 virtual ~SubclassB() {} | |
277 }; | |
278 | |
279 void test() { | |
280 SubclassA a; | |
281 SubclassB b; | |
282 } | |
283 } | |
284 | |
285 namespace ExplicitDestructorCall { | |
286 class VirtualDtor { | |
287 public: | |
288 virtual ~VirtualDtor() { | |
289 clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} | |
290 } | |
291 }; | |
292 | |
293 class Subclass : public VirtualDtor { | |
294 public: | |
295 virtual ~Subclass() { | |
296 clang_analyzer_checkInlined(false); // no-warning | |
297 } | |
298 }; | |
299 | |
300 void destroy(Subclass *obj) { | |
301 obj->VirtualDtor::~VirtualDtor(); | |
302 } | |
303 } | |
304 | |
305 | |
306 namespace MultidimensionalArrays { | |
307 void testArrayInvalidation() { | |
308 int i = 42; | |
309 int j = 42; | |
310 | |
311 { | |
312 IntWrapper arr[2][2]; | |
313 | |
314 // There should be no undefined value warnings here. | |
315 // Eventually these should be TRUE as well, but right now | |
316 // we can't handle array constructors. | |
317 clang_analyzer_eval(arr[0][0].x == 0); // expected-warning{{UNKNOWN}} | |
318 clang_analyzer_eval(arr[1][1].x == 0); // expected-warning{{UNKNOWN}} | |
319 | |
320 arr[0][0].x = &i; | |
321 arr[1][1].x = &j; | |
322 clang_analyzer_eval(*arr[0][0].x == 42); // expected-warning{{TRUE}} | |
323 clang_analyzer_eval(*arr[1][1].x == 42); // expected-warning{{TRUE}} | |
324 } | |
325 | |
326 // The destructors should have invalidated i and j. | |
327 clang_analyzer_eval(i == 42); // expected-warning{{UNKNOWN}} | |
328 clang_analyzer_eval(j == 42); // expected-warning{{UNKNOWN}} | |
329 } | |
330 } | |
331 | |
332 namespace LifetimeExtension { | |
333 struct IntWrapper { | |
334 int x; | |
335 IntWrapper(int y) : x(y) {} | |
336 IntWrapper() { | |
337 extern void use(int); | |
338 use(x); // no-warning | |
339 } | |
340 }; | |
341 | |
342 struct DerivedWrapper : public IntWrapper { | |
343 DerivedWrapper(int y) : IntWrapper(y) {} | |
344 }; | |
345 | |
346 DerivedWrapper get() { | |
347 return DerivedWrapper(1); | |
348 } | |
349 | |
350 void test() { | |
351 const DerivedWrapper &d = get(); // lifetime extended here | |
352 } | |
353 | |
354 | |
355 class SaveOnDestruct { | |
356 public: | |
357 static int lastOutput; | |
358 int value; | |
359 | |
360 SaveOnDestruct(); | |
361 ~SaveOnDestruct() { | |
362 lastOutput = value; | |
363 } | |
364 }; | |
365 | |
366 void testSimple() { | |
367 { | |
368 const SaveOnDestruct &obj = SaveOnDestruct(); | |
369 if (obj.value != 42) | |
370 return; | |
371 // destructor called here | |
372 } | |
373 | |
374 clang_analyzer_eval(SaveOnDestruct::lastOutput == 42); // expected-warning{{TRUE}} | |
375 } | |
376 | |
377 struct NRCheck { | |
378 bool bool_; | |
379 NRCheck():bool_(true) {} | |
380 ~NRCheck() __attribute__((noreturn)); | |
381 operator bool() const { return bool_; } | |
382 }; | |
383 | |
384 struct CheckAutoDestructor { | |
385 bool bool_; | |
386 CheckAutoDestructor():bool_(true) {} | |
387 operator bool() const { return bool_; } | |
388 }; | |
389 | |
390 struct CheckCustomDestructor { | |
391 bool bool_; | |
392 CheckCustomDestructor():bool_(true) {} | |
393 ~CheckCustomDestructor(); | |
394 operator bool() const { return bool_; } | |
395 }; | |
396 | |
397 bool testUnnamedNR() { | |
398 if (NRCheck()) | |
399 return true; | |
400 return false; | |
401 } | |
402 | |
403 bool testNamedNR() { | |
404 if (NRCheck c = NRCheck()) | |
405 return true; | |
406 return false; | |
407 } | |
408 | |
409 bool testUnnamedAutoDestructor() { | |
410 if (CheckAutoDestructor()) | |
411 return true; | |
412 return false; | |
413 } | |
414 | |
415 bool testNamedAutoDestructor() { | |
416 if (CheckAutoDestructor c = CheckAutoDestructor()) | |
417 return true; | |
418 return false; | |
419 } | |
420 | |
421 bool testUnnamedCustomDestructor() { | |
422 if (CheckCustomDestructor()) | |
423 return true; | |
424 return false; | |
425 } | |
426 | |
427 // This case used to cause an unexpected "Undefined or garbage value returned | |
428 // to caller" warning | |
429 bool testNamedCustomDestructor() { | |
430 if (CheckCustomDestructor c = CheckCustomDestructor()) | |
431 return true; | |
432 return false; | |
433 } | |
434 | |
435 bool testMultipleTemporariesCustomDestructor() { | |
436 if (CheckCustomDestructor c = (CheckCustomDestructor(), CheckCustomDestructor())) | |
437 return true; | |
438 return false; | |
439 } | |
440 | |
441 class VirtualDtorBase { | |
442 public: | |
443 int value; | |
444 virtual ~VirtualDtorBase() {} | |
445 }; | |
446 | |
447 class SaveOnVirtualDestruct : public VirtualDtorBase { | |
448 public: | |
449 static int lastOutput; | |
450 | |
451 SaveOnVirtualDestruct(); | |
452 virtual ~SaveOnVirtualDestruct() { | |
453 lastOutput = value; | |
454 } | |
455 }; | |
456 | |
457 void testVirtual() { | |
458 { | |
459 const VirtualDtorBase &obj = SaveOnVirtualDestruct(); | |
460 if (obj.value != 42) | |
461 return; | |
462 // destructor called here | |
463 } | |
464 | |
465 clang_analyzer_eval(SaveOnVirtualDestruct::lastOutput == 42); // expected-warning{{TRUE}} | |
466 } | |
467 } | |
468 | |
469 namespace NoReturn { | |
470 struct NR { | |
471 ~NR() __attribute__((noreturn)); | |
472 }; | |
473 | |
474 void f(int **x) { | |
475 NR nr; | |
476 } | |
477 | |
478 void g() { | |
479 int *x; | |
480 f(&x); | |
481 *x = 47; // no warning | |
482 } | |
483 | |
484 void g2(int *x) { | |
485 if (! x) NR(); | |
486 *x = 47; // no warning | |
487 } | |
488 } | |
489 | |
490 namespace PseudoDtor { | |
491 template <typename T> | |
492 void destroy(T &obj) { | |
493 clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} | |
494 obj.~T(); | |
495 } | |
496 | |
497 void test() { | |
498 int i; | |
499 destroy(i); | |
500 clang_analyzer_eval(true); // expected-warning{{TRUE}} | |
501 } | |
502 } | |
503 | |
504 namespace Incomplete { | |
505 class Foo; // expected-note{{forward declaration}} | |
506 void f(Foo *foo) { delete foo; } // expected-warning{{deleting pointer to incomplete type}} | |
507 } | |
508 | |
509 namespace TypeTraitExpr { | |
510 template <bool IsSimple, typename T> | |
511 struct copier { | |
512 static void do_copy(T *dest, const T *src, unsigned count); | |
513 }; | |
514 template <typename T, typename U> | |
515 void do_copy(T *dest, const U *src, unsigned count) { | |
516 const bool IsSimple = __is_trivial(T) && __is_same(T, U); | |
517 copier<IsSimple, T>::do_copy(dest, src, count); | |
518 } | |
519 struct NonTrivial { | |
520 int *p; | |
521 NonTrivial() : p(new int[1]) { p[0] = 0; } | |
522 NonTrivial(const NonTrivial &other) { | |
523 p = new int[1]; | |
524 do_copy(p, other.p, 1); | |
525 } | |
526 NonTrivial &operator=(const NonTrivial &other) { | |
527 p = other.p; | |
528 return *this; | |
529 } | |
530 ~NonTrivial() { | |
531 delete[] p; // expected-warning {{free released memory}} | |
532 } | |
533 }; | |
534 | |
535 void f() { | |
536 NonTrivial nt1; | |
537 NonTrivial nt2(nt1); | |
538 nt1 = nt2; | |
539 clang_analyzer_eval(__is_trivial(NonTrivial)); // expected-warning{{FALSE}} | |
540 clang_analyzer_eval(__alignof(NonTrivial) > 0); // expected-warning{{TRUE}} | |
541 } | |
542 } | |
543 | |
544 namespace dtor_over_loc_concrete_int { | |
545 struct A { | |
546 ~A() {} | |
547 }; | |
548 | |
549 struct B { | |
550 A a; | |
551 ~B() {} | |
552 }; | |
553 | |
554 struct C : A { | |
555 ~C() {} | |
556 }; | |
557 | |
558 void testB() { | |
559 B *b = (B *)-1; | |
560 b->~B(); // no-crash | |
561 } | |
562 | |
563 void testC() { | |
564 C *c = (C *)-1; | |
565 c->~C(); // no-crash | |
566 } | |
567 | |
568 void testAutoDtor() { | |
569 const A &a = *(A *)-1; | |
570 // no-crash | |
571 } | |
572 } // namespace dtor_over_loc_concrete_int |