1 //===- unittests/Analysis/FlowSensitive/CachedConstAccessorsLatticeTest.cpp ==//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h"
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
{
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
{
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
{
71 int *valProperty() const;
72 int &refProperty() const;
81 auto *SDecl
= cast
<CXXRecordDecl
>(
82 lookup("S", *AST
.context().getTranslationUnitDecl()));
83 SType
= AST
.context().getRecordType(SDecl
);
84 CallVal
= selectFirst
<CallExpr
>(
86 match(cxxMemberCallExpr(callee(functionDecl(hasName("valProperty"))))
89 assert(CallVal
!= nullptr);
91 CallRef
= selectFirst
<CallExpr
>(
93 match(cxxMemberCallExpr(callee(functionDecl(hasName("refProperty"))))
96 assert(CallRef
!= nullptr);
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(),
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(),
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(),
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
) {
184 S structValProperty() const;
188 s.structValProperty();
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"))))
199 ASSERT_NE(CE
, nullptr);
201 RecordStorageLocation
Loc(SType
, RecordStorageLocation::FieldToLoc(), {});
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(),
230 RecordStorageLocation
LocS2(Inputs
.SType
, RecordStorageLocation::FieldToLoc(),
234 auto NopInit
= [](StorageLocation
&) {};
235 StorageLocation
*RetLoc1
=
236 Lattice
.getOrCreateConstMethodReturnStorageLocation(LocS1
, CE
, Env
,
238 Lattice
.clearConstMethodReturnStorageLocations(LocS2
);
239 auto NotCalled
= [](StorageLocation
&) {
240 ASSERT_TRUE(false) << "Not reached";
242 StorageLocation
*RetLoc2
=
243 Lattice
.getOrCreateConstMethodReturnStorageLocation(LocS1
, CE
, Env
,
246 EXPECT_EQ(RetLoc1
, RetLoc2
);
249 TEST_F(CachedConstAccessorsLatticeTest
, DifferentValsFromDifferentLocs
) {
252 int *valProperty() const;
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"))))
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);
278 RecordStorageLocation
LocS1(SType
, RecordStorageLocation::FieldToLoc(), {});
279 RecordStorageLocation
LocS2(SType
, RecordStorageLocation::FieldToLoc(), {});
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(),
294 LatticeT EmptyLattice
;
295 LatticeT EmptyLattice2
;
296 EXPECT_EQ(EmptyLattice
.join(EmptyLattice2
), LatticeJoinEffect::Unchanged
);
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(),
309 // L1 w/ v vs L2 empty
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
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
);
334 } // namespace clang::dataflow