1- // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-inlining=constructors -verify %s
1+ // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-inlining=constructors -verify %s \
2+ // RUN: 2>&1 | FileCheck %s
23
34
4- template <typename T>
5- void clang_analyzer_dump (T&);
5+ void clang_analyzer_printState ();
6+ template <typename T> void clang_analyzer_dump_lref (T&);
7+ template <typename T> void clang_analyzer_dump_val (T);
8+ template <typename T> T conjure ();
9+ template <typename ... Ts> void nop (const Ts &... args) {}
610
711struct aggr {
812 int x;
@@ -15,20 +19,103 @@ struct empty {
1519void test_copy_return () {
1620 aggr s1 = {1 , 2 };
1721 aggr const & cr1 = aggr (s1);
18- clang_analyzer_dump (cr1); // expected-warning-re {{&lifetime_extended_object{aggr, cr1, S{{[0-9]+}}} }}
22+ clang_analyzer_dump_lref (cr1); // expected-warning-re {{&lifetime_extended_object{aggr, cr1, S{{[0-9]+}}} }}
1923
2024 empty s2;
2125 empty const & cr2 = empty{s2};
22- clang_analyzer_dump (cr2); // expected-warning-re {{&lifetime_extended_object{empty, cr2, S{{[0-9]+}}} }}
26+ clang_analyzer_dump_lref (cr2); // expected-warning-re {{&lifetime_extended_object{empty, cr2, S{{[0-9]+}}} }}
2327}
2428
2529void test_assign_return () {
2630 aggr s1 = {1 , 2 };
2731 aggr d1;
28- clang_analyzer_dump (d1 = s1); // expected-warning {{&d1 }}
32+ clang_analyzer_dump_lref (d1 = s1); // expected-warning {{&d1 }}
2933
3034 empty s2;
3135 empty d2;
32- clang_analyzer_dump (d2 = s2); // expected-warning {{&d2 }} was Unknown
36+ clang_analyzer_dump_lref (d2 = s2); // expected-warning {{&d2 }} was Unknown
3337}
3438
39+
40+ namespace trivial_struct_copy {
41+
42+ void _01_empty_structs () {
43+ clang_analyzer_dump_val (conjure<empty>()); // expected-warning {{lazyCompoundVal}}
44+ empty Empty = conjure<empty>();
45+ empty Empty2 = Empty;
46+ empty Empty3 = Empty2;
47+ // All of these should refer to the exact same LCV, because all of
48+ // these trivial copies refer to the original conjured value.
49+ // There were Unknown before:
50+ clang_analyzer_dump_val (Empty); // expected-warning {{lazyCompoundVal}}
51+ clang_analyzer_dump_val (Empty2); // expected-warning {{lazyCompoundVal}}
52+ clang_analyzer_dump_val (Empty3); // expected-warning {{lazyCompoundVal}}
53+
54+ // Notice that we don't have entries for the copies, only for the original "Empty" object.
55+ // That binding was added by the opaque "conjure" call, directly constructing to the variable "Empty" by copy-elision.
56+ // The copies are not present because the ExprEngine skips the evalBind of empty structs.
57+ // And even if such binds would reach the Store, the Store copies small structs by copying the individual fields,
58+ // and there are no fields within "Empty", thus we wouldn't have any entries anyways.
59+ clang_analyzer_printState ();
60+ // CHECK: "store": { "pointer": "0x{{[0-9a-f]+}}", "items": [
61+ // CHECK-NEXT: { "cluster": "GlobalInternalSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [
62+ // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "conj_$
63+ // CHECK-NEXT: ]},
64+ // CHECK-NEXT: { "cluster": "GlobalSystemSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [
65+ // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "conj_$
66+ // CHECK-NEXT: ]},
67+ // CHECK-NEXT: { "cluster": "Empty", "pointer": "0x{{[0-9a-f]+}}", "items": [
68+ // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "[[EMPTY_CONJ:conj_\$[0-9]+{int, LC[0-9]+, S[0-9]+, #[0-9]+}]]" }
69+ // CHECK-NEXT: ]}
70+ // CHECK-NEXT: ]},
71+
72+ nop (Empty, Empty2, Empty3);
73+ }
74+
75+ void _02_structs_with_members () {
76+ clang_analyzer_dump_val (conjure<aggr>()); // expected-warning {{lazyCompoundVal}}
77+ aggr Aggr = conjure<aggr>();
78+ aggr Aggr2 = Aggr;
79+ aggr Aggr3 = Aggr2;
80+ // All of these should refer to the exact same LCV, because all of
81+ // these trivial copies refer to the original conjured value.
82+ clang_analyzer_dump_val (Aggr); // expected-warning {{lazyCompoundVal}}
83+ clang_analyzer_dump_val (Aggr2); // expected-warning {{lazyCompoundVal}}
84+ clang_analyzer_dump_val (Aggr3); // expected-warning {{lazyCompoundVal}}
85+
86+ // We have fields in the struct we copy, thus we also have the entries for the copies
87+ // (and for all of their fields).
88+ clang_analyzer_printState ();
89+ // CHECK: "store": { "pointer": "0x{{[0-9a-f]+}}", "items": [
90+ // CHECK-NEXT: { "cluster": "GlobalInternalSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [
91+ // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "conj_$
92+ // CHECK-NEXT: ]},
93+ // CHECK-NEXT: { "cluster": "GlobalSystemSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [
94+ // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "conj_$
95+ // CHECK-NEXT: ]},
96+ // CHECK-NEXT: { "cluster": "Aggr", "pointer": "0x{{[0-9a-f]+}}", "items": [
97+ // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "[[AGGR_CONJ:conj_\$[0-9]+{int, LC[0-9]+, S[0-9]+, #[0-9]+}]]" }
98+ // CHECK-NEXT: ]},
99+ // CHECK-NEXT: { "cluster": "Aggr2", "pointer": "0x{{[0-9a-f]+}}", "items": [
100+ // CHECK-NEXT: { "kind": "Direct", "offset": 0, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.x}" },
101+ // CHECK-NEXT: { "kind": "Direct", "offset": 32, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.y}" }
102+ // CHECK-NEXT: ]},
103+ // CHECK-NEXT: { "cluster": "Aggr3", "pointer": "0x{{[0-9a-f]+}}", "items": [
104+ // CHECK-NEXT: { "kind": "Direct", "offset": 0, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.x}" },
105+ // CHECK-NEXT: { "kind": "Direct", "offset": 32, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.y}" }
106+ // CHECK-NEXT: ]}
107+ // CHECK-NEXT: ]},
108+
109+ nop (Aggr, Aggr2, Aggr3);
110+ }
111+
112+ // Tests that use `clang_analyzer_printState()` must share the analysis entry
113+ // point, and have a strict ordering between. This is to meet the different
114+ // `clang_analyzer_printState()` calls in a fixed relative ordering, thus
115+ // FileCheck could check the stdouts.
116+ void entrypoint () {
117+ _01_empty_structs ();
118+ _02_structs_with_members ();
119+ }
120+
121+ } // namespace trivial_struct_copy
0 commit comments