|  | // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-inlining=constructors -verify %s \ | 
|  | // RUN:   2>&1 | FileCheck %s | 
|  |  | 
|  |  | 
|  | void clang_analyzer_printState(); | 
|  | template <typename T> void clang_analyzer_dump_lref(T& param); | 
|  | template <typename T> void clang_analyzer_dump_val(T param); | 
|  | template <typename T> void clang_analyzer_denote(T param, const char *name); | 
|  | template <typename T> void clang_analyzer_express(T param); | 
|  | template <typename T> T conjure(); | 
|  | template <typename... Ts> void nop(const Ts &... args) {} | 
|  |  | 
|  | struct aggr { | 
|  | int x; | 
|  | int y; | 
|  | }; | 
|  |  | 
|  | struct empty { | 
|  | }; | 
|  |  | 
|  | void test_copy_return() { | 
|  | aggr s1 = {1, 2}; | 
|  | aggr const& cr1 = aggr(s1); | 
|  | clang_analyzer_dump_lref(cr1); // expected-warning-re {{&lifetime_extended_object{aggr, cr1, S{{[0-9]+}}} }} | 
|  |  | 
|  | empty s2; | 
|  | empty const& cr2 = empty{s2}; | 
|  | clang_analyzer_dump_lref(cr2); // expected-warning-re {{&lifetime_extended_object{empty, cr2, S{{[0-9]+}}} }} | 
|  | } | 
|  |  | 
|  | void test_assign_return() { | 
|  | aggr s1 = {1, 2}; | 
|  | aggr d1; | 
|  | clang_analyzer_dump_lref(d1 = s1); // expected-warning {{&d1 }} | 
|  |  | 
|  | empty s2; | 
|  | empty d2; | 
|  | clang_analyzer_dump_lref(d2 = s2); // expected-warning {{&d2 }} was Unknown | 
|  | } | 
|  |  | 
|  |  | 
|  | namespace trivial_struct_copy { | 
|  |  | 
|  | void _01_empty_structs() { | 
|  | clang_analyzer_dump_val(conjure<empty>()); // expected-warning {{conj_$}} | 
|  | empty Empty = conjure<empty>(); | 
|  | empty Empty2 = Empty; | 
|  | empty Empty3 = Empty2; | 
|  | // All of these should refer to the exact same symbol, because all of | 
|  | // these trivial copies refer to the original conjured value. | 
|  | // There were Unknown before: | 
|  | clang_analyzer_denote(Empty, "$Empty"); | 
|  | clang_analyzer_express(Empty);  // expected-warning {{$Empty}} | 
|  | clang_analyzer_express(Empty2); // expected-warning {{$Empty}} | 
|  | clang_analyzer_express(Empty3); // expected-warning {{$Empty}} | 
|  |  | 
|  | // We should have the same Conjured symbol for "Empty", "Empty2" and "Empty3". | 
|  | clang_analyzer_printState(); | 
|  | // CHECK:       "store": { "pointer": "0x{{[0-9a-f]+}}", "items": [ | 
|  | // CHECK-NEXT:    { "cluster": "GlobalInternalSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [ | 
|  | // CHECK-NEXT:      { "kind": "Default", "offset": 0, "value": "conj_$ | 
|  | // CHECK-NEXT:    ]}, | 
|  | // CHECK-NEXT:    { "cluster": "GlobalSystemSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [ | 
|  | // CHECK-NEXT:      { "kind": "Default", "offset": 0, "value": "conj_$ | 
|  | // CHECK-NEXT:    ]}, | 
|  | // CHECK-NEXT:    { "cluster": "Empty", "pointer": "0x{{[0-9a-f]+}}", "items": [ | 
|  | // CHECK-NEXT:      { "kind": "Default", "offset": 0, "value": "[[EMPTY_CONJ:conj_\$[0-9]+{int, LC[0-9]+, S[0-9]+, #[0-9]+}]]" } | 
|  | // CHECK-NEXT:    ]}, | 
|  | // CHECK-NEXT:    { "cluster": "Empty2", "pointer": "0x{{[0-9a-f]+}}", "items": [ | 
|  | // CHECK-NEXT:      { "kind": "Default", "offset": 0, "value": "[[EMPTY_CONJ]]" } | 
|  | // CHECK-NEXT:    ]}, | 
|  | // CHECK-NEXT:    { "cluster": "Empty3", "pointer": "0x{{[0-9a-f]+}}", "items": [ | 
|  | // CHECK-NEXT:      { "kind": "Default", "offset": 0, "value": "[[EMPTY_CONJ]]" } | 
|  | // CHECK-NEXT:    ]} | 
|  | // CHECK-NEXT:  ]}, | 
|  |  | 
|  | nop(Empty, Empty2, Empty3); | 
|  | } | 
|  |  | 
|  | void _02_structs_with_members() { | 
|  | clang_analyzer_dump_val(conjure<aggr>()); // expected-warning {{conj_$}} | 
|  | aggr Aggr = conjure<aggr>(); | 
|  | aggr Aggr2 = Aggr; | 
|  | aggr Aggr3 = Aggr2; | 
|  | // All of these should refer to the exact same symbol, because all of | 
|  | // these trivial copies refer to the original conjured value. | 
|  | clang_analyzer_denote(Aggr, "$Aggr"); | 
|  | clang_analyzer_express(Aggr);  // expected-warning {{$Aggr}} | 
|  | clang_analyzer_express(Aggr2); // expected-warning {{$Aggr}} | 
|  | clang_analyzer_express(Aggr3); // expected-warning {{$Aggr}} | 
|  |  | 
|  | // We should have the same Conjured symbol for "Aggr", "Aggr2" and "Aggr3". | 
|  | // We used to have Derived symbols for the individual fields that were | 
|  | // copied as part of copying the whole struct. | 
|  | clang_analyzer_printState(); | 
|  | // CHECK:       "store": { "pointer": "0x{{[0-9a-f]+}}", "items": [ | 
|  | // CHECK-NEXT:    { "cluster": "GlobalInternalSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [ | 
|  | // CHECK-NEXT:      { "kind": "Default", "offset": 0, "value": "conj_$ | 
|  | // CHECK-NEXT:    ]}, | 
|  | // CHECK-NEXT:    { "cluster": "GlobalSystemSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [ | 
|  | // CHECK-NEXT:      { "kind": "Default", "offset": 0, "value": "conj_$ | 
|  | // CHECK-NEXT:    ]}, | 
|  | // CHECK-NEXT:    { "cluster": "Aggr", "pointer": "0x{{[0-9a-f]+}}", "items": [ | 
|  | // CHECK-NEXT:      { "kind": "Default", "offset": 0, "value": "[[AGGR_CONJ:conj_\$[0-9]+{int, LC[0-9]+, S[0-9]+, #[0-9]+}]]" } | 
|  | // CHECK-NEXT:    ]}, | 
|  | // CHECK-NEXT:    { "cluster": "Aggr2", "pointer": "0x{{[0-9a-f]+}}", "items": [ | 
|  | // CHECK-NEXT:      { "kind": "Default", "offset": 0, "value": "[[AGGR_CONJ]]" } | 
|  | // CHECK-NEXT:    ]}, | 
|  | // CHECK-NEXT:    { "cluster": "Aggr3", "pointer": "0x{{[0-9a-f]+}}", "items": [ | 
|  | // CHECK-NEXT:      { "kind": "Default", "offset": 0, "value": "[[AGGR_CONJ]]" } | 
|  | // CHECK-NEXT:    ]} | 
|  | // CHECK-NEXT:  ]}, | 
|  |  | 
|  | nop(Aggr, Aggr2, Aggr3); | 
|  | } | 
|  |  | 
|  | // Tests that use `clang_analyzer_printState()` must share the analysis entry | 
|  | // point, and have a strict ordering between. This is to meet the different | 
|  | // `clang_analyzer_printState()` calls in a fixed relative ordering, thus | 
|  | // FileCheck could check the stdouts. | 
|  | void entrypoint() { | 
|  | _01_empty_structs(); | 
|  | _02_structs_with_members(); | 
|  | } | 
|  |  | 
|  | } // namespace trivial_struct_copy |