[ELF] Replace inExpr with lexState. NFC
[llvm-project.git] / clang / unittests / Analysis / FlowSensitive / CachedConstAccessorsLatticeTest.cpp
blobd27f6a6d27e7106a3d6e837cdd70e98389a4557d
1 //===- unittests/Analysis/FlowSensitive/CachedConstAccessorsLatticeTest.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 //===----------------------------------------------------------------------===//
9 #include "clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h"
11 #include <cassert>
12 #include <memory>
14 #include "clang/AST/Decl.h"
15 #include "clang/AST/DeclBase.h"
16 #include "clang/AST/DeclCXX.h"
17 #include "clang/AST/Expr.h"
18 #include "clang/AST/Type.h"
19 #include "clang/ASTMatchers/ASTMatchFinder.h"
20 #include "clang/ASTMatchers/ASTMatchers.h"
21 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
22 #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
23 #include "clang/Analysis/FlowSensitive/NoopLattice.h"
24 #include "clang/Analysis/FlowSensitive/StorageLocation.h"
25 #include "clang/Analysis/FlowSensitive/Value.h"
26 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
27 #include "clang/Basic/LLVM.h"
28 #include "clang/Testing/TestAST.h"
29 #include "gmock/gmock.h"
30 #include "gtest/gtest.h"
32 namespace clang::dataflow {
33 namespace {
35 using ast_matchers::BoundNodes;
36 using ast_matchers::callee;
37 using ast_matchers::cxxMemberCallExpr;
38 using ast_matchers::functionDecl;
39 using ast_matchers::hasName;
40 using ast_matchers::match;
41 using ast_matchers::selectFirst;
43 using dataflow::DataflowAnalysisContext;
44 using dataflow::Environment;
45 using dataflow::LatticeJoinEffect;
46 using dataflow::RecordStorageLocation;
47 using dataflow::Value;
48 using dataflow::WatchedLiteralsSolver;
50 using testing::SizeIs;
52 NamedDecl *lookup(StringRef Name, const DeclContext &DC) {
53 auto Result = DC.lookup(&DC.getParentASTContext().Idents.get(Name));
54 EXPECT_TRUE(Result.isSingleResult()) << Name;
55 return Result.front();
58 class CachedConstAccessorsLatticeTest : public ::testing::Test {
59 protected:
60 using LatticeT = CachedConstAccessorsLattice<NoopLattice>;
62 DataflowAnalysisContext DACtx{std::make_unique<WatchedLiteralsSolver>()};
63 Environment Env{DACtx};
66 // Basic test AST with two const methods (return a value, and return a ref).
67 struct CommonTestInputs {
68 CommonTestInputs()
69 : AST(R"cpp(
70 struct S {
71 int *valProperty() const;
72 int &refProperty() const;
74 void target() {
75 S s;
76 s.valProperty();
77 S s2;
78 s2.refProperty();
80 )cpp") {
81 auto *SDecl = cast<CXXRecordDecl>(
82 lookup("S", *AST.context().getTranslationUnitDecl()));
83 SType = AST.context().getRecordType(SDecl);
84 CallVal = selectFirst<CallExpr>(
85 "call",
86 match(cxxMemberCallExpr(callee(functionDecl(hasName("valProperty"))))
87 .bind("call"),
88 AST.context()));
89 assert(CallVal != nullptr);
91 CallRef = selectFirst<CallExpr>(
92 "call",
93 match(cxxMemberCallExpr(callee(functionDecl(hasName("refProperty"))))
94 .bind("call"),
95 AST.context()));
96 assert(CallRef != nullptr);
99 TestAST AST;
100 QualType SType;
101 const CallExpr *CallVal;
102 const CallExpr *CallRef;
105 TEST_F(CachedConstAccessorsLatticeTest,
106 SamePrimitiveValBeforeClearOrDiffAfterClear) {
107 CommonTestInputs Inputs;
108 auto *CE = Inputs.CallVal;
109 RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(),
110 {});
112 LatticeT Lattice;
113 Value *Val1 = Lattice.getOrCreateConstMethodReturnValue(Loc, CE, Env);
114 Value *Val2 = Lattice.getOrCreateConstMethodReturnValue(Loc, CE, Env);
116 EXPECT_EQ(Val1, Val2);
118 Lattice.clearConstMethodReturnValues(Loc);
119 Value *Val3 = Lattice.getOrCreateConstMethodReturnValue(Loc, CE, Env);
121 EXPECT_NE(Val3, Val1);
122 EXPECT_NE(Val3, Val2);
125 TEST_F(CachedConstAccessorsLatticeTest, SameLocBeforeClearOrDiffAfterClear) {
126 CommonTestInputs Inputs;
127 auto *CE = Inputs.CallRef;
128 RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(),
129 {});
131 LatticeT Lattice;
132 auto NopInit = [](StorageLocation &) {};
133 StorageLocation *Loc1 = Lattice.getOrCreateConstMethodReturnStorageLocation(
134 Loc, CE, Env, NopInit);
135 auto NotCalled = [](StorageLocation &) {
136 ASSERT_TRUE(false) << "Not reached";
138 StorageLocation *Loc2 = Lattice.getOrCreateConstMethodReturnStorageLocation(
139 Loc, CE, Env, NotCalled);
141 EXPECT_EQ(Loc1, Loc2);
143 Lattice.clearConstMethodReturnStorageLocations(Loc);
144 StorageLocation *Loc3 = Lattice.getOrCreateConstMethodReturnStorageLocation(
145 Loc, CE, Env, NopInit);
147 EXPECT_NE(Loc3, Loc1);
148 EXPECT_NE(Loc3, Loc2);
151 TEST_F(CachedConstAccessorsLatticeTest,
152 SameLocBeforeClearOrDiffAfterClearWithCallee) {
153 CommonTestInputs Inputs;
154 auto *CE = Inputs.CallRef;
155 RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(),
156 {});
158 LatticeT Lattice;
159 auto NopInit = [](StorageLocation &) {};
160 const FunctionDecl *Callee = CE->getDirectCallee();
161 ASSERT_NE(Callee, nullptr);
162 StorageLocation &Loc1 = Lattice.getOrCreateConstMethodReturnStorageLocation(
163 Loc, Callee, Env, NopInit);
164 auto NotCalled = [](StorageLocation &) {
165 ASSERT_TRUE(false) << "Not reached";
167 StorageLocation &Loc2 = Lattice.getOrCreateConstMethodReturnStorageLocation(
168 Loc, Callee, Env, NotCalled);
170 EXPECT_EQ(&Loc1, &Loc2);
172 Lattice.clearConstMethodReturnStorageLocations(Loc);
173 StorageLocation &Loc3 = Lattice.getOrCreateConstMethodReturnStorageLocation(
174 Loc, Callee, Env, NopInit);
176 EXPECT_NE(&Loc3, &Loc1);
177 EXPECT_NE(&Loc3, &Loc2);
180 TEST_F(CachedConstAccessorsLatticeTest,
181 SameStructValBeforeClearOrDiffAfterClear) {
182 TestAST AST(R"cpp(
183 struct S {
184 S structValProperty() const;
186 void target() {
187 S s;
188 s.structValProperty();
190 )cpp");
191 auto *SDecl =
192 cast<CXXRecordDecl>(lookup("S", *AST.context().getTranslationUnitDecl()));
193 QualType SType = AST.context().getRecordType(SDecl);
194 const CallExpr *CE = selectFirst<CallExpr>(
195 "call", match(cxxMemberCallExpr(
196 callee(functionDecl(hasName("structValProperty"))))
197 .bind("call"),
198 AST.context()));
199 ASSERT_NE(CE, nullptr);
201 RecordStorageLocation Loc(SType, RecordStorageLocation::FieldToLoc(), {});
203 LatticeT Lattice;
204 // Accessors that return a record by value are modeled by a record storage
205 // location (instead of a Value).
206 auto NopInit = [](StorageLocation &) {};
207 StorageLocation *Loc1 = Lattice.getOrCreateConstMethodReturnStorageLocation(
208 Loc, CE, Env, NopInit);
209 auto NotCalled = [](StorageLocation &) {
210 ASSERT_TRUE(false) << "Not reached";
212 StorageLocation *Loc2 = Lattice.getOrCreateConstMethodReturnStorageLocation(
213 Loc, CE, Env, NotCalled);
215 EXPECT_EQ(Loc1, Loc2);
217 Lattice.clearConstMethodReturnStorageLocations(Loc);
218 StorageLocation *Loc3 = Lattice.getOrCreateConstMethodReturnStorageLocation(
219 Loc, CE, Env, NopInit);
221 EXPECT_NE(Loc3, Loc1);
222 EXPECT_NE(Loc3, Loc1);
225 TEST_F(CachedConstAccessorsLatticeTest, ClearDifferentLocs) {
226 CommonTestInputs Inputs;
227 auto *CE = Inputs.CallRef;
228 RecordStorageLocation LocS1(Inputs.SType, RecordStorageLocation::FieldToLoc(),
229 {});
230 RecordStorageLocation LocS2(Inputs.SType, RecordStorageLocation::FieldToLoc(),
231 {});
233 LatticeT Lattice;
234 auto NopInit = [](StorageLocation &) {};
235 StorageLocation *RetLoc1 =
236 Lattice.getOrCreateConstMethodReturnStorageLocation(LocS1, CE, Env,
237 NopInit);
238 Lattice.clearConstMethodReturnStorageLocations(LocS2);
239 auto NotCalled = [](StorageLocation &) {
240 ASSERT_TRUE(false) << "Not reached";
242 StorageLocation *RetLoc2 =
243 Lattice.getOrCreateConstMethodReturnStorageLocation(LocS1, CE, Env,
244 NotCalled);
246 EXPECT_EQ(RetLoc1, RetLoc2);
249 TEST_F(CachedConstAccessorsLatticeTest, DifferentValsFromDifferentLocs) {
250 TestAST AST(R"cpp(
251 struct S {
252 int *valProperty() const;
254 void target() {
255 S s1;
256 s1.valProperty();
257 S s2;
258 s2.valProperty();
260 )cpp");
261 auto *SDecl =
262 cast<CXXRecordDecl>(lookup("S", *AST.context().getTranslationUnitDecl()));
263 QualType SType = AST.context().getRecordType(SDecl);
264 SmallVector<BoundNodes, 1> valPropertyCalls =
265 match(cxxMemberCallExpr(callee(functionDecl(hasName("valProperty"))))
266 .bind("call"),
267 AST.context());
268 ASSERT_THAT(valPropertyCalls, SizeIs(2));
270 const CallExpr *CE1 = selectFirst<CallExpr>("call", valPropertyCalls);
271 ASSERT_NE(CE1, nullptr);
273 valPropertyCalls.erase(valPropertyCalls.begin());
274 const CallExpr *CE2 = selectFirst<CallExpr>("call", valPropertyCalls);
275 ASSERT_NE(CE2, nullptr);
276 ASSERT_NE(CE1, CE2);
278 RecordStorageLocation LocS1(SType, RecordStorageLocation::FieldToLoc(), {});
279 RecordStorageLocation LocS2(SType, RecordStorageLocation::FieldToLoc(), {});
281 LatticeT Lattice;
282 Value *Val1 = Lattice.getOrCreateConstMethodReturnValue(LocS1, CE1, Env);
283 Value *Val2 = Lattice.getOrCreateConstMethodReturnValue(LocS2, CE2, Env);
285 EXPECT_NE(Val1, Val2);
288 TEST_F(CachedConstAccessorsLatticeTest, JoinSameNoop) {
289 CommonTestInputs Inputs;
290 auto *CE = Inputs.CallVal;
291 RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(),
292 {});
294 LatticeT EmptyLattice;
295 LatticeT EmptyLattice2;
296 EXPECT_EQ(EmptyLattice.join(EmptyLattice2), LatticeJoinEffect::Unchanged);
298 LatticeT Lattice1;
299 Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env);
300 EXPECT_EQ(Lattice1.join(Lattice1), LatticeJoinEffect::Unchanged);
303 TEST_F(CachedConstAccessorsLatticeTest, ProducesNewValueAfterJoinDistinct) {
304 CommonTestInputs Inputs;
305 auto *CE = Inputs.CallVal;
306 RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(),
307 {});
309 // L1 w/ v vs L2 empty
310 LatticeT Lattice1;
311 Value *Val1 = Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env);
313 LatticeT EmptyLattice;
315 EXPECT_EQ(Lattice1.join(EmptyLattice), LatticeJoinEffect::Changed);
316 Value *ValAfterJoin =
317 Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env);
319 EXPECT_NE(ValAfterJoin, Val1);
321 // L1 w/ v1 vs L3 w/ v2
322 LatticeT Lattice3;
323 Value *Val3 = Lattice3.getOrCreateConstMethodReturnValue(Loc, CE, Env);
325 EXPECT_EQ(Lattice1.join(Lattice3), LatticeJoinEffect::Changed);
326 Value *ValAfterJoin2 =
327 Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env);
329 EXPECT_NE(ValAfterJoin2, ValAfterJoin);
330 EXPECT_NE(ValAfterJoin2, Val3);
333 } // namespace
334 } // namespace clang::dataflow