1a393e68bSGeorge Karpenkov // 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
26a5cd5e1SArtem Dergachev // RUN: FileCheck --input-file=%t %s
3a393e68bSGeorge Karpenkov // RUN: %clang_analyze_cc1 -triple x86_64-unknown-linux -analyzer-checker=core,debug.ExprInspection -std=c++14 -verify -analyzer-config eagerly-assume=false %s
46a5cd5e1SArtem Dergachev 
56a5cd5e1SArtem Dergachev void clang_analyzer_eval(bool);
66a5cd5e1SArtem Dergachev 
76a5cd5e1SArtem Dergachev int global;
86a5cd5e1SArtem Dergachev 
96a5cd5e1SArtem Dergachev namespace variant_0 {
106a5cd5e1SArtem Dergachev // This variant of the code works correctly. Function foo() is not a template
116a5cd5e1SArtem Dergachev // function. Note that there are two destructors within foo().
126a5cd5e1SArtem Dergachev 
136a5cd5e1SArtem Dergachev class A {
146a5cd5e1SArtem Dergachev public:
~A()156a5cd5e1SArtem Dergachev   ~A() { ++global; }
166a5cd5e1SArtem Dergachev };
176a5cd5e1SArtem Dergachev 
186a5cd5e1SArtem Dergachev class B {
196a5cd5e1SArtem Dergachev   A a;
206a5cd5e1SArtem Dergachev };
216a5cd5e1SArtem Dergachev 
226a5cd5e1SArtem Dergachev // CHECK: void foo(int)
236a5cd5e1SArtem Dergachev // CHECK:       [B1]
24*888673b6SJonas Devlieghere // CHECK-NEXT:    1:  (CXXConstructExpr, [B1.2], class variant_0::B)
25*888673b6SJonas Devlieghere // CHECK-NEXT:    2: variant_0::B i;
266a5cd5e1SArtem Dergachev // CHECK-NEXT:    3: operator=
276a5cd5e1SArtem Dergachev // CHECK-NEXT:    4: [B1.3] (ImplicitCastExpr, FunctionToPointerDecay, class variant_0::B &(*)(class variant_0::B &&) noexcept)
286a5cd5e1SArtem Dergachev // CHECK-NEXT:    5: i
296a5cd5e1SArtem Dergachev // CHECK-NEXT:    6: {} (CXXConstructExpr, [B1.7], [B1.8], class variant_0::B)
306a5cd5e1SArtem Dergachev // CHECK-NEXT:    7: [B1.6] (BindTemporary)
316a5cd5e1SArtem Dergachev // CHECK-NEXT:    8: [B1.7]
326a5cd5e1SArtem Dergachev // CHECK-NEXT:    9: [B1.5] = [B1.8] (OperatorCall)
336a5cd5e1SArtem Dergachev // CHECK-NEXT:   10: ~variant_0::B() (Temporary object destructor)
34*888673b6SJonas Devlieghere // CHECK-NEXT:   11: [B1.2].~variant_0::B() (Implicit destructor)
foo(int)356a5cd5e1SArtem Dergachev void foo(int) {
366a5cd5e1SArtem Dergachev   B i;
376a5cd5e1SArtem Dergachev   i = {};
386a5cd5e1SArtem Dergachev }
396a5cd5e1SArtem Dergachev 
bar()406a5cd5e1SArtem Dergachev void bar() {
416a5cd5e1SArtem Dergachev   global = 0;
426a5cd5e1SArtem Dergachev   foo(1);
436a5cd5e1SArtem Dergachev   clang_analyzer_eval(global == 2); // expected-warning{{TRUE}}
446a5cd5e1SArtem Dergachev }
456a5cd5e1SArtem Dergachev 
466a5cd5e1SArtem Dergachev } // end namespace variant_0
476a5cd5e1SArtem Dergachev 
486a5cd5e1SArtem Dergachev namespace variant_1 {
496a5cd5e1SArtem Dergachev // Suddenly, if we turn foo() into a template, we are missing a
506a5cd5e1SArtem Dergachev // CXXBindTemporaryExpr in the AST, and therefore we're missing a
516a5cd5e1SArtem Dergachev // temporary destructor in the CFG.
526a5cd5e1SArtem Dergachev 
536a5cd5e1SArtem Dergachev class A {
546a5cd5e1SArtem Dergachev public:
~A()556a5cd5e1SArtem Dergachev   ~A() { ++global; }
566a5cd5e1SArtem Dergachev };
576a5cd5e1SArtem Dergachev 
586a5cd5e1SArtem Dergachev class B {
596a5cd5e1SArtem Dergachev   A a;
606a5cd5e1SArtem Dergachev };
616a5cd5e1SArtem Dergachev 
626a5cd5e1SArtem Dergachev // FIXME: Find the construction context for {} and enforce the temporary
636a5cd5e1SArtem Dergachev // destructor.
646a5cd5e1SArtem Dergachev // CHECK: template<> void foo<int>(int)
656a5cd5e1SArtem Dergachev // CHECK:       [B1]
66*888673b6SJonas Devlieghere // CHECK-NEXT:    1:  (CXXConstructExpr, [B1.2], class variant_1::B)
67*888673b6SJonas Devlieghere // CHECK-NEXT:    2: variant_1::B i;
686a5cd5e1SArtem Dergachev // CHECK-NEXT:    3: operator=
696a5cd5e1SArtem Dergachev // CHECK-NEXT:    4: [B1.3] (ImplicitCastExpr, FunctionToPointerDecay, class variant_1::B &(*)(class variant_1::B &&) noexcept)
706a5cd5e1SArtem Dergachev // CHECK-NEXT:    5: i
716a5cd5e1SArtem Dergachev // CHECK-NEXT:    6: {} (CXXConstructExpr, class variant_1::B)
726a5cd5e1SArtem Dergachev // CHECK-NEXT:    7: [B1.6]
736a5cd5e1SArtem Dergachev // CHECK-NEXT:    8: [B1.5] = [B1.7] (OperatorCall)
74*888673b6SJonas Devlieghere // CHECK-NEXT:    9: [B1.2].~variant_1::B() (Implicit destructor)
foo(T)756a5cd5e1SArtem Dergachev template <typename T> void foo(T) {
766a5cd5e1SArtem Dergachev   B i;
776a5cd5e1SArtem Dergachev   i = {};
786a5cd5e1SArtem Dergachev }
796a5cd5e1SArtem Dergachev 
bar()806a5cd5e1SArtem Dergachev void bar() {
816a5cd5e1SArtem Dergachev   global = 0;
826a5cd5e1SArtem Dergachev   foo(1);
836a5cd5e1SArtem Dergachev   // FIXME: Should be TRUE, i.e. we should call (and inline) two destructors.
846a5cd5e1SArtem Dergachev   clang_analyzer_eval(global == 2); // expected-warning{{UNKNOWN}}
856a5cd5e1SArtem Dergachev }
866a5cd5e1SArtem Dergachev 
876a5cd5e1SArtem Dergachev } // end namespace variant_1
886a5cd5e1SArtem Dergachev 
896a5cd5e1SArtem Dergachev namespace variant_2 {
906a5cd5e1SArtem Dergachev // Making field 'a' in class 'B' public turns the class into an aggregate.
916a5cd5e1SArtem Dergachev // In this case there is no constructor at {} - only an aggregate
926a5cd5e1SArtem Dergachev // initialization. Aggregate initialization is unsupported for now.
936a5cd5e1SArtem Dergachev 
946a5cd5e1SArtem Dergachev class A {
956a5cd5e1SArtem Dergachev public:
~A()966a5cd5e1SArtem Dergachev   ~A() { ++global; }
976a5cd5e1SArtem Dergachev };
986a5cd5e1SArtem Dergachev 
996a5cd5e1SArtem Dergachev class B {
1006a5cd5e1SArtem Dergachev public:
1016a5cd5e1SArtem Dergachev   A a;
1026a5cd5e1SArtem Dergachev };
1036a5cd5e1SArtem Dergachev 
1046a5cd5e1SArtem Dergachev // CHECK: template<> void foo<int>(int)
1056a5cd5e1SArtem Dergachev // CHECK:       [B1]
106*888673b6SJonas Devlieghere // CHECK-NEXT:    1:  (CXXConstructExpr, [B1.2], class variant_2::B)
107*888673b6SJonas Devlieghere // CHECK-NEXT:    2: variant_2::B i;
1086a5cd5e1SArtem Dergachev // CHECK-NEXT:    3: operator=
1096a5cd5e1SArtem Dergachev // CHECK-NEXT:    4: [B1.3] (ImplicitCastExpr, FunctionToPointerDecay, class variant_2::B &(*)(class variant_2::B &&) noexcept)
1106a5cd5e1SArtem Dergachev // CHECK-NEXT:    5: i
1116a5cd5e1SArtem Dergachev // CHECK-NEXT:    6: {}
1126a5cd5e1SArtem Dergachev // CHECK-NEXT:    7: {}
1136a5cd5e1SArtem Dergachev // CHECK-NEXT:    8: [B1.7] (BindTemporary)
1146a5cd5e1SArtem Dergachev // CHECK-NEXT:    9: [B1.8]
1156a5cd5e1SArtem Dergachev // CHECK-NEXT:   10: [B1.5] = [B1.9] (OperatorCall)
1166a5cd5e1SArtem Dergachev // CHECK-NEXT:   11: ~variant_2::B() (Temporary object destructor)
117*888673b6SJonas Devlieghere // CHECK-NEXT:   12: [B1.2].~variant_2::B() (Implicit destructor)
foo(T)1186a5cd5e1SArtem Dergachev template <typename T> void foo(T) {
1196a5cd5e1SArtem Dergachev   B i;
1206a5cd5e1SArtem Dergachev   i = {};
1216a5cd5e1SArtem Dergachev }
1226a5cd5e1SArtem Dergachev 
bar()1236a5cd5e1SArtem Dergachev void bar() {
1246a5cd5e1SArtem Dergachev   global = 0;
1256a5cd5e1SArtem Dergachev   foo(1);
1266a5cd5e1SArtem Dergachev   // FIXME: Should be TRUE, i.e. we should call (and inline) two destructors.
1276a5cd5e1SArtem Dergachev   clang_analyzer_eval(global == 2); // expected-warning{{UNKNOWN}}
1286a5cd5e1SArtem Dergachev }
1296a5cd5e1SArtem Dergachev 
1306a5cd5e1SArtem Dergachev } // end namespace variant_2
131