xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/RecordOpsTest.cpp (revision 9ecdbe3855a8048989a507ff8d470aee4d407589)
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<
22         void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
23              ASTContext &)>
24         VerifyResults,
25     LangStandard::Kind Std = LangStandard::lang_cxx17,
26     llvm::StringRef TargetFun = "target") {
27   ASSERT_THAT_ERROR(
28       checkDataflowWithNoopAnalysis(Code, VerifyResults,
29                                     DataflowAnalysisOptions{BuiltinOptions{}},
30                                     Std, TargetFun),
31       llvm::Succeeded());
32 }
33 
34 TEST(RecordOpsTest, CopyRecord) {
35   std::string Code = R"(
36     struct S {
37       int outer_int;
38       int &ref;
39       struct {
40         int inner_int;
41       } inner;
42     };
43     void target(S s1, S s2) {
44       (void)s1.outer_int;
45       (void)s1.ref;
46       (void)s1.inner.inner_int;
47       // [[p]]
48     }
49   )";
50   runDataflow(
51       Code,
52       [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
53          ASTContext &ASTCtx) {
54         Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
55 
56         const ValueDecl *OuterIntDecl = findValueDecl(ASTCtx, "outer_int");
57         const ValueDecl *RefDecl = findValueDecl(ASTCtx, "ref");
58         const ValueDecl *InnerDecl = findValueDecl(ASTCtx, "inner");
59         const ValueDecl *InnerIntDecl = findValueDecl(ASTCtx, "inner_int");
60 
61         auto &S1 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s1");
62         auto &S2 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s2");
63         auto &Inner1 = *cast<RecordStorageLocation>(S1.getChild(*InnerDecl));
64         auto &Inner2 = *cast<RecordStorageLocation>(S2.getChild(*InnerDecl));
65 
66         EXPECT_NE(getFieldValue(&S1, *OuterIntDecl, Env),
67                   getFieldValue(&S2, *OuterIntDecl, Env));
68         EXPECT_NE(S1.getChild(*RefDecl), S2.getChild(*RefDecl));
69         EXPECT_NE(getFieldValue(&Inner1, *InnerIntDecl, Env),
70                   getFieldValue(&Inner2, *InnerIntDecl, Env));
71 
72         auto *S1Val = cast<RecordValue>(Env.getValue(S1));
73         auto *S2Val = cast<RecordValue>(Env.getValue(S2));
74         EXPECT_NE(S1Val, S2Val);
75 
76         S1Val->setProperty("prop", Env.getBoolLiteralValue(true));
77 
78         copyRecord(S1, S2, Env);
79 
80         EXPECT_EQ(getFieldValue(&S1, *OuterIntDecl, Env),
81                   getFieldValue(&S2, *OuterIntDecl, Env));
82         EXPECT_EQ(S1.getChild(*RefDecl), S2.getChild(*RefDecl));
83         EXPECT_EQ(getFieldValue(&Inner1, *InnerIntDecl, Env),
84                   getFieldValue(&Inner2, *InnerIntDecl, Env));
85 
86         S1Val = cast<RecordValue>(Env.getValue(S1));
87         S2Val = cast<RecordValue>(Env.getValue(S2));
88         EXPECT_NE(S1Val, S2Val);
89 
90         EXPECT_EQ(S2Val->getProperty("prop"), &Env.getBoolLiteralValue(true));
91       });
92 }
93 
94 TEST(RecordOpsTest, RecordsEqual) {
95   std::string Code = R"(
96     struct S {
97       int outer_int;
98       int &ref;
99       struct {
100         int inner_int;
101       } inner;
102     };
103     void target(S s1, S s2) {
104       (void)s1.outer_int;
105       (void)s1.ref;
106       (void)s1.inner.inner_int;
107       // [[p]]
108     }
109   )";
110   runDataflow(
111       Code,
112       [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
113          ASTContext &ASTCtx) {
114         Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
115 
116         const ValueDecl *OuterIntDecl = findValueDecl(ASTCtx, "outer_int");
117         const ValueDecl *RefDecl = findValueDecl(ASTCtx, "ref");
118         const ValueDecl *InnerDecl = findValueDecl(ASTCtx, "inner");
119         const ValueDecl *InnerIntDecl = findValueDecl(ASTCtx, "inner_int");
120 
121         auto &S1 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s1");
122         auto &S2 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s2");
123         auto &Inner2 = *cast<RecordStorageLocation>(S2.getChild(*InnerDecl));
124 
125         cast<RecordValue>(Env.getValue(S1))
126             ->setProperty("prop", Env.getBoolLiteralValue(true));
127 
128         // Strategy: Create two equal records, then verify each of the various
129         // ways in which records can differ causes recordsEqual to return false.
130         // changes we can make to the record.
131 
132         // This test reuses the same objects for multiple checks, which isn't
133         // great, but seems better than duplicating the setup code for every
134         // check.
135 
136         copyRecord(S1, S2, Env);
137         EXPECT_TRUE(recordsEqual(S1, S2, Env));
138 
139         // S2 has a different outer_int.
140         Env.setValue(*S2.getChild(*OuterIntDecl), Env.create<IntegerValue>());
141         EXPECT_FALSE(recordsEqual(S1, S2, Env));
142         copyRecord(S1, S2, Env);
143         EXPECT_TRUE(recordsEqual(S1, S2, Env));
144 
145         // S2 doesn't have outer_int at all.
146         Env.clearValue(*S2.getChild(*OuterIntDecl));
147         EXPECT_FALSE(recordsEqual(S1, S2, Env));
148         copyRecord(S1, S2, Env);
149         EXPECT_TRUE(recordsEqual(S1, S2, Env));
150 
151         // S2 has a different ref.
152         S2.setChild(*RefDecl, &Env.createStorageLocation(
153                                   RefDecl->getType().getNonReferenceType()));
154         EXPECT_FALSE(recordsEqual(S1, S2, Env));
155         copyRecord(S1, S2, Env);
156         EXPECT_TRUE(recordsEqual(S1, S2, Env));
157 
158         // S2 as a different inner_int.
159         Env.setValue(*Inner2.getChild(*InnerIntDecl),
160                      Env.create<IntegerValue>());
161         EXPECT_FALSE(recordsEqual(S1, S2, Env));
162         copyRecord(S1, S2, Env);
163         EXPECT_TRUE(recordsEqual(S1, S2, Env));
164 
165         // S1 and S2 have the same property with different values.
166         cast<RecordValue>(Env.getValue(S2))
167             ->setProperty("prop", Env.getBoolLiteralValue(false));
168         EXPECT_FALSE(recordsEqual(S1, S2, Env));
169         copyRecord(S1, S2, Env);
170         EXPECT_TRUE(recordsEqual(S1, S2, Env));
171 
172         // S1 has a property that S2 doesn't have.
173         cast<RecordValue>(Env.getValue(S1))
174             ->setProperty("other_prop", Env.getBoolLiteralValue(false));
175         EXPECT_FALSE(recordsEqual(S1, S2, Env));
176         // We modified S1 this time, so need to copy back the other way.
177         copyRecord(S2, S1, Env);
178         EXPECT_TRUE(recordsEqual(S1, S2, Env));
179 
180         // S2 has a property that S1 doesn't have.
181         cast<RecordValue>(Env.getValue(S2))
182             ->setProperty("other_prop", Env.getBoolLiteralValue(false));
183         EXPECT_FALSE(recordsEqual(S1, S2, Env));
184         copyRecord(S1, S2, Env);
185         EXPECT_TRUE(recordsEqual(S1, S2, Env));
186 
187         // S1 and S2 have the same number of properties, but with different
188         // names.
189         cast<RecordValue>(Env.getValue(S1))
190             ->setProperty("prop1", Env.getBoolLiteralValue(false));
191         cast<RecordValue>(Env.getValue(S2))
192             ->setProperty("prop2", Env.getBoolLiteralValue(false));
193         EXPECT_FALSE(recordsEqual(S1, S2, Env));
194       });
195 }
196 
197 TEST(TransferTest, CopyRecordFromDerivedToBase) {
198   std::string Code = R"(
199     struct A {
200       int i;
201     };
202 
203     struct B : public A {
204     };
205 
206     void target(A a, B b) {
207       (void)a.i;
208       // [[p]]
209     }
210   )";
211   runDataflow(
212       Code,
213       [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
214          ASTContext &ASTCtx) {
215         Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
216 
217         const ValueDecl *IDecl = findValueDecl(ASTCtx, "i");
218         auto &A = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "a");
219         auto &B = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "b");
220 
221         EXPECT_NE(Env.getValue(*A.getChild(*IDecl)),
222                   Env.getValue(*B.getChild(*IDecl)));
223 
224         copyRecord(B, A, Env);
225 
226         EXPECT_EQ(Env.getValue(*A.getChild(*IDecl)),
227                   Env.getValue(*B.getChild(*IDecl)));
228       });
229 }
230 
231 } // namespace
232 } // namespace test
233 } // namespace dataflow
234 } // namespace clang
235