xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/RecordOpsTest.cpp (revision e8fce95887ecfd87a83db8dbb0cc55966b004d6f)
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