xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/RecordOpsTest.cpp (revision 71f2ec2db1295462d61e1407fcc1e715ba5d458b)
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 
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 
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 
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         auto *S1Val = cast<RecordValue>(Env.getValue(S1));
89         auto *S2Val = cast<RecordValue>(Env.getValue(S2));
90         EXPECT_NE(S1Val, S2Val);
91 
92         S1Val->setProperty("prop", Env.getBoolLiteralValue(true));
93 
94         copyRecord(S1, S2, Env);
95 
96         EXPECT_EQ(getFieldValue(&S1, *OuterIntDecl, Env),
97                   getFieldValue(&S2, *OuterIntDecl, Env));
98         EXPECT_EQ(S1.getChild(*RefDecl), S2.getChild(*RefDecl));
99         EXPECT_EQ(getFieldValue(&Inner1, *InnerIntDecl, Env),
100                   getFieldValue(&Inner2, *InnerIntDecl, Env));
101         EXPECT_EQ(Env.getValue(S1.getSyntheticField("synth_int")),
102                   Env.getValue(S2.getSyntheticField("synth_int")));
103 
104         S1Val = cast<RecordValue>(Env.getValue(S1));
105         S2Val = cast<RecordValue>(Env.getValue(S2));
106         EXPECT_NE(S1Val, S2Val);
107 
108         EXPECT_EQ(S2Val->getProperty("prop"), &Env.getBoolLiteralValue(true));
109       });
110 }
111 
112 TEST(RecordOpsTest, RecordsEqual) {
113   std::string Code = R"(
114     struct S {
115       int outer_int;
116       int &ref;
117       struct {
118         int inner_int;
119       } inner;
120     };
121     void target(S s1, S s2) {
122       (void)s1.outer_int;
123       (void)s1.ref;
124       (void)s1.inner.inner_int;
125       // [[p]]
126     }
127   )";
128   runDataflow(
129       Code,
130       [](QualType Ty) -> llvm::StringMap<QualType> {
131         if (Ty.getAsString() != "S")
132           return {};
133         QualType IntTy =
134             getFieldNamed(Ty->getAsRecordDecl(), "outer_int")->getType();
135         return {{"synth_int", IntTy}};
136       },
137       [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
138          ASTContext &ASTCtx) {
139         Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
140 
141         const ValueDecl *OuterIntDecl = findValueDecl(ASTCtx, "outer_int");
142         const ValueDecl *RefDecl = findValueDecl(ASTCtx, "ref");
143         const ValueDecl *InnerDecl = findValueDecl(ASTCtx, "inner");
144         const ValueDecl *InnerIntDecl = findValueDecl(ASTCtx, "inner_int");
145 
146         auto &S1 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s1");
147         auto &S2 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s2");
148         auto &Inner2 = *cast<RecordStorageLocation>(S2.getChild(*InnerDecl));
149 
150         Env.setValue(S1.getSyntheticField("synth_int"),
151                      Env.create<IntegerValue>());
152 
153         cast<RecordValue>(Env.getValue(S1))
154             ->setProperty("prop", Env.getBoolLiteralValue(true));
155 
156         // Strategy: Create two equal records, then verify each of the various
157         // ways in which records can differ causes recordsEqual to return false.
158         // changes we can make to the record.
159 
160         // This test reuses the same objects for multiple checks, which isn't
161         // great, but seems better than duplicating the setup code for every
162         // check.
163 
164         copyRecord(S1, S2, Env);
165         EXPECT_TRUE(recordsEqual(S1, S2, Env));
166 
167         // S2 has a different outer_int.
168         Env.setValue(*S2.getChild(*OuterIntDecl), Env.create<IntegerValue>());
169         EXPECT_FALSE(recordsEqual(S1, S2, Env));
170         copyRecord(S1, S2, Env);
171         EXPECT_TRUE(recordsEqual(S1, S2, Env));
172 
173         // S2 doesn't have outer_int at all.
174         Env.clearValue(*S2.getChild(*OuterIntDecl));
175         EXPECT_FALSE(recordsEqual(S1, S2, Env));
176         copyRecord(S1, S2, Env);
177         EXPECT_TRUE(recordsEqual(S1, S2, Env));
178 
179         // S2 has a different ref.
180         S2.setChild(*RefDecl, &Env.createStorageLocation(
181                                   RefDecl->getType().getNonReferenceType()));
182         EXPECT_FALSE(recordsEqual(S1, S2, Env));
183         copyRecord(S1, S2, Env);
184         EXPECT_TRUE(recordsEqual(S1, S2, Env));
185 
186         // S2 as a different inner_int.
187         Env.setValue(*Inner2.getChild(*InnerIntDecl),
188                      Env.create<IntegerValue>());
189         EXPECT_FALSE(recordsEqual(S1, S2, Env));
190         copyRecord(S1, S2, Env);
191         EXPECT_TRUE(recordsEqual(S1, S2, Env));
192 
193         // S2 has a different synth_int.
194         Env.setValue(S2.getSyntheticField("synth_int"),
195                      Env.create<IntegerValue>());
196         EXPECT_FALSE(recordsEqual(S1, S2, Env));
197         copyRecord(S1, S2, Env);
198         EXPECT_TRUE(recordsEqual(S1, S2, Env));
199 
200         // S2 doesn't have a value for synth_int.
201         Env.clearValue(S2.getSyntheticField("synth_int"));
202         EXPECT_FALSE(recordsEqual(S1, S2, Env));
203         copyRecord(S1, S2, Env);
204         EXPECT_TRUE(recordsEqual(S1, S2, Env));
205 
206         // S1 and S2 have the same property with different values.
207         cast<RecordValue>(Env.getValue(S2))
208             ->setProperty("prop", Env.getBoolLiteralValue(false));
209         EXPECT_FALSE(recordsEqual(S1, S2, Env));
210         copyRecord(S1, S2, Env);
211         EXPECT_TRUE(recordsEqual(S1, S2, Env));
212 
213         // S1 has a property that S2 doesn't have.
214         cast<RecordValue>(Env.getValue(S1))
215             ->setProperty("other_prop", Env.getBoolLiteralValue(false));
216         EXPECT_FALSE(recordsEqual(S1, S2, Env));
217         // We modified S1 this time, so need to copy back the other way.
218         copyRecord(S2, S1, Env);
219         EXPECT_TRUE(recordsEqual(S1, S2, Env));
220 
221         // S2 has a property that S1 doesn't have.
222         cast<RecordValue>(Env.getValue(S2))
223             ->setProperty("other_prop", Env.getBoolLiteralValue(false));
224         EXPECT_FALSE(recordsEqual(S1, S2, Env));
225         copyRecord(S1, S2, Env);
226         EXPECT_TRUE(recordsEqual(S1, S2, Env));
227 
228         // S1 and S2 have the same number of properties, but with different
229         // names.
230         cast<RecordValue>(Env.getValue(S1))
231             ->setProperty("prop1", Env.getBoolLiteralValue(false));
232         cast<RecordValue>(Env.getValue(S2))
233             ->setProperty("prop2", Env.getBoolLiteralValue(false));
234         EXPECT_FALSE(recordsEqual(S1, S2, Env));
235       });
236 }
237 
238 TEST(TransferTest, CopyRecordFromDerivedToBase) {
239   std::string Code = R"(
240     struct A {
241       int i;
242     };
243 
244     struct B : public A {
245     };
246 
247     void target(A a, B b) {
248       (void)a.i;
249       // [[p]]
250     }
251   )";
252   runDataflow(
253       Code, /*SyntheticFieldCallback=*/{},
254       [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
255          ASTContext &ASTCtx) {
256         Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
257 
258         const ValueDecl *IDecl = findValueDecl(ASTCtx, "i");
259         auto &A = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "a");
260         auto &B = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "b");
261 
262         EXPECT_NE(Env.getValue(*A.getChild(*IDecl)),
263                   Env.getValue(*B.getChild(*IDecl)));
264 
265         copyRecord(B, A, Env);
266 
267         EXPECT_EQ(Env.getValue(*A.getChild(*IDecl)),
268                   Env.getValue(*B.getChild(*IDecl)));
269       });
270 }
271 
272 } // namespace
273 } // namespace test
274 } // namespace dataflow
275 } // namespace clang
276