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