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
2
3
3
4
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) {}
6
10
7
11
struct aggr {
8
12
int x;
@@ -15,20 +19,103 @@ struct empty {
15
19
void test_copy_return () {
16
20
aggr s1 = {1 , 2 };
17
21
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]+}}} }}
19
23
20
24
empty s2;
21
25
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]+}}} }}
23
27
}
24
28
25
29
void test_assign_return () {
26
30
aggr s1 = {1 , 2 };
27
31
aggr d1;
28
- clang_analyzer_dump (d1 = s1); // expected-warning {{&d1 }}
32
+ clang_analyzer_dump_lref (d1 = s1); // expected-warning {{&d1 }}
29
33
30
34
empty s2;
31
35
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
33
37
}
34
38
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