|  | // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-inlining=destructors -verify -std=c++11 %s | 
|  | // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-inlining=destructors -verify -std=c++17 %s | 
|  |  | 
|  | using size_t =  __typeof(sizeof(int)); | 
|  |  | 
|  | void clang_analyzer_eval(bool); | 
|  | void clang_analyzer_checkInlined(bool); | 
|  | void clang_analyzer_warnIfReached(); | 
|  | void clang_analyzer_explain(int); | 
|  |  | 
|  | int a, b, c, d; | 
|  |  | 
|  | struct InlineDtor { | 
|  | static int cnt; | 
|  | static int dtorCalled; | 
|  | ~InlineDtor() { | 
|  | switch (dtorCalled % 4) { | 
|  | case 0: | 
|  | a = cnt++; | 
|  | break; | 
|  | case 1: | 
|  | b = cnt++; | 
|  | break; | 
|  | case 2: | 
|  | c = cnt++; | 
|  | break; | 
|  | case 3: | 
|  | d = cnt++; | 
|  | break; | 
|  | } | 
|  |  | 
|  | ++dtorCalled; | 
|  | } | 
|  | }; | 
|  |  | 
|  | int InlineDtor::cnt = 0; | 
|  | int InlineDtor::dtorCalled = 0; | 
|  |  | 
|  | void foo() { | 
|  | InlineDtor::cnt = 0; | 
|  | InlineDtor::dtorCalled = 0; | 
|  | InlineDtor arr[4]; | 
|  | } | 
|  |  | 
|  | void testAutoDtor() { | 
|  | foo(); | 
|  |  | 
|  | clang_analyzer_eval(a == 0); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(b == 1); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(c == 2); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(d == 3); // expected-warning {{TRUE}} | 
|  | } | 
|  |  | 
|  | void testDeleteDtor() { | 
|  | InlineDtor::cnt = 10; | 
|  | InlineDtor::dtorCalled = 0; | 
|  |  | 
|  | InlineDtor *arr = new InlineDtor[4]; | 
|  | delete[] arr; | 
|  |  | 
|  | clang_analyzer_eval(a == 10); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(b == 11); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(c == 12); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(d == 13); // expected-warning {{TRUE}} | 
|  | } | 
|  |  | 
|  | struct MemberDtor { | 
|  | InlineDtor arr[4]; | 
|  | }; | 
|  |  | 
|  | void testMemberDtor() { | 
|  | InlineDtor::cnt = 5; | 
|  | InlineDtor::dtorCalled = 0; | 
|  |  | 
|  | MemberDtor *MD = new MemberDtor{}; | 
|  | delete MD; | 
|  |  | 
|  | clang_analyzer_eval(a == 5); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(b == 6); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(c == 7); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(d == 8); // expected-warning {{TRUE}} | 
|  | } | 
|  |  | 
|  | struct MultipleMemberDtor | 
|  | { | 
|  | InlineDtor arr[4]; | 
|  | InlineDtor arr2[4]; | 
|  | }; | 
|  |  | 
|  | void testMultipleMemberDtor() { | 
|  | InlineDtor::cnt = 30; | 
|  | InlineDtor::dtorCalled = 0; | 
|  |  | 
|  | MultipleMemberDtor *MD = new MultipleMemberDtor{}; | 
|  | delete MD; | 
|  |  | 
|  | clang_analyzer_eval(a == 34); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(b == 35); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(c == 36); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(d == 37); // expected-warning {{TRUE}} | 
|  | } | 
|  |  | 
|  | int EvalOrderArr[4]; | 
|  |  | 
|  | struct EvalOrder | 
|  | { | 
|  | int ctor = 0; | 
|  | static int dtorCalled; | 
|  | static int ctorCalled; | 
|  |  | 
|  | EvalOrder() { ctor = ctorCalled++; }; | 
|  |  | 
|  | ~EvalOrder() { EvalOrderArr[ctor] = dtorCalled++; } | 
|  | }; | 
|  |  | 
|  | int EvalOrder::ctorCalled = 0; | 
|  | int EvalOrder::dtorCalled = 0; | 
|  |  | 
|  | void dtorEvaluationOrder() { | 
|  | EvalOrder::ctorCalled = 0; | 
|  | EvalOrder::dtorCalled = 0; | 
|  |  | 
|  | EvalOrder* eptr = new EvalOrder[4]; | 
|  | delete[] eptr; | 
|  |  | 
|  | clang_analyzer_eval(EvalOrder::dtorCalled == 4); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrder::dtorCalled == EvalOrder::ctorCalled); // expected-warning {{TRUE}} | 
|  |  | 
|  | clang_analyzer_eval(EvalOrderArr[0] == 3); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrderArr[1] == 2); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrderArr[2] == 1); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrderArr[3] == 0); // expected-warning {{TRUE}} | 
|  | } | 
|  |  | 
|  | struct EmptyDtor { | 
|  | ~EmptyDtor(){}; | 
|  | }; | 
|  |  | 
|  | struct DefaultDtor { | 
|  | ~DefaultDtor() = default; | 
|  | }; | 
|  |  | 
|  | // This function used to fail on an assertion. | 
|  | void no_crash() { | 
|  | EmptyDtor* eptr = new EmptyDtor[4]; | 
|  | delete[] eptr; | 
|  | clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}} | 
|  |  | 
|  | DefaultDtor* dptr = new DefaultDtor[4]; | 
|  | delete[] dptr; | 
|  | clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}} | 
|  | } | 
|  |  | 
|  | // This snippet used to crash. | 
|  | namespace crash2 | 
|  | { | 
|  | template <class _Tp> class unique_ptr { | 
|  | typedef _Tp *pointer; | 
|  | pointer __ptr_; | 
|  |  | 
|  | public: | 
|  | unique_ptr(pointer __p) : __ptr_(__p) {} | 
|  | ~unique_ptr() { reset(); } | 
|  | pointer get() { return __ptr_;} | 
|  | void reset() {} | 
|  | }; | 
|  |  | 
|  | struct S; | 
|  |  | 
|  | S *makeS(); | 
|  | int bar(S *x, S *y); | 
|  |  | 
|  | void foo() { | 
|  | unique_ptr<S> x(makeS()), y(makeS()); | 
|  | bar(x.get(), y.get()); | 
|  | } | 
|  |  | 
|  | void bar() { | 
|  | foo(); | 
|  | clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}} | 
|  | } | 
|  |  | 
|  | } // namespace crash2 | 
|  |  | 
|  | // This snippet used to crash. | 
|  | namespace crash3 | 
|  | { | 
|  | struct InlineDtor { | 
|  | ~InlineDtor() {} | 
|  | }; | 
|  | struct MultipleMemberDtor | 
|  | { | 
|  | InlineDtor arr[4]; | 
|  | InlineDtor arr2[4]; | 
|  | }; | 
|  |  | 
|  | void foo(){ | 
|  | auto *arr = new MultipleMemberDtor[4]; | 
|  | delete[] arr; | 
|  | clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}} | 
|  | } | 
|  | } // namespace crash3 | 
|  |  | 
|  | namespace crash4 { | 
|  | struct a { | 
|  | a *b; | 
|  | }; | 
|  | struct c { | 
|  | a d; | 
|  | c(); | 
|  | ~c() { | 
|  | for (a e = d;; e = *e.b) | 
|  | ; | 
|  | } | 
|  | }; | 
|  | void f() { | 
|  | c g; | 
|  | clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}} | 
|  | } | 
|  |  | 
|  | } // namespace crash4 | 
|  |  | 
|  | namespace crash5 { | 
|  | namespace std { | 
|  | template <class _Tp> class unique_ptr { | 
|  | _Tp *__ptr_; | 
|  | public: | 
|  | unique_ptr(_Tp *__p) : __ptr_(__p) {} | 
|  | ~unique_ptr() {} | 
|  | }; | 
|  | } // namespace std | 
|  |  | 
|  | int SSL_use_certificate(int *arg) { | 
|  | std::unique_ptr<int> free_x509(arg); | 
|  | { | 
|  | if (SSL_use_certificate(arg)) { | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}} | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | } // namespace crash5 | 
|  |  | 
|  | void zeroLength(){ | 
|  | InlineDtor::dtorCalled = 0; | 
|  |  | 
|  | auto *arr = new InlineDtor[0]; | 
|  | delete[] arr; | 
|  |  | 
|  | auto *arr2 = new InlineDtor[2][0][2]; | 
|  | delete[] arr2; | 
|  |  | 
|  | auto *arr3 = new InlineDtor[0][2][2]; | 
|  | delete[] arr3; | 
|  |  | 
|  | auto *arr4 = new InlineDtor[2][2][0]; | 
|  | delete[] arr4; | 
|  |  | 
|  | clang_analyzer_eval(InlineDtor::dtorCalled == 0); // expected-warning {{TRUE}} | 
|  | } | 
|  |  | 
|  |  | 
|  | void evalOrderPrep() { | 
|  | EvalOrderArr[0] = 0; | 
|  | EvalOrderArr[1] = 0; | 
|  | EvalOrderArr[2] = 0; | 
|  | EvalOrderArr[3] = 0; | 
|  |  | 
|  | EvalOrder::ctorCalled = 0; | 
|  | EvalOrder::dtorCalled = 0; | 
|  | } | 
|  |  | 
|  | void multidimensionalPrep(){ | 
|  | EvalOrder::ctorCalled = 0; | 
|  | EvalOrder::dtorCalled = 0; | 
|  |  | 
|  | EvalOrder arr[2][2]; | 
|  | } | 
|  |  | 
|  | void multidimensional(){ | 
|  | evalOrderPrep(); | 
|  | multidimensionalPrep(); | 
|  |  | 
|  | clang_analyzer_eval(EvalOrder::dtorCalled == 4); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrder::dtorCalled == EvalOrder::ctorCalled); // expected-warning {{TRUE}} | 
|  |  | 
|  | clang_analyzer_eval(EvalOrderArr[0] == 3); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrderArr[1] == 2); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrderArr[2] == 1); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrderArr[3] == 0); // expected-warning {{TRUE}} | 
|  | } | 
|  |  | 
|  | void multidimensionalHeap() { | 
|  | evalOrderPrep(); | 
|  |  | 
|  | auto* eptr = new EvalOrder[2][2]; | 
|  | delete[] eptr; | 
|  |  | 
|  | clang_analyzer_eval(EvalOrder::dtorCalled == 4); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrder::dtorCalled == EvalOrder::ctorCalled); // expected-warning {{TRUE}} | 
|  |  | 
|  | clang_analyzer_eval(EvalOrderArr[0] == 3); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrderArr[1] == 2); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrderArr[2] == 1); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrderArr[3] == 0); // expected-warning {{TRUE}} | 
|  | } | 
|  |  | 
|  | struct MultiWrapper{ | 
|  | EvalOrder arr[2][2]; | 
|  | }; | 
|  |  | 
|  | void multidimensionalMember(){ | 
|  | evalOrderPrep(); | 
|  |  | 
|  | auto* mptr = new MultiWrapper; | 
|  | delete mptr; | 
|  |  | 
|  | clang_analyzer_eval(EvalOrder::dtorCalled == 4); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrder::dtorCalled == EvalOrder::ctorCalled); // expected-warning {{TRUE}} | 
|  |  | 
|  | clang_analyzer_eval(EvalOrderArr[0] == 3); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrderArr[1] == 2); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrderArr[2] == 1); // expected-warning {{TRUE}} | 
|  | clang_analyzer_eval(EvalOrderArr[3] == 0); // expected-warning {{TRUE}} | 
|  | } | 
|  |  | 
|  | void *memset(void *, int, size_t); | 
|  | void clang_analyzer_dumpElementCount(InlineDtor *); | 
|  |  | 
|  | void nonConstantRegionExtent(){ | 
|  |  | 
|  | InlineDtor::dtorCalled = 0; | 
|  |  | 
|  | int x = 3; | 
|  | memset(&x, 1, sizeof(x)); | 
|  |  | 
|  | InlineDtor *arr = new InlineDtor[x]; | 
|  | clang_analyzer_dumpElementCount(arr); // expected-warning {{conj_$0}} | 
|  | delete [] arr; | 
|  |  | 
|  | //FIXME: This should be TRUE but memset also sets this | 
|  | // region to a conjured symbol. | 
|  | clang_analyzer_eval(InlineDtor::dtorCalled == 0); // expected-warning {{TRUE}} expected-warning {{FALSE}} | 
|  | } | 
|  |  | 
|  | namespace crash6 { | 
|  |  | 
|  | struct NonTrivialItem { | 
|  | ~NonTrivialItem(); | 
|  | }; | 
|  |  | 
|  | struct WeirdVec { | 
|  | void clear() { | 
|  | delete[] data; | 
|  | size = 0; | 
|  | } | 
|  | NonTrivialItem *data; | 
|  | unsigned size; | 
|  | }; | 
|  |  | 
|  | void top(int j) { | 
|  | WeirdVec *p = new WeirdVec; | 
|  |  | 
|  | p[j].size = 0; | 
|  | delete[] p->data; // no-crash | 
|  | } | 
|  |  | 
|  | template <typename T> | 
|  | T make_unknown() { | 
|  | return reinterpret_cast<T>(static_cast<int>(0.404)); | 
|  | } | 
|  |  | 
|  | void directUnknownSymbol() { | 
|  | delete[] make_unknown<NonTrivialItem*>(); // no-crash | 
|  | } | 
|  |  | 
|  | } |