1*6a5cd5e1SArtem Dergachev // RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG %s > %t 2>&1 2*6a5cd5e1SArtem Dergachev // RUN: FileCheck --input-file=%t %s 3*6a5cd5e1SArtem Dergachev // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++14 -verify %s 4*6a5cd5e1SArtem Dergachev 5*6a5cd5e1SArtem Dergachev void clang_analyzer_eval(bool); 6*6a5cd5e1SArtem Dergachev 7*6a5cd5e1SArtem Dergachev int global; 8*6a5cd5e1SArtem Dergachev 9*6a5cd5e1SArtem Dergachev namespace variant_0 { 10*6a5cd5e1SArtem Dergachev // This variant of the code works correctly. Function foo() is not a template 11*6a5cd5e1SArtem Dergachev // function. Note that there are two destructors within foo(). 12*6a5cd5e1SArtem Dergachev 13*6a5cd5e1SArtem Dergachev class A { 14*6a5cd5e1SArtem Dergachev public: 15*6a5cd5e1SArtem Dergachev ~A() { ++global; } 16*6a5cd5e1SArtem Dergachev }; 17*6a5cd5e1SArtem Dergachev 18*6a5cd5e1SArtem Dergachev class B { 19*6a5cd5e1SArtem Dergachev A a; 20*6a5cd5e1SArtem Dergachev }; 21*6a5cd5e1SArtem Dergachev 22*6a5cd5e1SArtem Dergachev // CHECK: void foo(int) 23*6a5cd5e1SArtem Dergachev // CHECK: [B1] 24*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 1: (CXXConstructExpr, [B1.2], class variant_0::B) 25*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 2: variant_0::B i; 26*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 3: operator= 27*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, FunctionToPointerDecay, class variant_0::B &(*)(class variant_0::B &&) noexcept) 28*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 5: i 29*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 6: {} (CXXConstructExpr, [B1.7], [B1.8], class variant_0::B) 30*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 7: [B1.6] (BindTemporary) 31*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 8: [B1.7] 32*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 9: [B1.5] = [B1.8] (OperatorCall) 33*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 10: ~variant_0::B() (Temporary object destructor) 34*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 11: [B1.2].~B() (Implicit destructor) 35*6a5cd5e1SArtem Dergachev void foo(int) { 36*6a5cd5e1SArtem Dergachev B i; 37*6a5cd5e1SArtem Dergachev i = {}; 38*6a5cd5e1SArtem Dergachev } 39*6a5cd5e1SArtem Dergachev 40*6a5cd5e1SArtem Dergachev void bar() { 41*6a5cd5e1SArtem Dergachev global = 0; 42*6a5cd5e1SArtem Dergachev foo(1); 43*6a5cd5e1SArtem Dergachev clang_analyzer_eval(global == 2); // expected-warning{{TRUE}} 44*6a5cd5e1SArtem Dergachev } 45*6a5cd5e1SArtem Dergachev 46*6a5cd5e1SArtem Dergachev } // end namespace variant_0 47*6a5cd5e1SArtem Dergachev 48*6a5cd5e1SArtem Dergachev namespace variant_1 { 49*6a5cd5e1SArtem Dergachev // Suddenly, if we turn foo() into a template, we are missing a 50*6a5cd5e1SArtem Dergachev // CXXBindTemporaryExpr in the AST, and therefore we're missing a 51*6a5cd5e1SArtem Dergachev // temporary destructor in the CFG. 52*6a5cd5e1SArtem Dergachev 53*6a5cd5e1SArtem Dergachev class A { 54*6a5cd5e1SArtem Dergachev public: 55*6a5cd5e1SArtem Dergachev ~A() { ++global; } 56*6a5cd5e1SArtem Dergachev }; 57*6a5cd5e1SArtem Dergachev 58*6a5cd5e1SArtem Dergachev class B { 59*6a5cd5e1SArtem Dergachev A a; 60*6a5cd5e1SArtem Dergachev }; 61*6a5cd5e1SArtem Dergachev 62*6a5cd5e1SArtem Dergachev // FIXME: Find the construction context for {} and enforce the temporary 63*6a5cd5e1SArtem Dergachev // destructor. 64*6a5cd5e1SArtem Dergachev // CHECK: template<> void foo<int>(int) 65*6a5cd5e1SArtem Dergachev // CHECK: [B1] 66*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 1: (CXXConstructExpr, [B1.2], class variant_1::B) 67*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 2: variant_1::B i; 68*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 3: operator= 69*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, FunctionToPointerDecay, class variant_1::B &(*)(class variant_1::B &&) noexcept) 70*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 5: i 71*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 6: {} (CXXConstructExpr, class variant_1::B) 72*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 7: [B1.6] 73*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 8: [B1.5] = [B1.7] (OperatorCall) 74*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 9: [B1.2].~B() (Implicit destructor) 75*6a5cd5e1SArtem Dergachev template <typename T> void foo(T) { 76*6a5cd5e1SArtem Dergachev B i; 77*6a5cd5e1SArtem Dergachev i = {}; 78*6a5cd5e1SArtem Dergachev } 79*6a5cd5e1SArtem Dergachev 80*6a5cd5e1SArtem Dergachev void bar() { 81*6a5cd5e1SArtem Dergachev global = 0; 82*6a5cd5e1SArtem Dergachev foo(1); 83*6a5cd5e1SArtem Dergachev // FIXME: Should be TRUE, i.e. we should call (and inline) two destructors. 84*6a5cd5e1SArtem Dergachev clang_analyzer_eval(global == 2); // expected-warning{{UNKNOWN}} 85*6a5cd5e1SArtem Dergachev } 86*6a5cd5e1SArtem Dergachev 87*6a5cd5e1SArtem Dergachev } // end namespace variant_1 88*6a5cd5e1SArtem Dergachev 89*6a5cd5e1SArtem Dergachev namespace variant_2 { 90*6a5cd5e1SArtem Dergachev // Making field 'a' in class 'B' public turns the class into an aggregate. 91*6a5cd5e1SArtem Dergachev // In this case there is no constructor at {} - only an aggregate 92*6a5cd5e1SArtem Dergachev // initialization. Aggregate initialization is unsupported for now. 93*6a5cd5e1SArtem Dergachev 94*6a5cd5e1SArtem Dergachev class A { 95*6a5cd5e1SArtem Dergachev public: 96*6a5cd5e1SArtem Dergachev ~A() { ++global; } 97*6a5cd5e1SArtem Dergachev }; 98*6a5cd5e1SArtem Dergachev 99*6a5cd5e1SArtem Dergachev class B { 100*6a5cd5e1SArtem Dergachev public: 101*6a5cd5e1SArtem Dergachev A a; 102*6a5cd5e1SArtem Dergachev }; 103*6a5cd5e1SArtem Dergachev 104*6a5cd5e1SArtem Dergachev // CHECK: template<> void foo<int>(int) 105*6a5cd5e1SArtem Dergachev // CHECK: [B1] 106*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 1: (CXXConstructExpr, [B1.2], class variant_2::B) 107*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 2: variant_2::B i; 108*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 3: operator= 109*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, FunctionToPointerDecay, class variant_2::B &(*)(class variant_2::B &&) noexcept) 110*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 5: i 111*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 6: {} 112*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 7: {} 113*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 8: [B1.7] (BindTemporary) 114*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 9: [B1.8] 115*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 10: [B1.5] = [B1.9] (OperatorCall) 116*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 11: ~variant_2::B() (Temporary object destructor) 117*6a5cd5e1SArtem Dergachev // CHECK-NEXT: 12: [B1.2].~B() (Implicit destructor) 118*6a5cd5e1SArtem Dergachev template <typename T> void foo(T) { 119*6a5cd5e1SArtem Dergachev B i; 120*6a5cd5e1SArtem Dergachev i = {}; 121*6a5cd5e1SArtem Dergachev } 122*6a5cd5e1SArtem Dergachev 123*6a5cd5e1SArtem Dergachev void bar() { 124*6a5cd5e1SArtem Dergachev global = 0; 125*6a5cd5e1SArtem Dergachev foo(1); 126*6a5cd5e1SArtem Dergachev // FIXME: Should be TRUE, i.e. we should call (and inline) two destructors. 127*6a5cd5e1SArtem Dergachev clang_analyzer_eval(global == 2); // expected-warning{{UNKNOWN}} 128*6a5cd5e1SArtem Dergachev } 129*6a5cd5e1SArtem Dergachev 130*6a5cd5e1SArtem Dergachev } // end namespace variant_2 131