312 lines · cpp
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-exception6//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 32namespace clang::dataflow {33namespace {34 35using ast_matchers::BoundNodes;36using ast_matchers::callee;37using ast_matchers::cxxMemberCallExpr;38using ast_matchers::functionDecl;39using ast_matchers::hasName;40using ast_matchers::match;41using ast_matchers::selectFirst;42 43using dataflow::DataflowAnalysisContext;44using dataflow::Environment;45using dataflow::LatticeEffect;46using dataflow::RecordStorageLocation;47using dataflow::Value;48using dataflow::WatchedLiteralsSolver;49 50using testing::SizeIs;51 52NamedDecl *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 58class CachedConstAccessorsLatticeTest : public ::testing::Test {59protected: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).67struct 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().getCanonicalTagType(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 105TEST_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 125TEST_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 const FunctionDecl *Callee = CE->getDirectCallee();134 ASSERT_NE(Callee, nullptr);135 StorageLocation &Loc1 = Lattice.getOrCreateConstMethodReturnStorageLocation(136 Loc, Callee, Env, NopInit);137 auto NotCalled = [](StorageLocation &) {138 ASSERT_TRUE(false) << "Not reached";139 };140 StorageLocation &Loc2 = Lattice.getOrCreateConstMethodReturnStorageLocation(141 Loc, Callee, Env, NotCalled);142 143 EXPECT_EQ(&Loc1, &Loc2);144 145 Lattice.clearConstMethodReturnStorageLocations(Loc);146 StorageLocation &Loc3 = Lattice.getOrCreateConstMethodReturnStorageLocation(147 Loc, Callee, Env, NopInit);148 149 EXPECT_NE(&Loc3, &Loc1);150 EXPECT_NE(&Loc3, &Loc2);151}152 153TEST_F(CachedConstAccessorsLatticeTest,154 SameStructValBeforeClearOrDiffAfterClear) {155 TestAST AST(R"cpp(156 struct S {157 S structValProperty() const;158 };159 void target() {160 S s;161 s.structValProperty();162 }163 )cpp");164 auto *SDecl =165 cast<CXXRecordDecl>(lookup("S", *AST.context().getTranslationUnitDecl()));166 CanQualType SType = AST.context().getCanonicalTagType(SDecl);167 const CallExpr *CE = selectFirst<CallExpr>(168 "call", match(cxxMemberCallExpr(169 callee(functionDecl(hasName("structValProperty"))))170 .bind("call"),171 AST.context()));172 ASSERT_NE(CE, nullptr);173 174 RecordStorageLocation Loc(SType, RecordStorageLocation::FieldToLoc(), {});175 176 LatticeT Lattice;177 // Accessors that return a record by value are modeled by a record storage178 // location (instead of a Value).179 auto NopInit = [](StorageLocation &) {};180 const FunctionDecl *Callee = CE->getDirectCallee();181 ASSERT_NE(Callee, nullptr);182 StorageLocation &Loc1 = Lattice.getOrCreateConstMethodReturnStorageLocation(183 Loc, Callee, Env, NopInit);184 auto NotCalled = [](StorageLocation &) {185 ASSERT_TRUE(false) << "Not reached";186 };187 StorageLocation &Loc2 = Lattice.getOrCreateConstMethodReturnStorageLocation(188 Loc, Callee, Env, NotCalled);189 190 EXPECT_EQ(&Loc1, &Loc2);191 192 Lattice.clearConstMethodReturnStorageLocations(Loc);193 StorageLocation &Loc3 = Lattice.getOrCreateConstMethodReturnStorageLocation(194 Loc, Callee, Env, NopInit);195 196 EXPECT_NE(&Loc3, &Loc1);197 EXPECT_NE(&Loc3, &Loc1);198}199 200TEST_F(CachedConstAccessorsLatticeTest, ClearDifferentLocs) {201 CommonTestInputs Inputs;202 auto *CE = Inputs.CallRef;203 RecordStorageLocation LocS1(Inputs.SType, RecordStorageLocation::FieldToLoc(),204 {});205 RecordStorageLocation LocS2(Inputs.SType, RecordStorageLocation::FieldToLoc(),206 {});207 208 LatticeT Lattice;209 auto NopInit = [](StorageLocation &) {};210 const FunctionDecl *Callee = CE->getDirectCallee();211 ASSERT_NE(Callee, nullptr);212 StorageLocation &RetLoc1 =213 Lattice.getOrCreateConstMethodReturnStorageLocation(LocS1, Callee, Env,214 NopInit);215 Lattice.clearConstMethodReturnStorageLocations(LocS2);216 auto NotCalled = [](StorageLocation &) {217 ASSERT_TRUE(false) << "Not reached";218 };219 StorageLocation &RetLoc2 =220 Lattice.getOrCreateConstMethodReturnStorageLocation(LocS1, Callee, Env,221 NotCalled);222 223 EXPECT_EQ(&RetLoc1, &RetLoc2);224}225 226TEST_F(CachedConstAccessorsLatticeTest, DifferentValsFromDifferentLocs) {227 TestAST AST(R"cpp(228 struct S {229 int *valProperty() const;230 };231 void target() {232 S s1;233 s1.valProperty();234 S s2;235 s2.valProperty();236 }237 )cpp");238 auto *SDecl =239 cast<CXXRecordDecl>(lookup("S", *AST.context().getTranslationUnitDecl()));240 CanQualType SType = AST.context().getCanonicalTagType(SDecl);241 SmallVector<BoundNodes, 1> valPropertyCalls =242 match(cxxMemberCallExpr(callee(functionDecl(hasName("valProperty"))))243 .bind("call"),244 AST.context());245 ASSERT_THAT(valPropertyCalls, SizeIs(2));246 247 const CallExpr *CE1 = selectFirst<CallExpr>("call", valPropertyCalls);248 ASSERT_NE(CE1, nullptr);249 250 valPropertyCalls.erase(valPropertyCalls.begin());251 const CallExpr *CE2 = selectFirst<CallExpr>("call", valPropertyCalls);252 ASSERT_NE(CE2, nullptr);253 ASSERT_NE(CE1, CE2);254 255 RecordStorageLocation LocS1(SType, RecordStorageLocation::FieldToLoc(), {});256 RecordStorageLocation LocS2(SType, RecordStorageLocation::FieldToLoc(), {});257 258 LatticeT Lattice;259 Value *Val1 = Lattice.getOrCreateConstMethodReturnValue(LocS1, CE1, Env);260 Value *Val2 = Lattice.getOrCreateConstMethodReturnValue(LocS2, CE2, Env);261 262 EXPECT_NE(Val1, Val2);263}264 265TEST_F(CachedConstAccessorsLatticeTest, JoinSameNoop) {266 CommonTestInputs Inputs;267 auto *CE = Inputs.CallVal;268 RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(),269 {});270 271 LatticeT EmptyLattice;272 LatticeT EmptyLattice2;273 EXPECT_EQ(EmptyLattice.join(EmptyLattice2), LatticeEffect::Unchanged);274 275 LatticeT Lattice1;276 Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env);277 EXPECT_EQ(Lattice1.join(Lattice1), LatticeEffect::Unchanged);278}279 280TEST_F(CachedConstAccessorsLatticeTest, ProducesNewValueAfterJoinDistinct) {281 CommonTestInputs Inputs;282 auto *CE = Inputs.CallVal;283 RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(),284 {});285 286 // L1 w/ v vs L2 empty287 LatticeT Lattice1;288 Value *Val1 = Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env);289 290 LatticeT EmptyLattice;291 292 EXPECT_EQ(Lattice1.join(EmptyLattice), LatticeEffect::Changed);293 Value *ValAfterJoin =294 Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env);295 296 EXPECT_NE(ValAfterJoin, Val1);297 298 // L1 w/ v1 vs L3 w/ v2299 LatticeT Lattice3;300 Value *Val3 = Lattice3.getOrCreateConstMethodReturnValue(Loc, CE, Env);301 302 EXPECT_EQ(Lattice1.join(Lattice3), LatticeEffect::Changed);303 Value *ValAfterJoin2 =304 Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env);305 306 EXPECT_NE(ValAfterJoin2, ValAfterJoin);307 EXPECT_NE(ValAfterJoin2, Val3);308}309 310} // namespace311} // namespace clang::dataflow312