1 //===- unittests/Analysis/FlowSensitive/RecordOpsTest.cpp -----------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang/Analysis/FlowSensitive/RecordOps.h"
10 #include "TestingSupport.h"
11 #include "llvm/Testing/Support/Error.h"
12 #include "gtest/gtest.h"
21 std::function
<llvm::StringMap
<QualType
>(QualType
)> SyntheticFieldCallback
,
23 void(const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &,
26 ASSERT_THAT_ERROR(checkDataflowWithNoopAnalysis(
27 Code
, ast_matchers::hasName("target"), VerifyResults
,
28 {BuiltinOptions()}, LangStandard::lang_cxx17
,
29 SyntheticFieldCallback
),
33 const FieldDecl
*getFieldNamed(RecordDecl
*RD
, llvm::StringRef Name
) {
34 for (const FieldDecl
*FD
: RD
->fields())
35 if (FD
->getName() == Name
)
41 TEST(RecordOpsTest
, CopyRecord
) {
42 std::string Code
= R
"(
50 void target(S s1, S s2) {
53 (void)s1.inner.inner_int;
59 [](QualType Ty
) -> llvm::StringMap
<QualType
> {
60 if (Ty
.getAsString() != "S")
63 getFieldNamed(Ty
->getAsRecordDecl(), "outer_int")->getType();
64 return {{"synth_int", IntTy
}};
66 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
68 Environment Env
= getEnvironmentAtAnnotation(Results
, "p").fork();
70 const ValueDecl
*OuterIntDecl
= findValueDecl(ASTCtx
, "outer_int");
71 const ValueDecl
*RefDecl
= findValueDecl(ASTCtx
, "ref");
72 const ValueDecl
*InnerDecl
= findValueDecl(ASTCtx
, "inner");
73 const ValueDecl
*InnerIntDecl
= findValueDecl(ASTCtx
, "inner_int");
75 auto &S1
= getLocForDecl
<RecordStorageLocation
>(ASTCtx
, Env
, "s1");
76 auto &S2
= getLocForDecl
<RecordStorageLocation
>(ASTCtx
, Env
, "s2");
77 auto &Inner1
= *cast
<RecordStorageLocation
>(S1
.getChild(*InnerDecl
));
78 auto &Inner2
= *cast
<RecordStorageLocation
>(S2
.getChild(*InnerDecl
));
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")));
88 copyRecord(S1
, S2
, Env
);
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")));
100 TEST(RecordOpsTest
, RecordsEqual
) {
101 std::string Code
= R
"(
109 void target(S s1, S s2) {
112 (void)s1.inner.inner_int;
118 [](QualType Ty
) -> llvm::StringMap
<QualType
> {
119 if (Ty
.getAsString() != "S")
122 getFieldNamed(Ty
->getAsRecordDecl(), "outer_int")->getType();
123 return {{"synth_int", IntTy
}};
125 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
126 ASTContext
&ASTCtx
) {
127 Environment Env
= getEnvironmentAtAnnotation(Results
, "p").fork();
129 const ValueDecl
*OuterIntDecl
= findValueDecl(ASTCtx
, "outer_int");
130 const ValueDecl
*RefDecl
= findValueDecl(ASTCtx
, "ref");
131 const ValueDecl
*InnerDecl
= findValueDecl(ASTCtx
, "inner");
132 const ValueDecl
*InnerIntDecl
= findValueDecl(ASTCtx
, "inner_int");
134 auto &S1
= getLocForDecl
<RecordStorageLocation
>(ASTCtx
, Env
, "s1");
135 auto &S2
= getLocForDecl
<RecordStorageLocation
>(ASTCtx
, Env
, "s2");
136 auto &Inner2
= *cast
<RecordStorageLocation
>(S2
.getChild(*InnerDecl
));
138 Env
.setValue(S1
.getSyntheticField("synth_int"),
139 Env
.create
<IntegerValue
>());
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.
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
149 copyRecord(S1
, S2
, Env
);
150 EXPECT_TRUE(recordsEqual(S1
, S2
, Env
));
152 // S2 has a different outer_int.
153 Env
.setValue(*S2
.getChild(*OuterIntDecl
), Env
.create
<IntegerValue
>());
154 EXPECT_FALSE(recordsEqual(S1
, S2
, Env
));
155 copyRecord(S1
, S2
, Env
);
156 EXPECT_TRUE(recordsEqual(S1
, S2
, Env
));
158 // S2 doesn't have outer_int at all.
159 Env
.clearValue(*S2
.getChild(*OuterIntDecl
));
160 EXPECT_FALSE(recordsEqual(S1
, S2
, Env
));
161 copyRecord(S1
, S2
, Env
);
162 EXPECT_TRUE(recordsEqual(S1
, S2
, Env
));
164 // S2 has a different ref.
165 S2
.setChild(*RefDecl
, &Env
.createStorageLocation(
166 RefDecl
->getType().getNonReferenceType()));
167 EXPECT_FALSE(recordsEqual(S1
, S2
, Env
));
168 copyRecord(S1
, S2
, Env
);
169 EXPECT_TRUE(recordsEqual(S1
, S2
, Env
));
171 // S2 as a different inner_int.
172 Env
.setValue(*Inner2
.getChild(*InnerIntDecl
),
173 Env
.create
<IntegerValue
>());
174 EXPECT_FALSE(recordsEqual(S1
, S2
, Env
));
175 copyRecord(S1
, S2
, Env
);
176 EXPECT_TRUE(recordsEqual(S1
, S2
, Env
));
178 // S2 has a different synth_int.
179 Env
.setValue(S2
.getSyntheticField("synth_int"),
180 Env
.create
<IntegerValue
>());
181 EXPECT_FALSE(recordsEqual(S1
, S2
, Env
));
182 copyRecord(S1
, S2
, Env
);
183 EXPECT_TRUE(recordsEqual(S1
, S2
, Env
));
185 // S2 doesn't have a value for synth_int.
186 Env
.clearValue(S2
.getSyntheticField("synth_int"));
187 EXPECT_FALSE(recordsEqual(S1
, S2
, Env
));
188 copyRecord(S1
, S2
, Env
);
189 EXPECT_TRUE(recordsEqual(S1
, S2
, Env
));
193 TEST(TransferTest
, CopyRecordBetweenDerivedAndBase
) {
194 std::string Code
= R
"(
199 struct B : public A {
202 void target(A a, B b) {
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()
215 ->getAsCXXRecordDecl();
218 QualType IntTy
= getFieldNamed(ADecl
, "i")->getType();
219 return {{"synth_int", IntTy
}};
221 // Test copying derived to base class.
223 Code
, SyntheticFieldCallback
,
224 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
225 ASTContext
&ASTCtx
) {
226 Environment Env
= getEnvironmentAtAnnotation(Results
, "p").fork();
228 const ValueDecl
*IDecl
= findValueDecl(ASTCtx
, "i");
229 auto &A
= getLocForDecl
<RecordStorageLocation
>(ASTCtx
, Env
, "a");
230 auto &B
= getLocForDecl
<RecordStorageLocation
>(ASTCtx
, Env
, "b");
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")));
237 copyRecord(B
, A
, Env
);
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")));
244 // Test copying base to derived class.
246 Code
, SyntheticFieldCallback
,
247 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
248 ASTContext
&ASTCtx
) {
249 Environment Env
= getEnvironmentAtAnnotation(Results
, "p").fork();
251 const ValueDecl
*IDecl
= findValueDecl(ASTCtx
, "i");
252 auto &A
= getLocForDecl
<RecordStorageLocation
>(ASTCtx
, Env
, "a");
253 auto &B
= getLocForDecl
<RecordStorageLocation
>(ASTCtx
, Env
, "b");
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")));
260 copyRecord(A
, B
, Env
);
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")));
271 } // namespace dataflow