brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.9 KiB · 0780db9 Raw
554 lines · cpp
1//===- unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.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/DataflowEnvironment.h"10#include "TestingSupport.h"11#include "clang/AST/DeclCXX.h"12#include "clang/AST/ExprCXX.h"13#include "clang/AST/Stmt.h"14#include "clang/ASTMatchers/ASTMatchFinder.h"15#include "clang/ASTMatchers/ASTMatchers.h"16#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"17#include "clang/Analysis/FlowSensitive/StorageLocation.h"18#include "clang/Analysis/FlowSensitive/Value.h"19#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"20#include "clang/Tooling/Tooling.h"21#include "gmock/gmock.h"22#include "gtest/gtest.h"23#include <memory>24#include <string>25 26namespace {27 28using namespace clang;29using namespace dataflow;30using ::clang::dataflow::test::findValueDecl;31using ::clang::dataflow::test::getFieldValue;32using ::testing::Contains;33using ::testing::IsNull;34using ::testing::NotNull;35 36class EnvironmentTest : public ::testing::Test {37protected:38  EnvironmentTest() : DAContext(std::make_unique<WatchedLiteralsSolver>()) {}39 40  DataflowAnalysisContext DAContext;41};42 43TEST_F(EnvironmentTest, FlowCondition) {44  Environment Env(DAContext);45  auto &A = Env.arena();46 47  EXPECT_TRUE(Env.proves(A.makeLiteral(true)));48  EXPECT_TRUE(Env.allows(A.makeLiteral(true)));49  EXPECT_FALSE(Env.proves(A.makeLiteral(false)));50  EXPECT_FALSE(Env.allows(A.makeLiteral(false)));51 52  auto &X = A.makeAtomRef(A.makeAtom());53  EXPECT_FALSE(Env.proves(X));54  EXPECT_TRUE(Env.allows(X));55 56  Env.assume(X);57  EXPECT_TRUE(Env.proves(X));58  EXPECT_TRUE(Env.allows(X));59 60  auto &NotX = A.makeNot(X);61  EXPECT_FALSE(Env.proves(NotX));62  EXPECT_FALSE(Env.allows(NotX));63}64 65TEST_F(EnvironmentTest, SetAndGetValueOnCfgOmittedNodes) {66  // Check that we can set a value on an expression that is omitted from the CFG67  // (see `ignoreCFGOmittedNodes()`), then retrieve that same value from the68  // expression. This is a regression test; `setValue()` and `getValue()`69  // previously did not use `ignoreCFGOmittedNodes()` consistently.70 71  using namespace ast_matchers;72 73  std::string Code = R"cc(74    struct S {75      int f();76    };77    void target() {78      // Method call on a temporary produces an `ExprWithCleanups`.79      S().f();80      (1);81    }82  )cc";83 84  auto Unit =85      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});86  auto &Context = Unit->getASTContext();87 88  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);89 90  const ExprWithCleanups *WithCleanups = selectFirst<ExprWithCleanups>(91      "cleanups",92      match(exprWithCleanups(hasType(isInteger())).bind("cleanups"), Context));93  ASSERT_NE(WithCleanups, nullptr);94 95  const ParenExpr *Paren = selectFirst<ParenExpr>(96      "paren", match(parenExpr(hasType(isInteger())).bind("paren"), Context));97  ASSERT_NE(Paren, nullptr);98 99  Environment Env(DAContext);100  IntegerValue *Val1 =101      cast<IntegerValue>(Env.createValue(Unit->getASTContext().IntTy));102  Env.setValue(*WithCleanups, *Val1);103  EXPECT_EQ(Env.getValue(*WithCleanups), Val1);104 105  IntegerValue *Val2 =106      cast<IntegerValue>(Env.createValue(Unit->getASTContext().IntTy));107  Env.setValue(*Paren, *Val2);108  EXPECT_EQ(Env.getValue(*Paren), Val2);109}110 111TEST_F(EnvironmentTest, CreateValueRecursiveType) {112  using namespace ast_matchers;113 114  std::string Code = R"cc(115    struct Recursive {116      bool X;117      Recursive *R;118    };119    // Use both fields to force them to be created with `createValue`.120    void Usage(Recursive R) { (void)R.X; (void)R.R; }121  )cc";122 123  auto Unit =124      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});125  auto &Context = Unit->getASTContext();126 127  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);128 129  auto Results =130      match(qualType(hasDeclaration(recordDecl(131                         hasName("Recursive"),132                         has(fieldDecl(hasName("R")).bind("field-r")))))133                .bind("target"),134            Context);135  const QualType *TyPtr = selectFirst<QualType>("target", Results);136  ASSERT_THAT(TyPtr, NotNull());137  QualType Ty = *TyPtr;138  ASSERT_FALSE(Ty.isNull());139 140  const FieldDecl *R = selectFirst<FieldDecl>("field-r", Results);141  ASSERT_THAT(R, NotNull());142 143  Results = match(functionDecl(hasName("Usage")).bind("fun"), Context);144  const auto *Fun = selectFirst<FunctionDecl>("fun", Results);145  ASSERT_THAT(Fun, NotNull());146 147  // Verify that the struct and the field (`R`) with first appearance of the148  // type is created successfully.149  Environment Env(DAContext, *Fun);150  Env.initialize();151  auto &SLoc = cast<RecordStorageLocation>(Env.createObject(Ty));152  PointerValue *PV = cast_or_null<PointerValue>(getFieldValue(&SLoc, *R, Env));153  EXPECT_THAT(PV, NotNull());154}155 156TEST_F(EnvironmentTest, DifferentReferenceLocInJoin) {157  // This tests the case where the storage location for a reference-type158  // variable is different for two states being joined. We used to believe this159  // could not happen and therefore had an assertion disallowing this; this test160  // exists to demonstrate that we can handle this condition without a failing161  // assertion. See also the discussion here:162  // https://discourse.llvm.org/t/70086/6163 164  using namespace ast_matchers;165 166  std::string Code = R"cc(167    void f(int &ref) {}168  )cc";169 170  auto Unit =171      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});172  auto &Context = Unit->getASTContext();173 174  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);175 176  const ValueDecl *Ref = findValueDecl(Context, "ref");177 178  Environment Env1(DAContext);179  StorageLocation &Loc1 = Env1.createStorageLocation(Context.IntTy);180  Env1.setStorageLocation(*Ref, Loc1);181 182  Environment Env2(DAContext);183  StorageLocation &Loc2 = Env2.createStorageLocation(Context.IntTy);184  Env2.setStorageLocation(*Ref, Loc2);185 186  EXPECT_NE(&Loc1, &Loc2);187 188  Environment::ValueModel Model;189  Environment EnvJoined =190      Environment::join(Env1, Env2, Model, Environment::DiscardExprState);191 192  // Joining environments with different storage locations for the same193  // declaration results in the declaration being removed from the joined194  // environment.195  EXPECT_EQ(EnvJoined.getStorageLocation(*Ref), nullptr);196}197 198TEST_F(EnvironmentTest, InitGlobalVarsFun) {199  using namespace ast_matchers;200 201  std::string Code = R"cc(202     int Global = 0;203     int Target () { return Global; }204  )cc";205 206  auto Unit =207      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});208  auto &Context = Unit->getASTContext();209 210  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);211 212  auto Results =213      match(decl(anyOf(varDecl(hasName("Global")).bind("global"),214                       functionDecl(hasName("Target")).bind("target"))),215            Context);216  const auto *Fun = selectFirst<FunctionDecl>("target", Results);217  const auto *Var = selectFirst<VarDecl>("global", Results);218  ASSERT_THAT(Fun, NotNull());219  ASSERT_THAT(Var, NotNull());220 221  // Verify the global variable is populated when we analyze `Target`.222  Environment Env(DAContext, *Fun);223  Env.initialize();224  EXPECT_THAT(Env.getValue(*Var), NotNull());225}226 227// Tests that fields mentioned only in default member initializers are included228// in the set of tracked fields.229TEST_F(EnvironmentTest, IncludeFieldsFromDefaultInitializers) {230  using namespace ast_matchers;231 232  std::string Code = R"cc(233     struct S {234       S() {}235       int X = 3;236       int Y = X;237     };238     S foo();239  )cc";240 241  auto Unit =242      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});243  auto &Context = Unit->getASTContext();244 245  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);246 247  auto Results = match(248      qualType(hasDeclaration(249                   cxxRecordDecl(hasName("S"),250                                 hasMethod(cxxConstructorDecl().bind("target")))251                       .bind("struct")))252          .bind("ty"),253      Context);254  const auto *Constructor = selectFirst<FunctionDecl>("target", Results);255  const auto *Rec = selectFirst<RecordDecl>("struct", Results);256  const auto QTy = *selectFirst<QualType>("ty", Results);257  ASSERT_THAT(Constructor, NotNull());258  ASSERT_THAT(Rec, NotNull());259  ASSERT_FALSE(QTy.isNull());260 261  auto Fields = Rec->fields();262  FieldDecl *XDecl = nullptr;263  for (FieldDecl *Field : Fields) {264    if (Field->getNameAsString() == "X") {265      XDecl = Field;266      break;267    }268  }269  ASSERT_THAT(XDecl, NotNull());270 271  // Verify that the `X` field of `S` is populated when analyzing the272  // constructor, even though it is not referenced directly in the constructor.273  Environment Env(DAContext, *Constructor);274  Env.initialize();275  auto &Loc = cast<RecordStorageLocation>(Env.createObject(QTy));276  EXPECT_THAT(getFieldValue(&Loc, *XDecl, Env), NotNull());277}278 279TEST_F(EnvironmentTest, InitGlobalVarsFieldFun) {280  using namespace ast_matchers;281 282  std::string Code = R"cc(283     struct S { int Bar; };284     S Global = {0};285     int Target () { return Global.Bar; }286  )cc";287 288  auto Unit =289      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});290  auto &Context = Unit->getASTContext();291 292  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);293 294  auto Results =295      match(decl(anyOf(varDecl(hasName("Global")).bind("global"),296                       functionDecl(hasName("Target")).bind("target"))),297            Context);298  const auto *Fun = selectFirst<FunctionDecl>("target", Results);299  const auto *GlobalDecl = selectFirst<VarDecl>("global", Results);300  ASSERT_THAT(Fun, NotNull());301  ASSERT_THAT(GlobalDecl, NotNull());302 303  ASSERT_TRUE(GlobalDecl->getType()->isStructureType());304  auto GlobalFields = GlobalDecl->getType()->getAsRecordDecl()->fields();305 306  FieldDecl *BarDecl = nullptr;307  for (FieldDecl *Field : GlobalFields) {308    if (Field->getNameAsString() == "Bar") {309      BarDecl = Field;310      break;311    }312    FAIL() << "Unexpected field: " << Field->getNameAsString();313  }314  ASSERT_THAT(BarDecl, NotNull());315 316  // Verify the global variable is populated when we analyze `Target`.317  Environment Env(DAContext, *Fun);318  Env.initialize();319  const auto *GlobalLoc =320      cast<RecordStorageLocation>(Env.getStorageLocation(*GlobalDecl));321  auto *BarVal = getFieldValue(GlobalLoc, *BarDecl, Env);322  EXPECT_TRUE(isa<IntegerValue>(BarVal));323}324 325TEST_F(EnvironmentTest, InitGlobalVarsConstructor) {326  using namespace ast_matchers;327 328  std::string Code = R"cc(329     int Global = 0;330     struct Target {331       Target() : Field(Global) {}332       int Field;333     };334  )cc";335 336  auto Unit =337      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});338  auto &Context = Unit->getASTContext();339 340  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);341 342  auto Results =343      match(decl(anyOf(344                varDecl(hasName("Global")).bind("global"),345                cxxConstructorDecl(ofClass(hasName("Target"))).bind("target"))),346            Context);347  const auto *Ctor = selectFirst<CXXConstructorDecl>("target", Results);348  const auto *Var = selectFirst<VarDecl>("global", Results);349  ASSERT_TRUE(Ctor != nullptr);350  ASSERT_THAT(Var, NotNull());351 352  // Verify the global variable is populated when we analyze `Target`.353  Environment Env(DAContext, *Ctor);354  Env.initialize();355  EXPECT_THAT(Env.getValue(*Var), NotNull());356}357 358// Pointers to Members are a tricky case of accessor calls, complicated further359// when using templates where the pointer to the member is a template argument.360// This is a repro of a failure case seen in the wild.361TEST_F(EnvironmentTest,362       ModelMemberForAccessorUsingMethodPointerThroughTemplate) {363  using namespace ast_matchers;364 365  std::string Code = R"cc(366      struct S {367        int accessor() {return member;}368 369        int member = 0;370      };371 372      template <auto method>373      int Target(S* S) {374        return (S->*method)();375      }376 377     // We want to analyze the instantiation of Target for the accessor.378     int Instantiator () {S S; return Target<&S::accessor>(&S); }379  )cc";380 381  auto Unit =382      // C++17 for the simplifying use of auto in the template declaration.383      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});384  auto &Context = Unit->getASTContext();385 386  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);387 388  auto Results = match(389      decl(anyOf(functionDecl(hasName("Target"), isTemplateInstantiation())390                     .bind("target"),391                 fieldDecl(hasName("member")).bind("member"),392                 recordDecl(hasName("S")).bind("struct"))),393      Context);394  const auto *Fun = selectFirst<FunctionDecl>("target", Results);395  const auto *Struct = selectFirst<RecordDecl>("struct", Results);396  const auto *Member = selectFirst<FieldDecl>("member", Results);397  ASSERT_THAT(Fun, NotNull());398  ASSERT_THAT(Struct, NotNull());399  ASSERT_THAT(Member, NotNull());400 401  // Verify that `member` is modeled for `S` when we analyze402  // `Target<&S::accessor>`.403  Environment Env(DAContext, *Fun);404  Env.initialize();405  EXPECT_THAT(DAContext.getModeledFields(Context.getCanonicalTagType(Struct)),406              Contains(Member));407}408 409// This is a repro of a failure case seen in the wild.410TEST_F(EnvironmentTest, CXXDefaultInitExprResultObjIsWrappedExprResultObj) {411  using namespace ast_matchers;412 413  std::string Code = R"cc(414      struct Inner {};415 416      struct S {417        S() {}418 419        Inner i = {};420      };421  )cc";422 423  auto Unit =424      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});425  auto &Context = Unit->getASTContext();426 427  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);428 429  auto Results =430      match(cxxConstructorDecl(431                hasAnyConstructorInitializer(cxxCtorInitializer(432                    withInitializer(expr().bind("default_init_expr")))))433                .bind("ctor"),434            Context);435  const auto *Constructor = selectFirst<CXXConstructorDecl>("ctor", Results);436  const auto *DefaultInit =437      selectFirst<CXXDefaultInitExpr>("default_init_expr", Results);438 439  Environment Env(DAContext, *Constructor);440  Env.initialize();441  EXPECT_EQ(&Env.getResultObjectLocation(*DefaultInit),442            &Env.getResultObjectLocation(*DefaultInit->getExpr()));443}444 445// This test verifies the behavior of `getResultObjectLocation()` in446// scenarios involving inherited constructors.447// Since the specific AST node of interest `CXXConstructorDecl` is implicitly448// generated, we cannot annotate any statements inside of it as we do in tests449// within TransferTest. Thus, the only way to get the right `Environment` is by450// explicitly initializing it as we do in tests within EnvironmentTest.451// This is why this test is not inside TransferTest, where most of the tests for452// `getResultObjectLocation()` are located.453TEST_F(EnvironmentTest, ResultObjectLocationForInheritedCtorInitExpr) {454  using namespace ast_matchers;455 456  std::string Code = R"(457    struct Base {458      Base(int b) {}459    };460    struct Derived : Base {461      using Base::Base;462    };463 464    Derived d = Derived(0);465  )";466 467  auto Unit =468      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++20"});469  auto &Context = Unit->getASTContext();470 471  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);472 473  auto Results =474      match(cxxConstructorDecl(475                hasAnyConstructorInitializer(cxxCtorInitializer(476                    withInitializer(expr().bind("inherited_ctor_init_expr")))))477                .bind("ctor"),478            Context);479  const auto *Constructor = selectFirst<CXXConstructorDecl>("ctor", Results);480  const auto *InheritedCtorInit = selectFirst<CXXInheritedCtorInitExpr>(481      "inherited_ctor_init_expr", Results);482 483  EXPECT_EQ(InheritedCtorInit->child_begin(), InheritedCtorInit->child_end());484 485  Environment Env(DAContext, *Constructor);486  Env.initialize();487 488  RecordStorageLocation &Loc = Env.getResultObjectLocation(*InheritedCtorInit);489  EXPECT_NE(&Loc, nullptr);490 491  EXPECT_EQ(&Loc, Env.getThisPointeeStorageLocation());492}493 494TEST_F(EnvironmentTest, Stmt) {495  using namespace ast_matchers;496 497  std::string Code = R"cc(498      struct S { int i; };499      void foo() {500        S AnS = S{1};501      }502    )cc";503  auto Unit =504      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});505  auto &Context = Unit->getASTContext();506 507  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);508 509  auto *DeclStatement = const_cast<DeclStmt *>(selectFirst<DeclStmt>(510      "d", match(declStmt(hasSingleDecl(varDecl(hasName("AnS")))).bind("d"),511                 Context)));512  ASSERT_THAT(DeclStatement, NotNull());513  auto *Init = (cast<VarDecl>(*DeclStatement->decl_begin()))->getInit();514  ASSERT_THAT(Init, NotNull());515 516  // Verify that we can retrieve the result object location for the initializer517  // expression when we analyze the DeclStmt for `AnS`.518  Environment Env(DAContext, *DeclStatement);519  // Don't crash when initializing.520  Env.initialize();521  // And don't crash when retrieving the result object location.522  Env.getResultObjectLocation(*Init);523}524 525// This is a crash repro.526TEST_F(EnvironmentTest, LambdaCapturingThisInFieldInitializer) {527  using namespace ast_matchers;528  std::string Code = R"cc(529      struct S {530        int f{[this]() { return 1; }()};531      };532    )cc";533 534  auto Unit =535      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});536  auto &Context = Unit->getASTContext();537 538  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);539 540  auto *LambdaCallOperator = selectFirst<CXXMethodDecl>(541      "method", match(cxxMethodDecl(hasName("operator()"),542                                    ofClass(cxxRecordDecl(isLambda())))543                          .bind("method"),544                      Context));545 546  Environment Env(DAContext, *LambdaCallOperator);547  // Don't crash when initializing.548  Env.initialize();549  // And initialize the captured `this` pointee.550  ASSERT_NE(nullptr, Env.getThisPointeeStorageLocation());551}552 553} // namespace554