| // RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,debug.ExprInspection,cplusplus -analyzer-config c++-inlining=destructors,cfg-temporary-dtors=true -Wno-null-dereference -Wno-inaccessible-base -verify %s | 
 |  | 
 | void clang_analyzer_eval(bool); | 
 | void clang_analyzer_checkInlined(bool); | 
 |  | 
 | class A { | 
 | public: | 
 |   ~A() {  | 
 |     int *x = 0; | 
 |     *x = 3; // expected-warning{{Dereference of null pointer}} | 
 |   } | 
 | }; | 
 |  | 
 | int main() { | 
 |   A a; | 
 | } | 
 |  | 
 |  | 
 | typedef __typeof(sizeof(int)) size_t; | 
 | void *malloc(size_t); | 
 | void free(void *); | 
 |  | 
 | class SmartPointer { | 
 |   void *X; | 
 | public: | 
 |   SmartPointer(void *x) : X(x) {} | 
 |   ~SmartPointer() { | 
 |     free(X); | 
 |   } | 
 | }; | 
 |  | 
 | void testSmartPointer() { | 
 |   char *mem = (char*)malloc(4); | 
 |   { | 
 |     SmartPointer Deleter(mem); | 
 |     // destructor called here | 
 |   } | 
 |   *mem = 0; // expected-warning{{Use of memory after it is freed}} | 
 | } | 
 |  | 
 |  | 
 | void doSomething(); | 
 | void testSmartPointer2() { | 
 |   char *mem = (char*)malloc(4); | 
 |   { | 
 |     SmartPointer Deleter(mem); | 
 |     // Remove dead bindings... | 
 |     doSomething(); | 
 |     // destructor called here | 
 |   } | 
 |   *mem = 0; // expected-warning{{Use of memory after it is freed}} | 
 | } | 
 |  | 
 |  | 
 | class Subclass : public SmartPointer { | 
 | public: | 
 |   Subclass(void *x) : SmartPointer(x) {} | 
 | }; | 
 |  | 
 | void testSubclassSmartPointer() { | 
 |   char *mem = (char*)malloc(4); | 
 |   { | 
 |     Subclass Deleter(mem); | 
 |     // Remove dead bindings... | 
 |     doSomething(); | 
 |     // destructor called here | 
 |   } | 
 |   *mem = 0; // expected-warning{{Use of memory after it is freed}} | 
 | } | 
 |  | 
 |  | 
 | class MultipleInheritance : public Subclass, public SmartPointer { | 
 | public: | 
 |   MultipleInheritance(void *a, void *b) : Subclass(a), SmartPointer(b) {} | 
 | }; | 
 |  | 
 | void testMultipleInheritance1() { | 
 |   char *mem = (char*)malloc(4); | 
 |   { | 
 |     MultipleInheritance Deleter(mem, 0); | 
 |     // Remove dead bindings... | 
 |     doSomething(); | 
 |     // destructor called here | 
 |   } | 
 |   *mem = 0; // expected-warning{{Use of memory after it is freed}} | 
 | } | 
 |  | 
 | void testMultipleInheritance2() { | 
 |   char *mem = (char*)malloc(4); | 
 |   { | 
 |     MultipleInheritance Deleter(0, mem); | 
 |     // Remove dead bindings... | 
 |     doSomething(); | 
 |     // destructor called here | 
 |   } | 
 |   *mem = 0; // expected-warning{{Use of memory after it is freed}} | 
 | } | 
 |  | 
 | void testMultipleInheritance3() { | 
 |   char *mem = (char*)malloc(4); | 
 |   { | 
 |     MultipleInheritance Deleter(mem, mem); | 
 |     // Remove dead bindings... | 
 |     doSomething(); | 
 |     // destructor called here | 
 |     // expected-warning@28 {{Attempt to free released memory}} | 
 |   } | 
 | } | 
 |  | 
 |  | 
 | class SmartPointerMember { | 
 |   SmartPointer P; | 
 | public: | 
 |   SmartPointerMember(void *x) : P(x) {} | 
 | }; | 
 |  | 
 | void testSmartPointerMember() { | 
 |   char *mem = (char*)malloc(4); | 
 |   { | 
 |     SmartPointerMember Deleter(mem); | 
 |     // Remove dead bindings... | 
 |     doSomething(); | 
 |     // destructor called here | 
 |   } | 
 |   *mem = 0; // expected-warning{{Use of memory after it is freed}} | 
 | } | 
 |  | 
 |  | 
 | struct IntWrapper { | 
 |   IntWrapper() : x(0) {} | 
 |   ~IntWrapper(); | 
 |   int *x; | 
 | }; | 
 |  | 
 | void testArrayInvalidation() { | 
 |   int i = 42; | 
 |   int j = 42; | 
 |  | 
 |   { | 
 |     IntWrapper arr[2]; | 
 |  | 
 |     // There should be no undefined value warnings here. | 
 |     // Eventually these should be TRUE as well, but right now | 
 |     // we can't handle array constructors. | 
 |     clang_analyzer_eval(arr[0].x == 0); // expected-warning{{UNKNOWN}} | 
 |     clang_analyzer_eval(arr[1].x == 0); // expected-warning{{UNKNOWN}} | 
 |  | 
 |     arr[0].x = &i; | 
 |     arr[1].x = &j; | 
 |     clang_analyzer_eval(*arr[0].x == 42); // expected-warning{{TRUE}} | 
 |     clang_analyzer_eval(*arr[1].x == 42); // expected-warning{{TRUE}} | 
 |   } | 
 |  | 
 |   // The destructors should have invalidated i and j. | 
 |   clang_analyzer_eval(i == 42); // expected-warning{{UNKNOWN}} | 
 |   clang_analyzer_eval(j == 42); // expected-warning{{UNKNOWN}} | 
 | } | 
 |  | 
 |  | 
 |  | 
 | // Don't crash on a default argument inside an initializer. | 
 | struct DefaultArg { | 
 |   DefaultArg(int x = 0) {} | 
 |   ~DefaultArg(); | 
 | }; | 
 |  | 
 | struct InheritsDefaultArg : DefaultArg { | 
 |   InheritsDefaultArg() {} | 
 |   virtual ~InheritsDefaultArg(); | 
 | }; | 
 |  | 
 | void testDefaultArg() { | 
 |   InheritsDefaultArg a; | 
 |   // Force a bug to be emitted. | 
 |   *(char *)0 = 1; // expected-warning{{Dereference of null pointer}} | 
 | } | 
 |  | 
 |  | 
 | namespace DestructorVirtualCalls { | 
 |   class A { | 
 |   public: | 
 |     int *out1, *out2, *out3; | 
 |  | 
 |     virtual int get() { return 1; } | 
 |  | 
 |     ~A() { | 
 |       *out1 = get(); | 
 |     } | 
 |   }; | 
 |  | 
 |   class B : public A { | 
 |   public: | 
 |     virtual int get() { return 2; } | 
 |  | 
 |     ~B() { | 
 |       *out2 = get(); | 
 |     } | 
 |   }; | 
 |  | 
 |   class C : public B { | 
 |   public: | 
 |     virtual int get() { return 3; } | 
 |  | 
 |     ~C() { | 
 |       *out3 = get(); | 
 |     } | 
 |   }; | 
 |  | 
 |   void test() { | 
 |     int a, b, c; | 
 |  | 
 |     // New scope for the C object. | 
 |     { | 
 |       C obj; | 
 |       clang_analyzer_eval(obj.get() == 3); // expected-warning{{TRUE}} | 
 |  | 
 |       // Sanity check for devirtualization. | 
 |       A *base = &obj; | 
 |       clang_analyzer_eval(base->get() == 3); // expected-warning{{TRUE}} | 
 |  | 
 |       obj.out1 = &a; | 
 |       obj.out2 = &b; | 
 |       obj.out3 = &c; | 
 |     } | 
 |  | 
 |     clang_analyzer_eval(a == 1); // expected-warning{{TRUE}} | 
 |     clang_analyzer_eval(b == 2); // expected-warning{{TRUE}} | 
 |     clang_analyzer_eval(c == 3); // expected-warning{{TRUE}} | 
 |   } | 
 | } | 
 |  | 
 |  | 
 | namespace DestructorsShouldNotAffectReturnValues { | 
 |   class Dtor { | 
 |   public: | 
 |     ~Dtor() { | 
 |       clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} | 
 |     } | 
 |   }; | 
 |  | 
 |   void *allocate() { | 
 |     Dtor d; | 
 |     return malloc(4); // no-warning | 
 |   } | 
 |  | 
 |   void test() { | 
 |     // At one point we had an issue where the statements inside an | 
 |     // inlined destructor kept us from finding the return statement, | 
 |     // leading the analyzer to believe that the malloc'd memory had leaked. | 
 |     void *p = allocate(); | 
 |     free(p); // no-warning | 
 |   } | 
 | } | 
 |  | 
 | namespace MultipleInheritanceVirtualDtors { | 
 |   class VirtualDtor { | 
 |   protected: | 
 |     virtual ~VirtualDtor() { | 
 |       clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} | 
 |     } | 
 |   }; | 
 |  | 
 |   class NonVirtualDtor { | 
 |   protected: | 
 |     ~NonVirtualDtor() { | 
 |       clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} | 
 |     } | 
 |   }; | 
 |  | 
 |   class SubclassA : public VirtualDtor, public NonVirtualDtor { | 
 |   public: | 
 |     virtual ~SubclassA() {} | 
 |   }; | 
 |   class SubclassB : public NonVirtualDtor, public VirtualDtor { | 
 |   public: | 
 |     virtual ~SubclassB() {} | 
 |   }; | 
 |  | 
 |   void test() { | 
 |     SubclassA a; | 
 |     SubclassB b; | 
 |   } | 
 | } | 
 |  | 
 | namespace ExplicitDestructorCall { | 
 |   class VirtualDtor { | 
 |   public: | 
 |     virtual ~VirtualDtor() { | 
 |       clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} | 
 |     } | 
 |   }; | 
 |    | 
 |   class Subclass : public VirtualDtor { | 
 |   public: | 
 |     virtual ~Subclass() { | 
 |       clang_analyzer_checkInlined(false); // no-warning | 
 |     } | 
 |   }; | 
 |    | 
 |   void destroy(Subclass *obj) { | 
 |     obj->VirtualDtor::~VirtualDtor(); | 
 |   } | 
 | } | 
 |  | 
 |  | 
 | namespace MultidimensionalArrays { | 
 |   void testArrayInvalidation() { | 
 |     int i = 42; | 
 |     int j = 42; | 
 |  | 
 |     { | 
 |       IntWrapper arr[2][2]; | 
 |  | 
 |       // There should be no undefined value warnings here. | 
 |       // Eventually these should be TRUE as well, but right now | 
 |       // we can't handle array constructors. | 
 |       clang_analyzer_eval(arr[0][0].x == 0); // expected-warning{{UNKNOWN}} | 
 |       clang_analyzer_eval(arr[1][1].x == 0); // expected-warning{{UNKNOWN}} | 
 |  | 
 |       arr[0][0].x = &i; | 
 |       arr[1][1].x = &j; | 
 |       clang_analyzer_eval(*arr[0][0].x == 42); // expected-warning{{TRUE}} | 
 |       clang_analyzer_eval(*arr[1][1].x == 42); // expected-warning{{TRUE}} | 
 |     } | 
 |  | 
 |     // The destructors should have invalidated i and j. | 
 |     clang_analyzer_eval(i == 42); // expected-warning{{UNKNOWN}} | 
 |     clang_analyzer_eval(j == 42); // expected-warning{{UNKNOWN}} | 
 |   } | 
 | } | 
 |  | 
 | namespace LifetimeExtension { | 
 |   struct IntWrapper { | 
 | 	int x; | 
 | 	IntWrapper(int y) : x(y) {} | 
 | 	IntWrapper() { | 
 |       extern void use(int); | 
 |       use(x); // no-warning | 
 | 	} | 
 |   }; | 
 |  | 
 |   struct DerivedWrapper : public IntWrapper { | 
 | 	DerivedWrapper(int y) : IntWrapper(y) {} | 
 |   }; | 
 |  | 
 |   DerivedWrapper get() { | 
 | 	return DerivedWrapper(1); | 
 |   } | 
 |  | 
 |   void test() { | 
 | 	const DerivedWrapper &d = get(); // lifetime extended here | 
 |   } | 
 |  | 
 |  | 
 |   class SaveOnDestruct { | 
 |   public: | 
 |     static int lastOutput; | 
 |     int value; | 
 |  | 
 |     SaveOnDestruct(); | 
 |     ~SaveOnDestruct() { | 
 |       lastOutput = value; | 
 |     } | 
 |   }; | 
 |  | 
 |   void testSimple() { | 
 |     { | 
 |       const SaveOnDestruct &obj = SaveOnDestruct(); | 
 |       if (obj.value != 42) | 
 |         return; | 
 |       // destructor called here | 
 |     } | 
 |  | 
 |     clang_analyzer_eval(SaveOnDestruct::lastOutput == 42); // expected-warning{{TRUE}} | 
 |   } | 
 |  | 
 |   struct NRCheck { | 
 |     bool bool_; | 
 |     NRCheck():bool_(true) {} | 
 |     ~NRCheck() __attribute__((noreturn)); | 
 |     operator bool() const { return bool_; } | 
 |   }; | 
 |  | 
 |   struct CheckAutoDestructor { | 
 |     bool bool_; | 
 |     CheckAutoDestructor():bool_(true) {} | 
 |     operator bool() const { return bool_; } | 
 |   }; | 
 |  | 
 |   struct CheckCustomDestructor { | 
 |     bool bool_; | 
 |     CheckCustomDestructor():bool_(true) {} | 
 |     ~CheckCustomDestructor(); | 
 |     operator bool() const { return bool_; } | 
 |   }; | 
 |  | 
 |   bool testUnnamedNR() { | 
 |     if (NRCheck()) | 
 |       return true; | 
 |     return false; | 
 |   } | 
 |  | 
 |   bool testNamedNR() { | 
 |     if (NRCheck c = NRCheck()) | 
 |       return true; | 
 |     return false; | 
 |   } | 
 |  | 
 |   bool testUnnamedAutoDestructor() { | 
 |     if (CheckAutoDestructor()) | 
 |       return true; | 
 |     return false; | 
 |   } | 
 |  | 
 |   bool testNamedAutoDestructor() { | 
 |     if (CheckAutoDestructor c = CheckAutoDestructor()) | 
 |       return true; | 
 |     return false; | 
 |   } | 
 |  | 
 |   bool testUnnamedCustomDestructor() { | 
 |     if (CheckCustomDestructor()) | 
 |       return true; | 
 |     return false; | 
 |   } | 
 |  | 
 |   // This case used to cause an unexpected "Undefined or garbage value returned | 
 |   // to caller" warning | 
 |   bool testNamedCustomDestructor() { | 
 |     if (CheckCustomDestructor c = CheckCustomDestructor()) | 
 |       return true; | 
 |     return false; | 
 |   } | 
 |  | 
 |   bool testMultipleTemporariesCustomDestructor() { | 
 |     if (CheckCustomDestructor c = (CheckCustomDestructor(), CheckCustomDestructor())) | 
 |       return true; | 
 |     return false; | 
 |   } | 
 |  | 
 |   class VirtualDtorBase { | 
 |   public: | 
 |     int value; | 
 |     virtual ~VirtualDtorBase() {} | 
 |   }; | 
 |  | 
 |   class SaveOnVirtualDestruct : public VirtualDtorBase { | 
 |   public: | 
 |     static int lastOutput; | 
 |  | 
 |     SaveOnVirtualDestruct(); | 
 |     virtual ~SaveOnVirtualDestruct() { | 
 |       lastOutput = value; | 
 |     } | 
 |   }; | 
 |  | 
 |   void testVirtual() { | 
 |     { | 
 |       const VirtualDtorBase &obj = SaveOnVirtualDestruct(); | 
 |       if (obj.value != 42) | 
 |         return; | 
 |       // destructor called here | 
 |     } | 
 |  | 
 |     clang_analyzer_eval(SaveOnVirtualDestruct::lastOutput == 42); // expected-warning{{TRUE}} | 
 |   } | 
 | } | 
 |  | 
 | namespace NoReturn { | 
 |   struct NR { | 
 |     ~NR() __attribute__((noreturn)); | 
 |   }; | 
 |  | 
 |   void f(int **x) { | 
 |     NR nr; | 
 |   } | 
 |  | 
 |   void g() { | 
 |     int *x; | 
 |     f(&x); | 
 |     *x = 47; // no warning | 
 |   } | 
 |  | 
 |   void g2(int *x) { | 
 |     if (! x) NR(); | 
 |     *x = 47; // no warning | 
 |   } | 
 | } | 
 |  | 
 | namespace PseudoDtor { | 
 |   template <typename T> | 
 |   void destroy(T &obj) { | 
 |     clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} | 
 |     obj.~T(); | 
 |   } | 
 |  | 
 |   void test() { | 
 |     int i; | 
 |     destroy(i); | 
 |     clang_analyzer_eval(true); // expected-warning{{TRUE}} | 
 |   } | 
 | } | 
 |  | 
 | namespace Incomplete { | 
 |   class Foo; // expected-note{{forward declaration}} | 
 |   void f(Foo *foo) { delete foo; } // expected-warning{{deleting pointer to incomplete type}} | 
 | } | 
 |  | 
 | namespace TypeTraitExpr { | 
 | template <bool IsSimple, typename T> | 
 | struct copier { | 
 |   static void do_copy(T *dest, const T *src, unsigned count); | 
 | }; | 
 | template <typename T, typename U> | 
 | void do_copy(T *dest, const U *src, unsigned count) { | 
 |   const bool IsSimple = __is_trivial(T) && __is_same(T, U); | 
 |   copier<IsSimple, T>::do_copy(dest, src, count); | 
 | } | 
 | struct NonTrivial { | 
 |   int *p; | 
 |   NonTrivial() : p(new int[1]) { p[0] = 0; } | 
 |   NonTrivial(const NonTrivial &other) { | 
 |     p = new int[1]; | 
 |     do_copy(p, other.p, 1); | 
 |   } | 
 |   NonTrivial &operator=(const NonTrivial &other) { | 
 |     p = other.p; | 
 |     return *this; | 
 |   } | 
 |   ~NonTrivial() { | 
 |     delete[] p; // expected-warning {{free released memory}} | 
 |   } | 
 | }; | 
 |  | 
 | void f() { | 
 |   NonTrivial nt1; | 
 |   NonTrivial nt2(nt1); | 
 |   nt1 = nt2; | 
 |   clang_analyzer_eval(__is_trivial(NonTrivial)); // expected-warning{{FALSE}} | 
 |   clang_analyzer_eval(__alignof(NonTrivial) > 0); // expected-warning{{TRUE}} | 
 | } | 
 | } |