187dd5dc8SJan Voung //===- unittests/Analysis/FlowSensitive/CachedConstAccessorsLatticeTest.cpp ==// 287dd5dc8SJan Voung // 387dd5dc8SJan Voung // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 487dd5dc8SJan Voung // See https://llvm.org/LICENSE.txt for license information. 587dd5dc8SJan Voung // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 687dd5dc8SJan Voung // 787dd5dc8SJan Voung //===----------------------------------------------------------------------===// 887dd5dc8SJan Voung 987dd5dc8SJan Voung #include "clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h" 1087dd5dc8SJan Voung 1187dd5dc8SJan Voung #include <cassert> 1287dd5dc8SJan Voung #include <memory> 1387dd5dc8SJan Voung 1487dd5dc8SJan Voung #include "clang/AST/Decl.h" 1587dd5dc8SJan Voung #include "clang/AST/DeclBase.h" 1687dd5dc8SJan Voung #include "clang/AST/DeclCXX.h" 1787dd5dc8SJan Voung #include "clang/AST/Expr.h" 1887dd5dc8SJan Voung #include "clang/AST/Type.h" 1987dd5dc8SJan Voung #include "clang/ASTMatchers/ASTMatchFinder.h" 2087dd5dc8SJan Voung #include "clang/ASTMatchers/ASTMatchers.h" 2187dd5dc8SJan Voung #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" 2287dd5dc8SJan Voung #include "clang/Analysis/FlowSensitive/DataflowLattice.h" 2387dd5dc8SJan Voung #include "clang/Analysis/FlowSensitive/NoopLattice.h" 2487dd5dc8SJan Voung #include "clang/Analysis/FlowSensitive/StorageLocation.h" 2587dd5dc8SJan Voung #include "clang/Analysis/FlowSensitive/Value.h" 2687dd5dc8SJan Voung #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h" 2787dd5dc8SJan Voung #include "clang/Basic/LLVM.h" 2887dd5dc8SJan Voung #include "clang/Testing/TestAST.h" 2987dd5dc8SJan Voung #include "gmock/gmock.h" 3087dd5dc8SJan Voung #include "gtest/gtest.h" 3187dd5dc8SJan Voung 3287dd5dc8SJan Voung namespace clang::dataflow { 3387dd5dc8SJan Voung namespace { 3487dd5dc8SJan Voung 3587dd5dc8SJan Voung using ast_matchers::BoundNodes; 3687dd5dc8SJan Voung using ast_matchers::callee; 3787dd5dc8SJan Voung using ast_matchers::cxxMemberCallExpr; 3887dd5dc8SJan Voung using ast_matchers::functionDecl; 3987dd5dc8SJan Voung using ast_matchers::hasName; 4087dd5dc8SJan Voung using ast_matchers::match; 4187dd5dc8SJan Voung using ast_matchers::selectFirst; 4287dd5dc8SJan Voung 4387dd5dc8SJan Voung using dataflow::DataflowAnalysisContext; 4487dd5dc8SJan Voung using dataflow::Environment; 4587dd5dc8SJan Voung using dataflow::LatticeJoinEffect; 4687dd5dc8SJan Voung using dataflow::RecordStorageLocation; 4787dd5dc8SJan Voung using dataflow::Value; 4887dd5dc8SJan Voung using dataflow::WatchedLiteralsSolver; 4987dd5dc8SJan Voung 5087dd5dc8SJan Voung using testing::SizeIs; 5187dd5dc8SJan Voung 5287dd5dc8SJan Voung NamedDecl *lookup(StringRef Name, const DeclContext &DC) { 5387dd5dc8SJan Voung auto Result = DC.lookup(&DC.getParentASTContext().Idents.get(Name)); 5487dd5dc8SJan Voung EXPECT_TRUE(Result.isSingleResult()) << Name; 5587dd5dc8SJan Voung return Result.front(); 5687dd5dc8SJan Voung } 5787dd5dc8SJan Voung 5887dd5dc8SJan Voung class CachedConstAccessorsLatticeTest : public ::testing::Test { 5987dd5dc8SJan Voung protected: 6087dd5dc8SJan Voung using LatticeT = CachedConstAccessorsLattice<NoopLattice>; 6187dd5dc8SJan Voung 6287dd5dc8SJan Voung DataflowAnalysisContext DACtx{std::make_unique<WatchedLiteralsSolver>()}; 6387dd5dc8SJan Voung Environment Env{DACtx}; 6487dd5dc8SJan Voung }; 6587dd5dc8SJan Voung 6687dd5dc8SJan Voung // Basic test AST with two const methods (return a value, and return a ref). 6787dd5dc8SJan Voung struct CommonTestInputs { 6887dd5dc8SJan Voung CommonTestInputs() 6987dd5dc8SJan Voung : AST(R"cpp( 7087dd5dc8SJan Voung struct S { 7187dd5dc8SJan Voung int *valProperty() const; 7287dd5dc8SJan Voung int &refProperty() const; 7387dd5dc8SJan Voung }; 7487dd5dc8SJan Voung void target() { 7587dd5dc8SJan Voung S s; 7687dd5dc8SJan Voung s.valProperty(); 7787dd5dc8SJan Voung S s2; 7887dd5dc8SJan Voung s2.refProperty(); 7987dd5dc8SJan Voung } 8087dd5dc8SJan Voung )cpp") { 8187dd5dc8SJan Voung auto *SDecl = cast<CXXRecordDecl>( 8287dd5dc8SJan Voung lookup("S", *AST.context().getTranslationUnitDecl())); 8387dd5dc8SJan Voung SType = AST.context().getRecordType(SDecl); 8487dd5dc8SJan Voung CallVal = selectFirst<CallExpr>( 8587dd5dc8SJan Voung "call", 8687dd5dc8SJan Voung match(cxxMemberCallExpr(callee(functionDecl(hasName("valProperty")))) 8787dd5dc8SJan Voung .bind("call"), 8887dd5dc8SJan Voung AST.context())); 8987dd5dc8SJan Voung assert(CallVal != nullptr); 9087dd5dc8SJan Voung 9187dd5dc8SJan Voung CallRef = selectFirst<CallExpr>( 9287dd5dc8SJan Voung "call", 9387dd5dc8SJan Voung match(cxxMemberCallExpr(callee(functionDecl(hasName("refProperty")))) 9487dd5dc8SJan Voung .bind("call"), 9587dd5dc8SJan Voung AST.context())); 9687dd5dc8SJan Voung assert(CallRef != nullptr); 9787dd5dc8SJan Voung } 9887dd5dc8SJan Voung 9987dd5dc8SJan Voung TestAST AST; 10087dd5dc8SJan Voung QualType SType; 10187dd5dc8SJan Voung const CallExpr *CallVal; 10287dd5dc8SJan Voung const CallExpr *CallRef; 10387dd5dc8SJan Voung }; 10487dd5dc8SJan Voung 10587dd5dc8SJan Voung TEST_F(CachedConstAccessorsLatticeTest, 10687dd5dc8SJan Voung SamePrimitiveValBeforeClearOrDiffAfterClear) { 10787dd5dc8SJan Voung CommonTestInputs Inputs; 10887dd5dc8SJan Voung auto *CE = Inputs.CallVal; 10987dd5dc8SJan Voung RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(), 11087dd5dc8SJan Voung {}); 11187dd5dc8SJan Voung 11287dd5dc8SJan Voung LatticeT Lattice; 11387dd5dc8SJan Voung Value *Val1 = Lattice.getOrCreateConstMethodReturnValue(Loc, CE, Env); 11487dd5dc8SJan Voung Value *Val2 = Lattice.getOrCreateConstMethodReturnValue(Loc, CE, Env); 11587dd5dc8SJan Voung 11687dd5dc8SJan Voung EXPECT_EQ(Val1, Val2); 11787dd5dc8SJan Voung 11887dd5dc8SJan Voung Lattice.clearConstMethodReturnValues(Loc); 11987dd5dc8SJan Voung Value *Val3 = Lattice.getOrCreateConstMethodReturnValue(Loc, CE, Env); 12087dd5dc8SJan Voung 12187dd5dc8SJan Voung EXPECT_NE(Val3, Val1); 12287dd5dc8SJan Voung EXPECT_NE(Val3, Val2); 12387dd5dc8SJan Voung } 12487dd5dc8SJan Voung 12587dd5dc8SJan Voung TEST_F(CachedConstAccessorsLatticeTest, SameLocBeforeClearOrDiffAfterClear) { 12687dd5dc8SJan Voung CommonTestInputs Inputs; 12787dd5dc8SJan Voung auto *CE = Inputs.CallRef; 12887dd5dc8SJan Voung RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(), 12987dd5dc8SJan Voung {}); 13087dd5dc8SJan Voung 13187dd5dc8SJan Voung LatticeT Lattice; 13287dd5dc8SJan Voung auto NopInit = [](StorageLocation &) {}; 13387dd5dc8SJan Voung StorageLocation *Loc1 = Lattice.getOrCreateConstMethodReturnStorageLocation( 13487dd5dc8SJan Voung Loc, CE, Env, NopInit); 13587dd5dc8SJan Voung auto NotCalled = [](StorageLocation &) { 13687dd5dc8SJan Voung ASSERT_TRUE(false) << "Not reached"; 13787dd5dc8SJan Voung }; 13887dd5dc8SJan Voung StorageLocation *Loc2 = Lattice.getOrCreateConstMethodReturnStorageLocation( 13987dd5dc8SJan Voung Loc, CE, Env, NotCalled); 14087dd5dc8SJan Voung 14187dd5dc8SJan Voung EXPECT_EQ(Loc1, Loc2); 14287dd5dc8SJan Voung 14387dd5dc8SJan Voung Lattice.clearConstMethodReturnStorageLocations(Loc); 14487dd5dc8SJan Voung StorageLocation *Loc3 = Lattice.getOrCreateConstMethodReturnStorageLocation( 14587dd5dc8SJan Voung Loc, CE, Env, NopInit); 14687dd5dc8SJan Voung 14787dd5dc8SJan Voung EXPECT_NE(Loc3, Loc1); 14887dd5dc8SJan Voung EXPECT_NE(Loc3, Loc2); 14987dd5dc8SJan Voung } 15087dd5dc8SJan Voung 15187dd5dc8SJan Voung TEST_F(CachedConstAccessorsLatticeTest, 152*72a28a3bSJan Voung SameLocBeforeClearOrDiffAfterClearWithCallee) { 153*72a28a3bSJan Voung CommonTestInputs Inputs; 154*72a28a3bSJan Voung auto *CE = Inputs.CallRef; 155*72a28a3bSJan Voung RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(), 156*72a28a3bSJan Voung {}); 157*72a28a3bSJan Voung 158*72a28a3bSJan Voung LatticeT Lattice; 159*72a28a3bSJan Voung auto NopInit = [](StorageLocation &) {}; 160*72a28a3bSJan Voung const FunctionDecl *Callee = CE->getDirectCallee(); 161*72a28a3bSJan Voung ASSERT_NE(Callee, nullptr); 162*72a28a3bSJan Voung StorageLocation &Loc1 = Lattice.getOrCreateConstMethodReturnStorageLocation( 163*72a28a3bSJan Voung Loc, Callee, Env, NopInit); 164*72a28a3bSJan Voung auto NotCalled = [](StorageLocation &) { 165*72a28a3bSJan Voung ASSERT_TRUE(false) << "Not reached"; 166*72a28a3bSJan Voung }; 167*72a28a3bSJan Voung StorageLocation &Loc2 = Lattice.getOrCreateConstMethodReturnStorageLocation( 168*72a28a3bSJan Voung Loc, Callee, Env, NotCalled); 169*72a28a3bSJan Voung 170*72a28a3bSJan Voung EXPECT_EQ(&Loc1, &Loc2); 171*72a28a3bSJan Voung 172*72a28a3bSJan Voung Lattice.clearConstMethodReturnStorageLocations(Loc); 173*72a28a3bSJan Voung StorageLocation &Loc3 = Lattice.getOrCreateConstMethodReturnStorageLocation( 174*72a28a3bSJan Voung Loc, Callee, Env, NopInit); 175*72a28a3bSJan Voung 176*72a28a3bSJan Voung EXPECT_NE(&Loc3, &Loc1); 177*72a28a3bSJan Voung EXPECT_NE(&Loc3, &Loc2); 178*72a28a3bSJan Voung } 179*72a28a3bSJan Voung 180*72a28a3bSJan Voung TEST_F(CachedConstAccessorsLatticeTest, 18187dd5dc8SJan Voung SameStructValBeforeClearOrDiffAfterClear) { 18287dd5dc8SJan Voung TestAST AST(R"cpp( 18387dd5dc8SJan Voung struct S { 18487dd5dc8SJan Voung S structValProperty() const; 18587dd5dc8SJan Voung }; 18687dd5dc8SJan Voung void target() { 18787dd5dc8SJan Voung S s; 18887dd5dc8SJan Voung s.structValProperty(); 18987dd5dc8SJan Voung } 19087dd5dc8SJan Voung )cpp"); 19187dd5dc8SJan Voung auto *SDecl = 19287dd5dc8SJan Voung cast<CXXRecordDecl>(lookup("S", *AST.context().getTranslationUnitDecl())); 19387dd5dc8SJan Voung QualType SType = AST.context().getRecordType(SDecl); 19487dd5dc8SJan Voung const CallExpr *CE = selectFirst<CallExpr>( 19587dd5dc8SJan Voung "call", match(cxxMemberCallExpr( 19687dd5dc8SJan Voung callee(functionDecl(hasName("structValProperty")))) 19787dd5dc8SJan Voung .bind("call"), 19887dd5dc8SJan Voung AST.context())); 19987dd5dc8SJan Voung ASSERT_NE(CE, nullptr); 20087dd5dc8SJan Voung 20187dd5dc8SJan Voung RecordStorageLocation Loc(SType, RecordStorageLocation::FieldToLoc(), {}); 20287dd5dc8SJan Voung 20387dd5dc8SJan Voung LatticeT Lattice; 20487dd5dc8SJan Voung // Accessors that return a record by value are modeled by a record storage 20587dd5dc8SJan Voung // location (instead of a Value). 20687dd5dc8SJan Voung auto NopInit = [](StorageLocation &) {}; 20787dd5dc8SJan Voung StorageLocation *Loc1 = Lattice.getOrCreateConstMethodReturnStorageLocation( 20887dd5dc8SJan Voung Loc, CE, Env, NopInit); 20987dd5dc8SJan Voung auto NotCalled = [](StorageLocation &) { 21087dd5dc8SJan Voung ASSERT_TRUE(false) << "Not reached"; 21187dd5dc8SJan Voung }; 21287dd5dc8SJan Voung StorageLocation *Loc2 = Lattice.getOrCreateConstMethodReturnStorageLocation( 21387dd5dc8SJan Voung Loc, CE, Env, NotCalled); 21487dd5dc8SJan Voung 21587dd5dc8SJan Voung EXPECT_EQ(Loc1, Loc2); 21687dd5dc8SJan Voung 21787dd5dc8SJan Voung Lattice.clearConstMethodReturnStorageLocations(Loc); 21887dd5dc8SJan Voung StorageLocation *Loc3 = Lattice.getOrCreateConstMethodReturnStorageLocation( 21987dd5dc8SJan Voung Loc, CE, Env, NopInit); 22087dd5dc8SJan Voung 22187dd5dc8SJan Voung EXPECT_NE(Loc3, Loc1); 22287dd5dc8SJan Voung EXPECT_NE(Loc3, Loc1); 22387dd5dc8SJan Voung } 22487dd5dc8SJan Voung 22587dd5dc8SJan Voung TEST_F(CachedConstAccessorsLatticeTest, ClearDifferentLocs) { 22687dd5dc8SJan Voung CommonTestInputs Inputs; 22787dd5dc8SJan Voung auto *CE = Inputs.CallRef; 22887dd5dc8SJan Voung RecordStorageLocation LocS1(Inputs.SType, RecordStorageLocation::FieldToLoc(), 22987dd5dc8SJan Voung {}); 23087dd5dc8SJan Voung RecordStorageLocation LocS2(Inputs.SType, RecordStorageLocation::FieldToLoc(), 23187dd5dc8SJan Voung {}); 23287dd5dc8SJan Voung 23387dd5dc8SJan Voung LatticeT Lattice; 23487dd5dc8SJan Voung auto NopInit = [](StorageLocation &) {}; 23587dd5dc8SJan Voung StorageLocation *RetLoc1 = 23687dd5dc8SJan Voung Lattice.getOrCreateConstMethodReturnStorageLocation(LocS1, CE, Env, 23787dd5dc8SJan Voung NopInit); 23887dd5dc8SJan Voung Lattice.clearConstMethodReturnStorageLocations(LocS2); 23987dd5dc8SJan Voung auto NotCalled = [](StorageLocation &) { 24087dd5dc8SJan Voung ASSERT_TRUE(false) << "Not reached"; 24187dd5dc8SJan Voung }; 24287dd5dc8SJan Voung StorageLocation *RetLoc2 = 24387dd5dc8SJan Voung Lattice.getOrCreateConstMethodReturnStorageLocation(LocS1, CE, Env, 24487dd5dc8SJan Voung NotCalled); 24587dd5dc8SJan Voung 24687dd5dc8SJan Voung EXPECT_EQ(RetLoc1, RetLoc2); 24787dd5dc8SJan Voung } 24887dd5dc8SJan Voung 24987dd5dc8SJan Voung TEST_F(CachedConstAccessorsLatticeTest, DifferentValsFromDifferentLocs) { 25087dd5dc8SJan Voung TestAST AST(R"cpp( 25187dd5dc8SJan Voung struct S { 25287dd5dc8SJan Voung int *valProperty() const; 25387dd5dc8SJan Voung }; 25487dd5dc8SJan Voung void target() { 25587dd5dc8SJan Voung S s1; 25687dd5dc8SJan Voung s1.valProperty(); 25787dd5dc8SJan Voung S s2; 25887dd5dc8SJan Voung s2.valProperty(); 25987dd5dc8SJan Voung } 26087dd5dc8SJan Voung )cpp"); 26187dd5dc8SJan Voung auto *SDecl = 26287dd5dc8SJan Voung cast<CXXRecordDecl>(lookup("S", *AST.context().getTranslationUnitDecl())); 26387dd5dc8SJan Voung QualType SType = AST.context().getRecordType(SDecl); 26487dd5dc8SJan Voung SmallVector<BoundNodes, 1> valPropertyCalls = 26587dd5dc8SJan Voung match(cxxMemberCallExpr(callee(functionDecl(hasName("valProperty")))) 26687dd5dc8SJan Voung .bind("call"), 26787dd5dc8SJan Voung AST.context()); 26887dd5dc8SJan Voung ASSERT_THAT(valPropertyCalls, SizeIs(2)); 26987dd5dc8SJan Voung 27087dd5dc8SJan Voung const CallExpr *CE1 = selectFirst<CallExpr>("call", valPropertyCalls); 27187dd5dc8SJan Voung ASSERT_NE(CE1, nullptr); 27287dd5dc8SJan Voung 27387dd5dc8SJan Voung valPropertyCalls.erase(valPropertyCalls.begin()); 27487dd5dc8SJan Voung const CallExpr *CE2 = selectFirst<CallExpr>("call", valPropertyCalls); 27587dd5dc8SJan Voung ASSERT_NE(CE2, nullptr); 27687dd5dc8SJan Voung ASSERT_NE(CE1, CE2); 27787dd5dc8SJan Voung 27887dd5dc8SJan Voung RecordStorageLocation LocS1(SType, RecordStorageLocation::FieldToLoc(), {}); 27987dd5dc8SJan Voung RecordStorageLocation LocS2(SType, RecordStorageLocation::FieldToLoc(), {}); 28087dd5dc8SJan Voung 28187dd5dc8SJan Voung LatticeT Lattice; 28287dd5dc8SJan Voung Value *Val1 = Lattice.getOrCreateConstMethodReturnValue(LocS1, CE1, Env); 28387dd5dc8SJan Voung Value *Val2 = Lattice.getOrCreateConstMethodReturnValue(LocS2, CE2, Env); 28487dd5dc8SJan Voung 28587dd5dc8SJan Voung EXPECT_NE(Val1, Val2); 28687dd5dc8SJan Voung } 28787dd5dc8SJan Voung 28887dd5dc8SJan Voung TEST_F(CachedConstAccessorsLatticeTest, JoinSameNoop) { 28987dd5dc8SJan Voung CommonTestInputs Inputs; 29087dd5dc8SJan Voung auto *CE = Inputs.CallVal; 29187dd5dc8SJan Voung RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(), 29287dd5dc8SJan Voung {}); 29387dd5dc8SJan Voung 29487dd5dc8SJan Voung LatticeT EmptyLattice; 29587dd5dc8SJan Voung LatticeT EmptyLattice2; 29687dd5dc8SJan Voung EXPECT_EQ(EmptyLattice.join(EmptyLattice2), LatticeJoinEffect::Unchanged); 29787dd5dc8SJan Voung 29887dd5dc8SJan Voung LatticeT Lattice1; 29987dd5dc8SJan Voung Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env); 30087dd5dc8SJan Voung EXPECT_EQ(Lattice1.join(Lattice1), LatticeJoinEffect::Unchanged); 30187dd5dc8SJan Voung } 30287dd5dc8SJan Voung 30387dd5dc8SJan Voung TEST_F(CachedConstAccessorsLatticeTest, ProducesNewValueAfterJoinDistinct) { 30487dd5dc8SJan Voung CommonTestInputs Inputs; 30587dd5dc8SJan Voung auto *CE = Inputs.CallVal; 30687dd5dc8SJan Voung RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(), 30787dd5dc8SJan Voung {}); 30887dd5dc8SJan Voung 30987dd5dc8SJan Voung // L1 w/ v vs L2 empty 31087dd5dc8SJan Voung LatticeT Lattice1; 31187dd5dc8SJan Voung Value *Val1 = Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env); 31287dd5dc8SJan Voung 31387dd5dc8SJan Voung LatticeT EmptyLattice; 31487dd5dc8SJan Voung 31587dd5dc8SJan Voung EXPECT_EQ(Lattice1.join(EmptyLattice), LatticeJoinEffect::Changed); 31687dd5dc8SJan Voung Value *ValAfterJoin = 31787dd5dc8SJan Voung Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env); 31887dd5dc8SJan Voung 31987dd5dc8SJan Voung EXPECT_NE(ValAfterJoin, Val1); 32087dd5dc8SJan Voung 32187dd5dc8SJan Voung // L1 w/ v1 vs L3 w/ v2 32287dd5dc8SJan Voung LatticeT Lattice3; 32387dd5dc8SJan Voung Value *Val3 = Lattice3.getOrCreateConstMethodReturnValue(Loc, CE, Env); 32487dd5dc8SJan Voung 32587dd5dc8SJan Voung EXPECT_EQ(Lattice1.join(Lattice3), LatticeJoinEffect::Changed); 32687dd5dc8SJan Voung Value *ValAfterJoin2 = 32787dd5dc8SJan Voung Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env); 32887dd5dc8SJan Voung 32987dd5dc8SJan Voung EXPECT_NE(ValAfterJoin2, ValAfterJoin); 33087dd5dc8SJan Voung EXPECT_NE(ValAfterJoin2, Val3); 33187dd5dc8SJan Voung } 33287dd5dc8SJan Voung 33387dd5dc8SJan Voung } // namespace 33487dd5dc8SJan Voung } // namespace clang::dataflow 335