xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/RecordOpsTest.cpp (revision e8fce95887ecfd87a83db8dbb0cc55966b004d6f)
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 
14 namespace clang {
15 namespace dataflow {
16 namespace test {
17 namespace {
18 
runDataflow(llvm::StringRef Code,std::function<llvm::StringMap<QualType> (QualType)> SyntheticFieldCallback,std::function<void (const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,ASTContext &)> VerifyResults)19 void 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 
getFieldNamed(RecordDecl * RD,llvm::StringRef Name)33 const 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 
TEST(RecordOpsTest,CopyRecord)41 TEST(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       [](QualType Ty) -> llvm::StringMap<QualType> {
60         if (Ty.getAsString() != "S")
61           return {};
62         QualType IntTy =
63             getFieldNamed(Ty->getAsRecordDecl(), "outer_int")->getType();
64         return {{"synth_int", IntTy}};
65       },
66       [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
67          ASTContext &ASTCtx) {
68         Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
69 
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");
74 
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));
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(S1, 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 
TEST(RecordOpsTest,RecordsEqual)100 TEST(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       [](QualType Ty) -> llvm::StringMap<QualType> {
119         if (Ty.getAsString() != "S")
120           return {};
121         QualType IntTy =
122             getFieldNamed(Ty->getAsRecordDecl(), "outer_int")->getType();
123         return {{"synth_int", IntTy}};
124       },
125       [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
126          ASTContext &ASTCtx) {
127         Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
128 
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");
133 
134         auto &S1 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s1");
135         auto &S2 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s2");
136         auto &Inner2 = *cast<RecordStorageLocation>(S2.getChild(*InnerDecl));
137 
138         Env.setValue(S1.getSyntheticField("synth_int"),
139                      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(S1, S2, Env);
150         EXPECT_TRUE(recordsEqual(S1, S2, Env));
151 
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));
157 
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));
163 
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));
170 
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));
177 
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));
184 
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));
190       });
191 }
192 
TEST(TransferTest,CopyRecordBetweenDerivedAndBase)193 TEST(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       [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
225          ASTContext &ASTCtx) {
226         Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
227 
228         const ValueDecl *IDecl = findValueDecl(ASTCtx, "i");
229         auto &A = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "a");
230         auto &B = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "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(B, 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       [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
248          ASTContext &ASTCtx) {
249         Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
250 
251         const ValueDecl *IDecl = findValueDecl(ASTCtx, "i");
252         auto &A = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "a");
253         auto &B = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "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(A, 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