// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config c++-inlining=destructors -Wno-null-dereference -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}} } 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}} } }