1f2123af1SMartin Braenne //===- unittests/Analysis/FlowSensitive/RecordOpsTest.cpp -----------------===//
2f2123af1SMartin Braenne //
3f2123af1SMartin Braenne // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4f2123af1SMartin Braenne // See https://llvm.org/LICENSE.txt for license information.
5f2123af1SMartin Braenne // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f2123af1SMartin Braenne //
7f2123af1SMartin Braenne //===----------------------------------------------------------------------===//
8f2123af1SMartin Braenne
9f2123af1SMartin Braenne #include "clang/Analysis/FlowSensitive/RecordOps.h"
10f2123af1SMartin Braenne #include "TestingSupport.h"
11f2123af1SMartin Braenne #include "llvm/Testing/Support/Error.h"
12f2123af1SMartin Braenne #include "gtest/gtest.h"
13f2123af1SMartin Braenne
14f2123af1SMartin Braenne namespace clang {
15f2123af1SMartin Braenne namespace dataflow {
16f2123af1SMartin Braenne namespace test {
17f2123af1SMartin Braenne namespace {
18f2123af1SMartin Braenne
runDataflow(llvm::StringRef Code,std::function<llvm::StringMap<QualType> (QualType)> SyntheticFieldCallback,std::function<void (const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,ASTContext &)> VerifyResults)19d0be47c5SMartin Braenne void runDataflow(
20d0be47c5SMartin Braenne llvm::StringRef Code,
2171f2ec2dSmartinboehme std::function<llvm::StringMap<QualType>(QualType)> SyntheticFieldCallback,
22d0be47c5SMartin Braenne std::function<
23d0be47c5SMartin Braenne void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
24d0be47c5SMartin Braenne ASTContext &)>
2571f2ec2dSmartinboehme VerifyResults) {
2671f2ec2dSmartinboehme ASSERT_THAT_ERROR(checkDataflowWithNoopAnalysis(
2771f2ec2dSmartinboehme Code, ast_matchers::hasName("target"), VerifyResults,
2871f2ec2dSmartinboehme {BuiltinOptions()}, LangStandard::lang_cxx17,
2971f2ec2dSmartinboehme SyntheticFieldCallback),
30f2123af1SMartin Braenne llvm::Succeeded());
31f2123af1SMartin Braenne }
32f2123af1SMartin Braenne
getFieldNamed(RecordDecl * RD,llvm::StringRef Name)3371f2ec2dSmartinboehme const FieldDecl *getFieldNamed(RecordDecl *RD, llvm::StringRef Name) {
3471f2ec2dSmartinboehme for (const FieldDecl *FD : RD->fields())
3571f2ec2dSmartinboehme if (FD->getName() == Name)
3671f2ec2dSmartinboehme return FD;
3771f2ec2dSmartinboehme assert(false);
3871f2ec2dSmartinboehme return nullptr;
3971f2ec2dSmartinboehme }
4071f2ec2dSmartinboehme
TEST(RecordOpsTest,CopyRecord)41f2123af1SMartin Braenne TEST(RecordOpsTest, CopyRecord) {
42f2123af1SMartin Braenne std::string Code = R"(
43f2123af1SMartin Braenne struct S {
44f2123af1SMartin Braenne int outer_int;
45f2123af1SMartin Braenne int &ref;
46f2123af1SMartin Braenne struct {
47f2123af1SMartin Braenne int inner_int;
48f2123af1SMartin Braenne } inner;
49f2123af1SMartin Braenne };
50f2123af1SMartin Braenne void target(S s1, S s2) {
51f2123af1SMartin Braenne (void)s1.outer_int;
52f2123af1SMartin Braenne (void)s1.ref;
53f2123af1SMartin Braenne (void)s1.inner.inner_int;
54f2123af1SMartin Braenne // [[p]]
55f2123af1SMartin Braenne }
56f2123af1SMartin Braenne )";
57f2123af1SMartin Braenne runDataflow(
58f2123af1SMartin Braenne Code,
5971f2ec2dSmartinboehme [](QualType Ty) -> llvm::StringMap<QualType> {
6071f2ec2dSmartinboehme if (Ty.getAsString() != "S")
6171f2ec2dSmartinboehme return {};
6271f2ec2dSmartinboehme QualType IntTy =
6371f2ec2dSmartinboehme getFieldNamed(Ty->getAsRecordDecl(), "outer_int")->getType();
6471f2ec2dSmartinboehme return {{"synth_int", IntTy}};
6571f2ec2dSmartinboehme },
66f2123af1SMartin Braenne [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
67f2123af1SMartin Braenne ASTContext &ASTCtx) {
681adc5a78SSam McCall Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
69f2123af1SMartin Braenne
70f2123af1SMartin Braenne const ValueDecl *OuterIntDecl = findValueDecl(ASTCtx, "outer_int");
71f2123af1SMartin Braenne const ValueDecl *RefDecl = findValueDecl(ASTCtx, "ref");
72f2123af1SMartin Braenne const ValueDecl *InnerDecl = findValueDecl(ASTCtx, "inner");
73f2123af1SMartin Braenne const ValueDecl *InnerIntDecl = findValueDecl(ASTCtx, "inner_int");
74f2123af1SMartin Braenne
759ecdbe38SMartin Braenne auto &S1 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s1");
769ecdbe38SMartin Braenne auto &S2 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s2");
779ecdbe38SMartin Braenne auto &Inner1 = *cast<RecordStorageLocation>(S1.getChild(*InnerDecl));
789ecdbe38SMartin Braenne auto &Inner2 = *cast<RecordStorageLocation>(S2.getChild(*InnerDecl));
79f2123af1SMartin Braenne
802902ea3dSMartin Braenne EXPECT_NE(getFieldValue(&S1, *OuterIntDecl, Env),
812902ea3dSMartin Braenne getFieldValue(&S2, *OuterIntDecl, Env));
8244f98d01SMartin Braenne EXPECT_NE(S1.getChild(*RefDecl), S2.getChild(*RefDecl));
832902ea3dSMartin Braenne EXPECT_NE(getFieldValue(&Inner1, *InnerIntDecl, Env),
842902ea3dSMartin Braenne getFieldValue(&Inner2, *InnerIntDecl, Env));
8571f2ec2dSmartinboehme EXPECT_NE(Env.getValue(S1.getSyntheticField("synth_int")),
8671f2ec2dSmartinboehme Env.getValue(S2.getSyntheticField("synth_int")));
87f2123af1SMartin Braenne
88f2123af1SMartin Braenne copyRecord(S1, S2, Env);
89f2123af1SMartin Braenne
902902ea3dSMartin Braenne EXPECT_EQ(getFieldValue(&S1, *OuterIntDecl, Env),
912902ea3dSMartin Braenne getFieldValue(&S2, *OuterIntDecl, Env));
9244f98d01SMartin Braenne EXPECT_EQ(S1.getChild(*RefDecl), S2.getChild(*RefDecl));
932902ea3dSMartin Braenne EXPECT_EQ(getFieldValue(&Inner1, *InnerIntDecl, Env),
942902ea3dSMartin Braenne getFieldValue(&Inner2, *InnerIntDecl, Env));
9571f2ec2dSmartinboehme EXPECT_EQ(Env.getValue(S1.getSyntheticField("synth_int")),
9671f2ec2dSmartinboehme Env.getValue(S2.getSyntheticField("synth_int")));
97f2123af1SMartin Braenne });
98f2123af1SMartin Braenne }
99f2123af1SMartin Braenne
TEST(RecordOpsTest,RecordsEqual)100f2123af1SMartin Braenne TEST(RecordOpsTest, RecordsEqual) {
101f2123af1SMartin Braenne std::string Code = R"(
102f2123af1SMartin Braenne struct S {
103f2123af1SMartin Braenne int outer_int;
104f2123af1SMartin Braenne int &ref;
105f2123af1SMartin Braenne struct {
106f2123af1SMartin Braenne int inner_int;
107f2123af1SMartin Braenne } inner;
108f2123af1SMartin Braenne };
109f2123af1SMartin Braenne void target(S s1, S s2) {
110f2123af1SMartin Braenne (void)s1.outer_int;
111f2123af1SMartin Braenne (void)s1.ref;
112f2123af1SMartin Braenne (void)s1.inner.inner_int;
113f2123af1SMartin Braenne // [[p]]
114f2123af1SMartin Braenne }
115f2123af1SMartin Braenne )";
116f2123af1SMartin Braenne runDataflow(
117f2123af1SMartin Braenne Code,
11871f2ec2dSmartinboehme [](QualType Ty) -> llvm::StringMap<QualType> {
11971f2ec2dSmartinboehme if (Ty.getAsString() != "S")
12071f2ec2dSmartinboehme return {};
12171f2ec2dSmartinboehme QualType IntTy =
12271f2ec2dSmartinboehme getFieldNamed(Ty->getAsRecordDecl(), "outer_int")->getType();
12371f2ec2dSmartinboehme return {{"synth_int", IntTy}};
12471f2ec2dSmartinboehme },
125f2123af1SMartin Braenne [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
126f2123af1SMartin Braenne ASTContext &ASTCtx) {
1271adc5a78SSam McCall Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
128f2123af1SMartin Braenne
129f2123af1SMartin Braenne const ValueDecl *OuterIntDecl = findValueDecl(ASTCtx, "outer_int");
130f2123af1SMartin Braenne const ValueDecl *RefDecl = findValueDecl(ASTCtx, "ref");
131f2123af1SMartin Braenne const ValueDecl *InnerDecl = findValueDecl(ASTCtx, "inner");
132f2123af1SMartin Braenne const ValueDecl *InnerIntDecl = findValueDecl(ASTCtx, "inner_int");
133f2123af1SMartin Braenne
1349ecdbe38SMartin Braenne auto &S1 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s1");
1359ecdbe38SMartin Braenne auto &S2 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s2");
1369ecdbe38SMartin Braenne auto &Inner2 = *cast<RecordStorageLocation>(S2.getChild(*InnerDecl));
137f2123af1SMartin Braenne
13871f2ec2dSmartinboehme Env.setValue(S1.getSyntheticField("synth_int"),
13971f2ec2dSmartinboehme Env.create<IntegerValue>());
14071f2ec2dSmartinboehme
141f2123af1SMartin Braenne // Strategy: Create two equal records, then verify each of the various
142f2123af1SMartin Braenne // ways in which records can differ causes recordsEqual to return false.
143f2123af1SMartin Braenne // changes we can make to the record.
144f2123af1SMartin Braenne
145f2123af1SMartin Braenne // This test reuses the same objects for multiple checks, which isn't
146f2123af1SMartin Braenne // great, but seems better than duplicating the setup code for every
147f2123af1SMartin Braenne // check.
148f2123af1SMartin Braenne
149f2123af1SMartin Braenne copyRecord(S1, S2, Env);
150f2123af1SMartin Braenne EXPECT_TRUE(recordsEqual(S1, S2, Env));
151f2123af1SMartin Braenne
152f2123af1SMartin Braenne // S2 has a different outer_int.
15344f98d01SMartin Braenne Env.setValue(*S2.getChild(*OuterIntDecl), Env.create<IntegerValue>());
154f2123af1SMartin Braenne EXPECT_FALSE(recordsEqual(S1, S2, Env));
155f2123af1SMartin Braenne copyRecord(S1, S2, Env);
156f2123af1SMartin Braenne EXPECT_TRUE(recordsEqual(S1, S2, Env));
157f2123af1SMartin Braenne
158f2123af1SMartin Braenne // S2 doesn't have outer_int at all.
15944f98d01SMartin Braenne Env.clearValue(*S2.getChild(*OuterIntDecl));
160f2123af1SMartin Braenne EXPECT_FALSE(recordsEqual(S1, S2, Env));
161f2123af1SMartin Braenne copyRecord(S1, S2, Env);
162f2123af1SMartin Braenne EXPECT_TRUE(recordsEqual(S1, S2, Env));
163f2123af1SMartin Braenne
164f2123af1SMartin Braenne // S2 has a different ref.
16544f98d01SMartin Braenne S2.setChild(*RefDecl, &Env.createStorageLocation(
16644f98d01SMartin Braenne RefDecl->getType().getNonReferenceType()));
167f2123af1SMartin Braenne EXPECT_FALSE(recordsEqual(S1, S2, Env));
168f2123af1SMartin Braenne copyRecord(S1, S2, Env);
169f2123af1SMartin Braenne EXPECT_TRUE(recordsEqual(S1, S2, Env));
170f2123af1SMartin Braenne
171f2123af1SMartin Braenne // S2 as a different inner_int.
17244f98d01SMartin Braenne Env.setValue(*Inner2.getChild(*InnerIntDecl),
173f2123af1SMartin Braenne Env.create<IntegerValue>());
174f2123af1SMartin Braenne EXPECT_FALSE(recordsEqual(S1, S2, Env));
175f2123af1SMartin Braenne copyRecord(S1, S2, Env);
176f2123af1SMartin Braenne EXPECT_TRUE(recordsEqual(S1, S2, Env));
177f2123af1SMartin Braenne
17871f2ec2dSmartinboehme // S2 has a different synth_int.
17971f2ec2dSmartinboehme Env.setValue(S2.getSyntheticField("synth_int"),
18071f2ec2dSmartinboehme Env.create<IntegerValue>());
18171f2ec2dSmartinboehme EXPECT_FALSE(recordsEqual(S1, S2, Env));
18271f2ec2dSmartinboehme copyRecord(S1, S2, Env);
18371f2ec2dSmartinboehme EXPECT_TRUE(recordsEqual(S1, S2, Env));
18471f2ec2dSmartinboehme
18571f2ec2dSmartinboehme // S2 doesn't have a value for synth_int.
18671f2ec2dSmartinboehme Env.clearValue(S2.getSyntheticField("synth_int"));
18771f2ec2dSmartinboehme EXPECT_FALSE(recordsEqual(S1, S2, Env));
18871f2ec2dSmartinboehme copyRecord(S1, S2, Env);
18971f2ec2dSmartinboehme EXPECT_TRUE(recordsEqual(S1, S2, Env));
190f2123af1SMartin Braenne });
191f2123af1SMartin Braenne }
192f2123af1SMartin Braenne
TEST(TransferTest,CopyRecordBetweenDerivedAndBase)193*b788e465Smartinboehme TEST(TransferTest, CopyRecordBetweenDerivedAndBase) {
194145f353fSPaul Semel std::string Code = R"(
195145f353fSPaul Semel struct A {
196145f353fSPaul Semel int i;
197145f353fSPaul Semel };
198145f353fSPaul Semel
199145f353fSPaul Semel struct B : public A {
200145f353fSPaul Semel };
201145f353fSPaul Semel
202145f353fSPaul Semel void target(A a, B b) {
203145f353fSPaul Semel (void)a.i;
204145f353fSPaul Semel // [[p]]
205145f353fSPaul Semel }
206145f353fSPaul Semel )";
207*b788e465Smartinboehme auto SyntheticFieldCallback = [](QualType Ty) -> llvm::StringMap<QualType> {
208*b788e465Smartinboehme CXXRecordDecl *ADecl = nullptr;
209*b788e465Smartinboehme if (Ty.getAsString() == "A")
210*b788e465Smartinboehme ADecl = Ty->getAsCXXRecordDecl();
211*b788e465Smartinboehme else if (Ty.getAsString() == "B")
212*b788e465Smartinboehme ADecl = Ty->getAsCXXRecordDecl()
213*b788e465Smartinboehme ->bases_begin()
214*b788e465Smartinboehme ->getType()
215*b788e465Smartinboehme ->getAsCXXRecordDecl();
216*b788e465Smartinboehme else
217*b788e465Smartinboehme return {};
218*b788e465Smartinboehme QualType IntTy = getFieldNamed(ADecl, "i")->getType();
219*b788e465Smartinboehme return {{"synth_int", IntTy}};
220*b788e465Smartinboehme };
221*b788e465Smartinboehme // Test copying derived to base class.
222145f353fSPaul Semel runDataflow(
223*b788e465Smartinboehme Code, SyntheticFieldCallback,
224145f353fSPaul Semel [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
225145f353fSPaul Semel ASTContext &ASTCtx) {
226145f353fSPaul Semel Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
227145f353fSPaul Semel
228145f353fSPaul Semel const ValueDecl *IDecl = findValueDecl(ASTCtx, "i");
2299ecdbe38SMartin Braenne auto &A = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "a");
2309ecdbe38SMartin Braenne auto &B = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "b");
231145f353fSPaul Semel
232145f353fSPaul Semel EXPECT_NE(Env.getValue(*A.getChild(*IDecl)),
233145f353fSPaul Semel Env.getValue(*B.getChild(*IDecl)));
234*b788e465Smartinboehme EXPECT_NE(Env.getValue(A.getSyntheticField("synth_int")),
235*b788e465Smartinboehme Env.getValue(B.getSyntheticField("synth_int")));
236145f353fSPaul Semel
237145f353fSPaul Semel copyRecord(B, A, Env);
238145f353fSPaul Semel
239145f353fSPaul Semel EXPECT_EQ(Env.getValue(*A.getChild(*IDecl)),
240145f353fSPaul Semel Env.getValue(*B.getChild(*IDecl)));
241*b788e465Smartinboehme EXPECT_EQ(Env.getValue(A.getSyntheticField("synth_int")),
242*b788e465Smartinboehme Env.getValue(B.getSyntheticField("synth_int")));
243*b788e465Smartinboehme });
244*b788e465Smartinboehme // Test copying base to derived class.
245*b788e465Smartinboehme runDataflow(
246*b788e465Smartinboehme Code, SyntheticFieldCallback,
247*b788e465Smartinboehme [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
248*b788e465Smartinboehme ASTContext &ASTCtx) {
249*b788e465Smartinboehme Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
250*b788e465Smartinboehme
251*b788e465Smartinboehme const ValueDecl *IDecl = findValueDecl(ASTCtx, "i");
252*b788e465Smartinboehme auto &A = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "a");
253*b788e465Smartinboehme auto &B = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "b");
254*b788e465Smartinboehme
255*b788e465Smartinboehme EXPECT_NE(Env.getValue(*A.getChild(*IDecl)),
256*b788e465Smartinboehme Env.getValue(*B.getChild(*IDecl)));
257*b788e465Smartinboehme EXPECT_NE(Env.getValue(A.getSyntheticField("synth_int")),
258*b788e465Smartinboehme Env.getValue(B.getSyntheticField("synth_int")));
259*b788e465Smartinboehme
260*b788e465Smartinboehme copyRecord(A, B, Env);
261*b788e465Smartinboehme
262*b788e465Smartinboehme EXPECT_EQ(Env.getValue(*A.getChild(*IDecl)),
263*b788e465Smartinboehme Env.getValue(*B.getChild(*IDecl)));
264*b788e465Smartinboehme EXPECT_EQ(Env.getValue(A.getSyntheticField("synth_int")),
265*b788e465Smartinboehme Env.getValue(B.getSyntheticField("synth_int")));
266145f353fSPaul Semel });
267145f353fSPaul Semel }
268145f353fSPaul Semel
269f2123af1SMartin Braenne } // namespace
270f2123af1SMartin Braenne } // namespace test
271f2123af1SMartin Braenne } // namespace dataflow
272f2123af1SMartin Braenne } // namespace clang
273