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