dtor.cpp revision 6bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89
1// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config c++-inlining=destructors,cfg-temporary-dtors=true -Wno-null-dereference -verify %s
2
3void clang_analyzer_eval(bool);
4void clang_analyzer_checkInlined(bool);
5
6class A {
7public:
8  ~A() {
9    int *x = 0;
10    *x = 3; // expected-warning{{Dereference of null pointer}}
11  }
12};
13
14int main() {
15  A a;
16}
17
18
19typedef __typeof(sizeof(int)) size_t;
20void *malloc(size_t);
21void free(void *);
22
23class SmartPointer {
24  void *X;
25public:
26  SmartPointer(void *x) : X(x) {}
27  ~SmartPointer() {
28    free(X);
29  }
30};
31
32void 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
42void doSomething();
43void 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
55class Subclass : public SmartPointer {
56public:
57  Subclass(void *x) : SmartPointer(x) {}
58};
59
60void 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
72class MultipleInheritance : public Subclass, public SmartPointer {
73public:
74  MultipleInheritance(void *a, void *b) : Subclass(a), SmartPointer(b) {}
75};
76
77void 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
88void 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
99void 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
111class SmartPointerMember {
112  SmartPointer P;
113public:
114  SmartPointerMember(void *x) : P(x) {}
115};
116
117void 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
129struct IntWrapper {
130  IntWrapper() : x(0) {}
131  ~IntWrapper();
132  int *x;
133};
134
135void 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.
162struct DefaultArg {
163  DefaultArg(int x = 0) {}
164  ~DefaultArg();
165};
166
167struct InheritsDefaultArg : DefaultArg {
168  InheritsDefaultArg() {}
169  virtual ~InheritsDefaultArg();
170};
171
172void testDefaultArg() {
173  InheritsDefaultArg a;
174  // Force a bug to be emitted.
175  *(char *)0 = 1; // expected-warning{{Dereference of null pointer}}
176}
177
178
179namespace 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
233namespace 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
255namespace 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
285namespace 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
306namespace 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
332namespace 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
469namespace 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
490namespace 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
504namespace Incomplete {
505  class Foo; // expected-note{{forward declaration}}
506  void f(Foo *foo) { delete foo; } // expected-warning{{deleting pointer to incomplete type}}
507}
508