// RUN: %clang_cc1 -analyze -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}} } }