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 //===----------------------------------------------------------------------===// 8 9 #include "clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h" 10 11 #include <cassert> 12 #include <memory> 13 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" 31 32 namespace clang::dataflow { 33 namespace { 34 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; 42 43 using dataflow::DataflowAnalysisContext; 44 using dataflow::Environment; 45 using dataflow::LatticeJoinEffect; 46 using dataflow::RecordStorageLocation; 47 using dataflow::Value; 48 using dataflow::WatchedLiteralsSolver; 49 50 using testing::SizeIs; 51 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(); 56 } 57 58 class CachedConstAccessorsLatticeTest : public ::testing::Test { 59 protected: 60 using LatticeT = CachedConstAccessorsLattice<NoopLattice>; 61 62 DataflowAnalysisContext DACtx{std::make_unique<WatchedLiteralsSolver>()}; 63 Environment Env{DACtx}; 64 }; 65 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; 73 }; 74 void target() { 75 S s; 76 s.valProperty(); 77 S s2; 78 s2.refProperty(); 79 } 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); 90 91 CallRef = selectFirst<CallExpr>( 92 "call", 93 match(cxxMemberCallExpr(callee(functionDecl(hasName("refProperty")))) 94 .bind("call"), 95 AST.context())); 96 assert(CallRef != nullptr); 97 } 98 99 TestAST AST; 100 QualType SType; 101 const CallExpr *CallVal; 102 const CallExpr *CallRef; 103 }; 104 105 TEST_F(CachedConstAccessorsLatticeTest, 106 SamePrimitiveValBeforeClearOrDiffAfterClear) { 107 CommonTestInputs Inputs; 108 auto *CE = Inputs.CallVal; 109 RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(), 110 {}); 111 112 LatticeT Lattice; 113 Value *Val1 = Lattice.getOrCreateConstMethodReturnValue(Loc, CE, Env); 114 Value *Val2 = Lattice.getOrCreateConstMethodReturnValue(Loc, CE, Env); 115 116 EXPECT_EQ(Val1, Val2); 117 118 Lattice.clearConstMethodReturnValues(Loc); 119 Value *Val3 = Lattice.getOrCreateConstMethodReturnValue(Loc, CE, Env); 120 121 EXPECT_NE(Val3, Val1); 122 EXPECT_NE(Val3, Val2); 123 } 124 125 TEST_F(CachedConstAccessorsLatticeTest, SameLocBeforeClearOrDiffAfterClear) { 126 CommonTestInputs Inputs; 127 auto *CE = Inputs.CallRef; 128 RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(), 129 {}); 130 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"; 137 }; 138 StorageLocation *Loc2 = Lattice.getOrCreateConstMethodReturnStorageLocation( 139 Loc, CE, Env, NotCalled); 140 141 EXPECT_EQ(Loc1, Loc2); 142 143 Lattice.clearConstMethodReturnStorageLocations(Loc); 144 StorageLocation *Loc3 = Lattice.getOrCreateConstMethodReturnStorageLocation( 145 Loc, CE, Env, NopInit); 146 147 EXPECT_NE(Loc3, Loc1); 148 EXPECT_NE(Loc3, Loc2); 149 } 150 151 TEST_F(CachedConstAccessorsLatticeTest, 152 SameLocBeforeClearOrDiffAfterClearWithCallee) { 153 CommonTestInputs Inputs; 154 auto *CE = Inputs.CallRef; 155 RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(), 156 {}); 157 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"; 166 }; 167 StorageLocation &Loc2 = Lattice.getOrCreateConstMethodReturnStorageLocation( 168 Loc, Callee, Env, NotCalled); 169 170 EXPECT_EQ(&Loc1, &Loc2); 171 172 Lattice.clearConstMethodReturnStorageLocations(Loc); 173 StorageLocation &Loc3 = Lattice.getOrCreateConstMethodReturnStorageLocation( 174 Loc, Callee, Env, NopInit); 175 176 EXPECT_NE(&Loc3, &Loc1); 177 EXPECT_NE(&Loc3, &Loc2); 178 } 179 180 TEST_F(CachedConstAccessorsLatticeTest, 181 SameStructValBeforeClearOrDiffAfterClear) { 182 TestAST AST(R"cpp( 183 struct S { 184 S structValProperty() const; 185 }; 186 void target() { 187 S s; 188 s.structValProperty(); 189 } 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); 200 201 RecordStorageLocation Loc(SType, RecordStorageLocation::FieldToLoc(), {}); 202 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"; 211 }; 212 StorageLocation *Loc2 = Lattice.getOrCreateConstMethodReturnStorageLocation( 213 Loc, CE, Env, NotCalled); 214 215 EXPECT_EQ(Loc1, Loc2); 216 217 Lattice.clearConstMethodReturnStorageLocations(Loc); 218 StorageLocation *Loc3 = Lattice.getOrCreateConstMethodReturnStorageLocation( 219 Loc, CE, Env, NopInit); 220 221 EXPECT_NE(Loc3, Loc1); 222 EXPECT_NE(Loc3, Loc1); 223 } 224 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 {}); 232 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"; 241 }; 242 StorageLocation *RetLoc2 = 243 Lattice.getOrCreateConstMethodReturnStorageLocation(LocS1, CE, Env, 244 NotCalled); 245 246 EXPECT_EQ(RetLoc1, RetLoc2); 247 } 248 249 TEST_F(CachedConstAccessorsLatticeTest, DifferentValsFromDifferentLocs) { 250 TestAST AST(R"cpp( 251 struct S { 252 int *valProperty() const; 253 }; 254 void target() { 255 S s1; 256 s1.valProperty(); 257 S s2; 258 s2.valProperty(); 259 } 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)); 269 270 const CallExpr *CE1 = selectFirst<CallExpr>("call", valPropertyCalls); 271 ASSERT_NE(CE1, nullptr); 272 273 valPropertyCalls.erase(valPropertyCalls.begin()); 274 const CallExpr *CE2 = selectFirst<CallExpr>("call", valPropertyCalls); 275 ASSERT_NE(CE2, nullptr); 276 ASSERT_NE(CE1, CE2); 277 278 RecordStorageLocation LocS1(SType, RecordStorageLocation::FieldToLoc(), {}); 279 RecordStorageLocation LocS2(SType, RecordStorageLocation::FieldToLoc(), {}); 280 281 LatticeT Lattice; 282 Value *Val1 = Lattice.getOrCreateConstMethodReturnValue(LocS1, CE1, Env); 283 Value *Val2 = Lattice.getOrCreateConstMethodReturnValue(LocS2, CE2, Env); 284 285 EXPECT_NE(Val1, Val2); 286 } 287 288 TEST_F(CachedConstAccessorsLatticeTest, JoinSameNoop) { 289 CommonTestInputs Inputs; 290 auto *CE = Inputs.CallVal; 291 RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(), 292 {}); 293 294 LatticeT EmptyLattice; 295 LatticeT EmptyLattice2; 296 EXPECT_EQ(EmptyLattice.join(EmptyLattice2), LatticeJoinEffect::Unchanged); 297 298 LatticeT Lattice1; 299 Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env); 300 EXPECT_EQ(Lattice1.join(Lattice1), LatticeJoinEffect::Unchanged); 301 } 302 303 TEST_F(CachedConstAccessorsLatticeTest, ProducesNewValueAfterJoinDistinct) { 304 CommonTestInputs Inputs; 305 auto *CE = Inputs.CallVal; 306 RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(), 307 {}); 308 309 // L1 w/ v vs L2 empty 310 LatticeT Lattice1; 311 Value *Val1 = Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env); 312 313 LatticeT EmptyLattice; 314 315 EXPECT_EQ(Lattice1.join(EmptyLattice), LatticeJoinEffect::Changed); 316 Value *ValAfterJoin = 317 Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env); 318 319 EXPECT_NE(ValAfterJoin, Val1); 320 321 // L1 w/ v1 vs L3 w/ v2 322 LatticeT Lattice3; 323 Value *Val3 = Lattice3.getOrCreateConstMethodReturnValue(Loc, CE, Env); 324 325 EXPECT_EQ(Lattice1.join(Lattice3), LatticeJoinEffect::Changed); 326 Value *ValAfterJoin2 = 327 Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env); 328 329 EXPECT_NE(ValAfterJoin2, ValAfterJoin); 330 EXPECT_NE(ValAfterJoin2, Val3); 331 } 332 333 } // namespace 334 } // namespace clang::dataflow 335