1//===- unittests/Analysis/FlowSensitive/RecordOpsTest.cpp -----------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/Analysis/FlowSensitive/RecordOps.h"
10#include "TestingSupport.h"
11#include "llvm/Testing/Support/Error.h"
12#include "gtest/gtest.h"
13
14namespace clang {
15namespace dataflow {
16namespace test {
17namespace {
18
19void runDataflow(
20 llvm::StringRef Code,
21 std::function<llvm::StringMap<QualType>(QualType)> SyntheticFieldCallback,
22 std::function<
23 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
24 ASTContext &)>
25 VerifyResults) {
26 ASSERT_THAT_ERROR(checkDataflowWithNoopAnalysis(
27 Code, ast_matchers::hasName("target"), VerifyResults,
28 {BuiltinOptions()}, LangStandard::lang_cxx17,
29 SyntheticFieldCallback),
30 llvm::Succeeded());
31}
32
33const FieldDecl *getFieldNamed(RecordDecl *RD, llvm::StringRef Name) {
34 for (const FieldDecl *FD : RD->fields())
35 if (FD->getName() == Name)
36 return FD;
37 assert(false);
38 return nullptr;
39}
40
41TEST(RecordOpsTest, CopyRecord) {
42 std::string Code = R"(
43 struct S {
44 int outer_int;
45 int &ref;
46 struct {
47 int inner_int;
48 } inner;
49 };
50 void target(S s1, S s2) {
51 (void)s1.outer_int;
52 (void)s1.ref;
53 (void)s1.inner.inner_int;
54 // [[p]]
55 }
56 )";
57 runDataflow(
58 Code,
59 SyntheticFieldCallback: [](QualType Ty) -> llvm::StringMap<QualType> {
60 if (Ty.getAsString() != "S")
61 return {};
62 QualType IntTy =
63 getFieldNamed(RD: Ty->getAsRecordDecl(), Name: "outer_int")->getType();
64 return {{"synth_int", IntTy}};
65 },
66 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
67 ASTContext &ASTCtx) {
68 Environment Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p").fork();
69
70 const ValueDecl *OuterIntDecl = findValueDecl(ASTCtx, Name: "outer_int");
71 const ValueDecl *RefDecl = findValueDecl(ASTCtx, Name: "ref");
72 const ValueDecl *InnerDecl = findValueDecl(ASTCtx, Name: "inner");
73 const ValueDecl *InnerIntDecl = findValueDecl(ASTCtx, Name: "inner_int");
74
75 auto &S1 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "s1");
76 auto &S2 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "s2");
77 auto &Inner1 = *cast<RecordStorageLocation>(Val: S1.getChild(D: *InnerDecl));
78 auto &Inner2 = *cast<RecordStorageLocation>(Val: S2.getChild(D: *InnerDecl));
79
80 EXPECT_NE(getFieldValue(&S1, *OuterIntDecl, Env),
81 getFieldValue(&S2, *OuterIntDecl, Env));
82 EXPECT_NE(S1.getChild(*RefDecl), S2.getChild(*RefDecl));
83 EXPECT_NE(getFieldValue(&Inner1, *InnerIntDecl, Env),
84 getFieldValue(&Inner2, *InnerIntDecl, Env));
85 EXPECT_NE(Env.getValue(S1.getSyntheticField("synth_int")),
86 Env.getValue(S2.getSyntheticField("synth_int")));
87
88 copyRecord(Src&: S1, Dst&: S2, Env);
89
90 EXPECT_EQ(getFieldValue(&S1, *OuterIntDecl, Env),
91 getFieldValue(&S2, *OuterIntDecl, Env));
92 EXPECT_EQ(S1.getChild(*RefDecl), S2.getChild(*RefDecl));
93 EXPECT_EQ(getFieldValue(&Inner1, *InnerIntDecl, Env),
94 getFieldValue(&Inner2, *InnerIntDecl, Env));
95 EXPECT_EQ(Env.getValue(S1.getSyntheticField("synth_int")),
96 Env.getValue(S2.getSyntheticField("synth_int")));
97 });
98}
99
100TEST(RecordOpsTest, RecordsEqual) {
101 std::string Code = R"(
102 struct S {
103 int outer_int;
104 int &ref;
105 struct {
106 int inner_int;
107 } inner;
108 };
109 void target(S s1, S s2) {
110 (void)s1.outer_int;
111 (void)s1.ref;
112 (void)s1.inner.inner_int;
113 // [[p]]
114 }
115 )";
116 runDataflow(
117 Code,
118 SyntheticFieldCallback: [](QualType Ty) -> llvm::StringMap<QualType> {
119 if (Ty.getAsString() != "S")
120 return {};
121 QualType IntTy =
122 getFieldNamed(RD: Ty->getAsRecordDecl(), Name: "outer_int")->getType();
123 return {{"synth_int", IntTy}};
124 },
125 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
126 ASTContext &ASTCtx) {
127 Environment Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p").fork();
128
129 const ValueDecl *OuterIntDecl = findValueDecl(ASTCtx, Name: "outer_int");
130 const ValueDecl *RefDecl = findValueDecl(ASTCtx, Name: "ref");
131 const ValueDecl *InnerDecl = findValueDecl(ASTCtx, Name: "inner");
132 const ValueDecl *InnerIntDecl = findValueDecl(ASTCtx, Name: "inner_int");
133
134 auto &S1 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "s1");
135 auto &S2 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "s2");
136 auto &Inner2 = *cast<RecordStorageLocation>(Val: S2.getChild(D: *InnerDecl));
137
138 Env.setValue(Loc: S1.getSyntheticField(Name: "synth_int"),
139 Val&: Env.create<IntegerValue>());
140
141 // Strategy: Create two equal records, then verify each of the various
142 // ways in which records can differ causes recordsEqual to return false.
143 // changes we can make to the record.
144
145 // This test reuses the same objects for multiple checks, which isn't
146 // great, but seems better than duplicating the setup code for every
147 // check.
148
149 copyRecord(Src&: S1, Dst&: S2, Env);
150 EXPECT_TRUE(recordsEqual(S1, S2, Env));
151
152 // S2 has a different outer_int.
153 Env.setValue(Loc: *S2.getChild(D: *OuterIntDecl), Val&: Env.create<IntegerValue>());
154 EXPECT_FALSE(recordsEqual(S1, S2, Env));
155 copyRecord(Src&: S1, Dst&: S2, Env);
156 EXPECT_TRUE(recordsEqual(S1, S2, Env));
157
158 // S2 doesn't have outer_int at all.
159 Env.clearValue(Loc: *S2.getChild(D: *OuterIntDecl));
160 EXPECT_FALSE(recordsEqual(S1, S2, Env));
161 copyRecord(Src&: S1, Dst&: S2, Env);
162 EXPECT_TRUE(recordsEqual(S1, S2, Env));
163
164 // S2 has a different ref.
165 S2.setChild(D: *RefDecl, Loc: &Env.createStorageLocation(
166 Type: RefDecl->getType().getNonReferenceType()));
167 EXPECT_FALSE(recordsEqual(S1, S2, Env));
168 copyRecord(Src&: S1, Dst&: S2, Env);
169 EXPECT_TRUE(recordsEqual(S1, S2, Env));
170
171 // S2 as a different inner_int.
172 Env.setValue(Loc: *Inner2.getChild(D: *InnerIntDecl),
173 Val&: Env.create<IntegerValue>());
174 EXPECT_FALSE(recordsEqual(S1, S2, Env));
175 copyRecord(Src&: S1, Dst&: S2, Env);
176 EXPECT_TRUE(recordsEqual(S1, S2, Env));
177
178 // S2 has a different synth_int.
179 Env.setValue(Loc: S2.getSyntheticField(Name: "synth_int"),
180 Val&: Env.create<IntegerValue>());
181 EXPECT_FALSE(recordsEqual(S1, S2, Env));
182 copyRecord(Src&: S1, Dst&: S2, Env);
183 EXPECT_TRUE(recordsEqual(S1, S2, Env));
184
185 // S2 doesn't have a value for synth_int.
186 Env.clearValue(Loc: S2.getSyntheticField(Name: "synth_int"));
187 EXPECT_FALSE(recordsEqual(S1, S2, Env));
188 copyRecord(Src&: S1, Dst&: S2, Env);
189 EXPECT_TRUE(recordsEqual(S1, S2, Env));
190 });
191}
192
193TEST(TransferTest, CopyRecordBetweenDerivedAndBase) {
194 std::string Code = R"(
195 struct A {
196 int i;
197 };
198
199 struct B : public A {
200 };
201
202 void target(A a, B b) {
203 (void)a.i;
204 // [[p]]
205 }
206 )";
207 auto SyntheticFieldCallback = [](QualType Ty) -> llvm::StringMap<QualType> {
208 CXXRecordDecl *ADecl = nullptr;
209 if (Ty.getAsString() == "A")
210 ADecl = Ty->getAsCXXRecordDecl();
211 else if (Ty.getAsString() == "B")
212 ADecl = Ty->getAsCXXRecordDecl()
213 ->bases_begin()
214 ->getType()
215 ->getAsCXXRecordDecl();
216 else
217 return {};
218 QualType IntTy = getFieldNamed(ADecl, "i")->getType();
219 return {{"synth_int", IntTy}};
220 };
221 // Test copying derived to base class.
222 runDataflow(
223 Code, SyntheticFieldCallback,
224 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
225 ASTContext &ASTCtx) {
226 Environment Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p").fork();
227
228 const ValueDecl *IDecl = findValueDecl(ASTCtx, Name: "i");
229 auto &A = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "a");
230 auto &B = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "b");
231
232 EXPECT_NE(Env.getValue(*A.getChild(*IDecl)),
233 Env.getValue(*B.getChild(*IDecl)));
234 EXPECT_NE(Env.getValue(A.getSyntheticField("synth_int")),
235 Env.getValue(B.getSyntheticField("synth_int")));
236
237 copyRecord(Src&: B, Dst&: A, Env);
238
239 EXPECT_EQ(Env.getValue(*A.getChild(*IDecl)),
240 Env.getValue(*B.getChild(*IDecl)));
241 EXPECT_EQ(Env.getValue(A.getSyntheticField("synth_int")),
242 Env.getValue(B.getSyntheticField("synth_int")));
243 });
244 // Test copying base to derived class.
245 runDataflow(
246 Code, SyntheticFieldCallback,
247 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
248 ASTContext &ASTCtx) {
249 Environment Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p").fork();
250
251 const ValueDecl *IDecl = findValueDecl(ASTCtx, Name: "i");
252 auto &A = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "a");
253 auto &B = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "b");
254
255 EXPECT_NE(Env.getValue(*A.getChild(*IDecl)),
256 Env.getValue(*B.getChild(*IDecl)));
257 EXPECT_NE(Env.getValue(A.getSyntheticField("synth_int")),
258 Env.getValue(B.getSyntheticField("synth_int")));
259
260 copyRecord(Src&: A, Dst&: B, Env);
261
262 EXPECT_EQ(Env.getValue(*A.getChild(*IDecl)),
263 Env.getValue(*B.getChild(*IDecl)));
264 EXPECT_EQ(Env.getValue(A.getSyntheticField("synth_int")),
265 Env.getValue(B.getSyntheticField("synth_int")));
266 });
267}
268
269} // namespace
270} // namespace test
271} // namespace dataflow
272} // namespace clang
273

source code of clang/unittests/Analysis/FlowSensitive/RecordOpsTest.cpp