brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.9 KiB · 67b471e Raw
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