1ae60884dSStanislav Gatev //===- unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp -------===// 2ae60884dSStanislav Gatev // 3ae60884dSStanislav Gatev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4ae60884dSStanislav Gatev // See https://llvm.org/LICENSE.txt for license information. 5ae60884dSStanislav Gatev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6ae60884dSStanislav Gatev // 7ae60884dSStanislav Gatev //===----------------------------------------------------------------------===// 8ae60884dSStanislav Gatev 9ae60884dSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" 102902ea3dSMartin Braenne #include "TestingSupport.h" 11f3700bdbSYitzhak Mandelbaum #include "clang/AST/DeclCXX.h" 1280d9ae9cSSamira Bazuzi #include "clang/AST/ExprCXX.h" 1380d9ae9cSSamira Bazuzi #include "clang/AST/Stmt.h" 1473c98831SYitzhak Mandelbaum #include "clang/ASTMatchers/ASTMatchFinder.h" 15f3700bdbSYitzhak Mandelbaum #include "clang/ASTMatchers/ASTMatchers.h" 16ae60884dSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" 1773c98831SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/StorageLocation.h" 1818c84e2dSYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/Value.h" 19ae60884dSStanislav Gatev #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h" 2073c98831SYitzhak Mandelbaum #include "clang/Tooling/Tooling.h" 21ae60884dSStanislav Gatev #include "gmock/gmock.h" 22ae60884dSStanislav Gatev #include "gtest/gtest.h" 23ae60884dSStanislav Gatev #include <memory> 243fed312dSSamira Bazuzi #include <string> 25ae60884dSStanislav Gatev 26ae60884dSStanislav Gatev namespace { 27ae60884dSStanislav Gatev 28ae60884dSStanislav Gatev using namespace clang; 29ae60884dSStanislav Gatev using namespace dataflow; 3071f1932bSmartinboehme using ::clang::dataflow::test::findValueDecl; 312902ea3dSMartin Braenne using ::clang::dataflow::test::getFieldValue; 3240381d12SSamira Bazuzi using ::testing::Contains; 330f6cf555SMartin Braenne using ::testing::IsNull; 34f3700bdbSYitzhak Mandelbaum using ::testing::NotNull; 35ae60884dSStanislav Gatev 36ae60884dSStanislav Gatev class EnvironmentTest : public ::testing::Test { 37ae60884dSStanislav Gatev protected: 38f3700bdbSYitzhak Mandelbaum EnvironmentTest() : DAContext(std::make_unique<WatchedLiteralsSolver>()) {} 39ae60884dSStanislav Gatev 40f3700bdbSYitzhak Mandelbaum DataflowAnalysisContext DAContext; 41ae60884dSStanislav Gatev }; 42ae60884dSStanislav Gatev 43ae60884dSStanislav Gatev TEST_F(EnvironmentTest, FlowCondition) { 44f3700bdbSYitzhak Mandelbaum Environment Env(DAContext); 456272226bSSam McCall auto &A = Env.arena(); 46f3700bdbSYitzhak Mandelbaum 47d1f59544Smartinboehme EXPECT_TRUE(Env.proves(A.makeLiteral(true))); 48d1f59544Smartinboehme EXPECT_TRUE(Env.allows(A.makeLiteral(true))); 49d1f59544Smartinboehme EXPECT_FALSE(Env.proves(A.makeLiteral(false))); 50d1f59544Smartinboehme EXPECT_FALSE(Env.allows(A.makeLiteral(false))); 51ae60884dSStanislav Gatev 526272226bSSam McCall auto &X = A.makeAtomRef(A.makeAtom()); 53d1f59544Smartinboehme EXPECT_FALSE(Env.proves(X)); 54d1f59544Smartinboehme EXPECT_TRUE(Env.allows(X)); 55ae60884dSStanislav Gatev 56d1f59544Smartinboehme Env.assume(X); 57d1f59544Smartinboehme EXPECT_TRUE(Env.proves(X)); 58d1f59544Smartinboehme EXPECT_TRUE(Env.allows(X)); 59ae60884dSStanislav Gatev 606272226bSSam McCall auto &NotX = A.makeNot(X); 61d1f59544Smartinboehme EXPECT_FALSE(Env.proves(NotX)); 62d1f59544Smartinboehme EXPECT_FALSE(Env.allows(NotX)); 63ae60884dSStanislav Gatev } 64ae60884dSStanislav Gatev 6523bfc271Smartinboehme TEST_F(EnvironmentTest, SetAndGetValueOnCfgOmittedNodes) { 6623bfc271Smartinboehme // Check that we can set a value on an expression that is omitted from the CFG 6723bfc271Smartinboehme // (see `ignoreCFGOmittedNodes()`), then retrieve that same value from the 6823bfc271Smartinboehme // expression. This is a regression test; `setValue()` and `getValue()` 6923bfc271Smartinboehme // previously did not use `ignoreCFGOmittedNodes()` consistently. 7023bfc271Smartinboehme 7123bfc271Smartinboehme using namespace ast_matchers; 7223bfc271Smartinboehme 7323bfc271Smartinboehme std::string Code = R"cc( 7423bfc271Smartinboehme struct S { 7523bfc271Smartinboehme int f(); 7623bfc271Smartinboehme }; 7723bfc271Smartinboehme void target() { 7823bfc271Smartinboehme // Method call on a temporary produces an `ExprWithCleanups`. 7923bfc271Smartinboehme S().f(); 8023bfc271Smartinboehme (1); 8123bfc271Smartinboehme } 8223bfc271Smartinboehme )cc"; 8323bfc271Smartinboehme 8423bfc271Smartinboehme auto Unit = 8523bfc271Smartinboehme tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"}); 8623bfc271Smartinboehme auto &Context = Unit->getASTContext(); 8723bfc271Smartinboehme 8823bfc271Smartinboehme ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 8923bfc271Smartinboehme 9023bfc271Smartinboehme const ExprWithCleanups *WithCleanups = selectFirst<ExprWithCleanups>( 9123bfc271Smartinboehme "cleanups", 9223bfc271Smartinboehme match(exprWithCleanups(hasType(isInteger())).bind("cleanups"), Context)); 9323bfc271Smartinboehme ASSERT_NE(WithCleanups, nullptr); 9423bfc271Smartinboehme 9523bfc271Smartinboehme const ParenExpr *Paren = selectFirst<ParenExpr>( 9623bfc271Smartinboehme "paren", match(parenExpr(hasType(isInteger())).bind("paren"), Context)); 9723bfc271Smartinboehme ASSERT_NE(Paren, nullptr); 9823bfc271Smartinboehme 9923bfc271Smartinboehme Environment Env(DAContext); 10023bfc271Smartinboehme IntegerValue *Val1 = 10123bfc271Smartinboehme cast<IntegerValue>(Env.createValue(Unit->getASTContext().IntTy)); 10223bfc271Smartinboehme Env.setValue(*WithCleanups, *Val1); 10323bfc271Smartinboehme EXPECT_EQ(Env.getValue(*WithCleanups), Val1); 10423bfc271Smartinboehme 10523bfc271Smartinboehme IntegerValue *Val2 = 10623bfc271Smartinboehme cast<IntegerValue>(Env.createValue(Unit->getASTContext().IntTy)); 10723bfc271Smartinboehme Env.setValue(*Paren, *Val2); 10823bfc271Smartinboehme EXPECT_EQ(Env.getValue(*Paren), Val2); 10923bfc271Smartinboehme } 11023bfc271Smartinboehme 11118c84e2dSYitzhak Mandelbaum TEST_F(EnvironmentTest, CreateValueRecursiveType) { 11218c84e2dSYitzhak Mandelbaum using namespace ast_matchers; 11318c84e2dSYitzhak Mandelbaum 11418c84e2dSYitzhak Mandelbaum std::string Code = R"cc( 11518c84e2dSYitzhak Mandelbaum struct Recursive { 11618c84e2dSYitzhak Mandelbaum bool X; 11718c84e2dSYitzhak Mandelbaum Recursive *R; 11818c84e2dSYitzhak Mandelbaum }; 11901ccf7b3SYitzhak Mandelbaum // Use both fields to force them to be created with `createValue`. 12001ccf7b3SYitzhak Mandelbaum void Usage(Recursive R) { (void)R.X; (void)R.R; } 12118c84e2dSYitzhak Mandelbaum )cc"; 12218c84e2dSYitzhak Mandelbaum 12318c84e2dSYitzhak Mandelbaum auto Unit = 12418c84e2dSYitzhak Mandelbaum tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 12518c84e2dSYitzhak Mandelbaum auto &Context = Unit->getASTContext(); 12618c84e2dSYitzhak Mandelbaum 12718c84e2dSYitzhak Mandelbaum ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 12818c84e2dSYitzhak Mandelbaum 12918c84e2dSYitzhak Mandelbaum auto Results = 13018c84e2dSYitzhak Mandelbaum match(qualType(hasDeclaration(recordDecl( 13118c84e2dSYitzhak Mandelbaum hasName("Recursive"), 13218c84e2dSYitzhak Mandelbaum has(fieldDecl(hasName("R")).bind("field-r"))))) 13318c84e2dSYitzhak Mandelbaum .bind("target"), 13418c84e2dSYitzhak Mandelbaum Context); 13501ccf7b3SYitzhak Mandelbaum const QualType *TyPtr = selectFirst<QualType>("target", Results); 13601ccf7b3SYitzhak Mandelbaum ASSERT_THAT(TyPtr, NotNull()); 13701ccf7b3SYitzhak Mandelbaum QualType Ty = *TyPtr; 13801ccf7b3SYitzhak Mandelbaum ASSERT_FALSE(Ty.isNull()); 13901ccf7b3SYitzhak Mandelbaum 14018c84e2dSYitzhak Mandelbaum const FieldDecl *R = selectFirst<FieldDecl>("field-r", Results); 14101ccf7b3SYitzhak Mandelbaum ASSERT_THAT(R, NotNull()); 14201ccf7b3SYitzhak Mandelbaum 14301ccf7b3SYitzhak Mandelbaum Results = match(functionDecl(hasName("Usage")).bind("fun"), Context); 14401ccf7b3SYitzhak Mandelbaum const auto *Fun = selectFirst<FunctionDecl>("fun", Results); 14501ccf7b3SYitzhak Mandelbaum ASSERT_THAT(Fun, NotNull()); 14618c84e2dSYitzhak Mandelbaum 14718c84e2dSYitzhak Mandelbaum // Verify that the struct and the field (`R`) with first appearance of the 14818c84e2dSYitzhak Mandelbaum // type is created successfully. 14901ccf7b3SYitzhak Mandelbaum Environment Env(DAContext, *Fun); 15071f2ec2dSmartinboehme Env.initialize(); 1517cf20f15Smartinboehme auto &SLoc = cast<RecordStorageLocation>(Env.createObject(Ty)); 1527cf20f15Smartinboehme PointerValue *PV = cast_or_null<PointerValue>(getFieldValue(&SLoc, *R, Env)); 1532902ea3dSMartin Braenne EXPECT_THAT(PV, NotNull()); 15418c84e2dSYitzhak Mandelbaum } 15518c84e2dSYitzhak Mandelbaum 15671f1932bSmartinboehme TEST_F(EnvironmentTest, DifferentReferenceLocInJoin) { 15771f1932bSmartinboehme // This tests the case where the storage location for a reference-type 15871f1932bSmartinboehme // variable is different for two states being joined. We used to believe this 15971f1932bSmartinboehme // could not happen and therefore had an assertion disallowing this; this test 16071f1932bSmartinboehme // exists to demonstrate that we can handle this condition without a failing 16171f1932bSmartinboehme // assertion. See also the discussion here: 16271f1932bSmartinboehme // https://discourse.llvm.org/t/70086/6 16371f1932bSmartinboehme 16471f1932bSmartinboehme using namespace ast_matchers; 16571f1932bSmartinboehme 16671f1932bSmartinboehme std::string Code = R"cc( 16771f1932bSmartinboehme void f(int &ref) {} 16871f1932bSmartinboehme )cc"; 16971f1932bSmartinboehme 17071f1932bSmartinboehme auto Unit = 17171f1932bSmartinboehme tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 17271f1932bSmartinboehme auto &Context = Unit->getASTContext(); 17371f1932bSmartinboehme 17471f1932bSmartinboehme ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 17571f1932bSmartinboehme 17671f1932bSmartinboehme const ValueDecl *Ref = findValueDecl(Context, "ref"); 17771f1932bSmartinboehme 17871f1932bSmartinboehme Environment Env1(DAContext); 17971f1932bSmartinboehme StorageLocation &Loc1 = Env1.createStorageLocation(Context.IntTy); 18071f1932bSmartinboehme Env1.setStorageLocation(*Ref, Loc1); 18171f1932bSmartinboehme 18271f1932bSmartinboehme Environment Env2(DAContext); 18371f1932bSmartinboehme StorageLocation &Loc2 = Env2.createStorageLocation(Context.IntTy); 18471f1932bSmartinboehme Env2.setStorageLocation(*Ref, Loc2); 18571f1932bSmartinboehme 18671f1932bSmartinboehme EXPECT_NE(&Loc1, &Loc2); 18771f1932bSmartinboehme 18871f1932bSmartinboehme Environment::ValueModel Model; 18971f1932bSmartinboehme Environment EnvJoined = 19071f1932bSmartinboehme Environment::join(Env1, Env2, Model, Environment::DiscardExprState); 19171f1932bSmartinboehme 19271f1932bSmartinboehme // Joining environments with different storage locations for the same 19371f1932bSmartinboehme // declaration results in the declaration being removed from the joined 19471f1932bSmartinboehme // environment. 19571f1932bSmartinboehme EXPECT_EQ(EnvJoined.getStorageLocation(*Ref), nullptr); 19671f1932bSmartinboehme } 19771f1932bSmartinboehme 198f3700bdbSYitzhak Mandelbaum TEST_F(EnvironmentTest, InitGlobalVarsFun) { 199f3700bdbSYitzhak Mandelbaum using namespace ast_matchers; 200f3700bdbSYitzhak Mandelbaum 201f3700bdbSYitzhak Mandelbaum std::string Code = R"cc( 202f3700bdbSYitzhak Mandelbaum int Global = 0; 203f3700bdbSYitzhak Mandelbaum int Target () { return Global; } 204f3700bdbSYitzhak Mandelbaum )cc"; 205f3700bdbSYitzhak Mandelbaum 206f3700bdbSYitzhak Mandelbaum auto Unit = 207f3700bdbSYitzhak Mandelbaum tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 208f3700bdbSYitzhak Mandelbaum auto &Context = Unit->getASTContext(); 209f3700bdbSYitzhak Mandelbaum 210f3700bdbSYitzhak Mandelbaum ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 211f3700bdbSYitzhak Mandelbaum 212f3700bdbSYitzhak Mandelbaum auto Results = 213f3700bdbSYitzhak Mandelbaum match(decl(anyOf(varDecl(hasName("Global")).bind("global"), 214f3700bdbSYitzhak Mandelbaum functionDecl(hasName("Target")).bind("target"))), 215f3700bdbSYitzhak Mandelbaum Context); 216f3700bdbSYitzhak Mandelbaum const auto *Fun = selectFirst<FunctionDecl>("target", Results); 217f3700bdbSYitzhak Mandelbaum const auto *Var = selectFirst<VarDecl>("global", Results); 2183ce03c42SYitzhak Mandelbaum ASSERT_THAT(Fun, NotNull()); 219f3700bdbSYitzhak Mandelbaum ASSERT_THAT(Var, NotNull()); 220f3700bdbSYitzhak Mandelbaum 221f3700bdbSYitzhak Mandelbaum // Verify the global variable is populated when we analyze `Target`. 222f3700bdbSYitzhak Mandelbaum Environment Env(DAContext, *Fun); 22371f2ec2dSmartinboehme Env.initialize(); 2240c852dc8SMartin Braenne EXPECT_THAT(Env.getValue(*Var), NotNull()); 225f3700bdbSYitzhak Mandelbaum } 226f3700bdbSYitzhak Mandelbaum 22773c98831SYitzhak Mandelbaum // Tests that fields mentioned only in default member initializers are included 22873c98831SYitzhak Mandelbaum // in the set of tracked fields. 22973c98831SYitzhak Mandelbaum TEST_F(EnvironmentTest, IncludeFieldsFromDefaultInitializers) { 23073c98831SYitzhak Mandelbaum using namespace ast_matchers; 23173c98831SYitzhak Mandelbaum 23273c98831SYitzhak Mandelbaum std::string Code = R"cc( 23373c98831SYitzhak Mandelbaum struct S { 23473c98831SYitzhak Mandelbaum S() {} 23573c98831SYitzhak Mandelbaum int X = 3; 23673c98831SYitzhak Mandelbaum int Y = X; 23773c98831SYitzhak Mandelbaum }; 23873c98831SYitzhak Mandelbaum S foo(); 23973c98831SYitzhak Mandelbaum )cc"; 24073c98831SYitzhak Mandelbaum 24173c98831SYitzhak Mandelbaum auto Unit = 24273c98831SYitzhak Mandelbaum tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 24373c98831SYitzhak Mandelbaum auto &Context = Unit->getASTContext(); 24473c98831SYitzhak Mandelbaum 24573c98831SYitzhak Mandelbaum ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 24673c98831SYitzhak Mandelbaum 24773c98831SYitzhak Mandelbaum auto Results = match( 24873c98831SYitzhak Mandelbaum qualType(hasDeclaration( 24973c98831SYitzhak Mandelbaum cxxRecordDecl(hasName("S"), 25073c98831SYitzhak Mandelbaum hasMethod(cxxConstructorDecl().bind("target"))) 25173c98831SYitzhak Mandelbaum .bind("struct"))) 25273c98831SYitzhak Mandelbaum .bind("ty"), 25373c98831SYitzhak Mandelbaum Context); 25473c98831SYitzhak Mandelbaum const auto *Constructor = selectFirst<FunctionDecl>("target", Results); 25573c98831SYitzhak Mandelbaum const auto *Rec = selectFirst<RecordDecl>("struct", Results); 25673c98831SYitzhak Mandelbaum const auto QTy = *selectFirst<QualType>("ty", Results); 25773c98831SYitzhak Mandelbaum ASSERT_THAT(Constructor, NotNull()); 25873c98831SYitzhak Mandelbaum ASSERT_THAT(Rec, NotNull()); 25973c98831SYitzhak Mandelbaum ASSERT_FALSE(QTy.isNull()); 26073c98831SYitzhak Mandelbaum 26173c98831SYitzhak Mandelbaum auto Fields = Rec->fields(); 26273c98831SYitzhak Mandelbaum FieldDecl *XDecl = nullptr; 26373c98831SYitzhak Mandelbaum for (FieldDecl *Field : Fields) { 26473c98831SYitzhak Mandelbaum if (Field->getNameAsString() == "X") { 26573c98831SYitzhak Mandelbaum XDecl = Field; 26673c98831SYitzhak Mandelbaum break; 26773c98831SYitzhak Mandelbaum } 26873c98831SYitzhak Mandelbaum } 26973c98831SYitzhak Mandelbaum ASSERT_THAT(XDecl, NotNull()); 27073c98831SYitzhak Mandelbaum 27173c98831SYitzhak Mandelbaum // Verify that the `X` field of `S` is populated when analyzing the 27273c98831SYitzhak Mandelbaum // constructor, even though it is not referenced directly in the constructor. 27373c98831SYitzhak Mandelbaum Environment Env(DAContext, *Constructor); 27471f2ec2dSmartinboehme Env.initialize(); 2757cf20f15Smartinboehme auto &Loc = cast<RecordStorageLocation>(Env.createObject(QTy)); 2767cf20f15Smartinboehme EXPECT_THAT(getFieldValue(&Loc, *XDecl, Env), NotNull()); 27773c98831SYitzhak Mandelbaum } 27873c98831SYitzhak Mandelbaum 2793ce03c42SYitzhak Mandelbaum TEST_F(EnvironmentTest, InitGlobalVarsFieldFun) { 2803ce03c42SYitzhak Mandelbaum using namespace ast_matchers; 2813ce03c42SYitzhak Mandelbaum 2823ce03c42SYitzhak Mandelbaum std::string Code = R"cc( 2833ce03c42SYitzhak Mandelbaum struct S { int Bar; }; 2843ce03c42SYitzhak Mandelbaum S Global = {0}; 2853ce03c42SYitzhak Mandelbaum int Target () { return Global.Bar; } 2863ce03c42SYitzhak Mandelbaum )cc"; 2873ce03c42SYitzhak Mandelbaum 2883ce03c42SYitzhak Mandelbaum auto Unit = 2893ce03c42SYitzhak Mandelbaum tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 2903ce03c42SYitzhak Mandelbaum auto &Context = Unit->getASTContext(); 2913ce03c42SYitzhak Mandelbaum 2923ce03c42SYitzhak Mandelbaum ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 2933ce03c42SYitzhak Mandelbaum 2943ce03c42SYitzhak Mandelbaum auto Results = 2953ce03c42SYitzhak Mandelbaum match(decl(anyOf(varDecl(hasName("Global")).bind("global"), 2963ce03c42SYitzhak Mandelbaum functionDecl(hasName("Target")).bind("target"))), 2973ce03c42SYitzhak Mandelbaum Context); 2983ce03c42SYitzhak Mandelbaum const auto *Fun = selectFirst<FunctionDecl>("target", Results); 2993ce03c42SYitzhak Mandelbaum const auto *GlobalDecl = selectFirst<VarDecl>("global", Results); 3003ce03c42SYitzhak Mandelbaum ASSERT_THAT(Fun, NotNull()); 3013ce03c42SYitzhak Mandelbaum ASSERT_THAT(GlobalDecl, NotNull()); 3023ce03c42SYitzhak Mandelbaum 3033ce03c42SYitzhak Mandelbaum ASSERT_TRUE(GlobalDecl->getType()->isStructureType()); 3043ce03c42SYitzhak Mandelbaum auto GlobalFields = GlobalDecl->getType()->getAsRecordDecl()->fields(); 3053ce03c42SYitzhak Mandelbaum 3063ce03c42SYitzhak Mandelbaum FieldDecl *BarDecl = nullptr; 3073ce03c42SYitzhak Mandelbaum for (FieldDecl *Field : GlobalFields) { 3083ce03c42SYitzhak Mandelbaum if (Field->getNameAsString() == "Bar") { 3093ce03c42SYitzhak Mandelbaum BarDecl = Field; 3103ce03c42SYitzhak Mandelbaum break; 3113ce03c42SYitzhak Mandelbaum } 3123ce03c42SYitzhak Mandelbaum FAIL() << "Unexpected field: " << Field->getNameAsString(); 3133ce03c42SYitzhak Mandelbaum } 3143ce03c42SYitzhak Mandelbaum ASSERT_THAT(BarDecl, NotNull()); 3153ce03c42SYitzhak Mandelbaum 3163ce03c42SYitzhak Mandelbaum // Verify the global variable is populated when we analyze `Target`. 3173ce03c42SYitzhak Mandelbaum Environment Env(DAContext, *Fun); 31871f2ec2dSmartinboehme Env.initialize(); 3199940fac7SMartin Braenne const auto *GlobalLoc = 3209ecdbe38SMartin Braenne cast<RecordStorageLocation>(Env.getStorageLocation(*GlobalDecl)); 3217cf20f15Smartinboehme auto *BarVal = getFieldValue(GlobalLoc, *BarDecl, Env); 3223ce03c42SYitzhak Mandelbaum EXPECT_TRUE(isa<IntegerValue>(BarVal)); 3233ce03c42SYitzhak Mandelbaum } 3243ce03c42SYitzhak Mandelbaum 325f3700bdbSYitzhak Mandelbaum TEST_F(EnvironmentTest, InitGlobalVarsConstructor) { 326f3700bdbSYitzhak Mandelbaum using namespace ast_matchers; 327f3700bdbSYitzhak Mandelbaum 328f3700bdbSYitzhak Mandelbaum std::string Code = R"cc( 329f3700bdbSYitzhak Mandelbaum int Global = 0; 330f3700bdbSYitzhak Mandelbaum struct Target { 331f3700bdbSYitzhak Mandelbaum Target() : Field(Global) {} 332f3700bdbSYitzhak Mandelbaum int Field; 333f3700bdbSYitzhak Mandelbaum }; 334f3700bdbSYitzhak Mandelbaum )cc"; 335f3700bdbSYitzhak Mandelbaum 336f3700bdbSYitzhak Mandelbaum auto Unit = 337f3700bdbSYitzhak Mandelbaum tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 338f3700bdbSYitzhak Mandelbaum auto &Context = Unit->getASTContext(); 339f3700bdbSYitzhak Mandelbaum 340f3700bdbSYitzhak Mandelbaum ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 341f3700bdbSYitzhak Mandelbaum 342f3700bdbSYitzhak Mandelbaum auto Results = 343f3700bdbSYitzhak Mandelbaum match(decl(anyOf( 344f3700bdbSYitzhak Mandelbaum varDecl(hasName("Global")).bind("global"), 345f3700bdbSYitzhak Mandelbaum cxxConstructorDecl(ofClass(hasName("Target"))).bind("target"))), 346f3700bdbSYitzhak Mandelbaum Context); 347f3700bdbSYitzhak Mandelbaum const auto *Ctor = selectFirst<CXXConstructorDecl>("target", Results); 348f3700bdbSYitzhak Mandelbaum const auto *Var = selectFirst<VarDecl>("global", Results); 349f3700bdbSYitzhak Mandelbaum ASSERT_TRUE(Ctor != nullptr); 350f3700bdbSYitzhak Mandelbaum ASSERT_THAT(Var, NotNull()); 351f3700bdbSYitzhak Mandelbaum 352f3700bdbSYitzhak Mandelbaum // Verify the global variable is populated when we analyze `Target`. 353f3700bdbSYitzhak Mandelbaum Environment Env(DAContext, *Ctor); 35471f2ec2dSmartinboehme Env.initialize(); 3550c852dc8SMartin Braenne EXPECT_THAT(Env.getValue(*Var), NotNull()); 356f3700bdbSYitzhak Mandelbaum } 357f3700bdbSYitzhak Mandelbaum 35840381d12SSamira Bazuzi // Pointers to Members are a tricky case of accessor calls, complicated further 35940381d12SSamira Bazuzi // when using templates where the pointer to the member is a template argument. 36040381d12SSamira Bazuzi // This is a repro of a failure case seen in the wild. 36140381d12SSamira Bazuzi TEST_F(EnvironmentTest, 36240381d12SSamira Bazuzi ModelMemberForAccessorUsingMethodPointerThroughTemplate) { 36340381d12SSamira Bazuzi using namespace ast_matchers; 36440381d12SSamira Bazuzi 36540381d12SSamira Bazuzi std::string Code = R"cc( 36640381d12SSamira Bazuzi struct S { 36740381d12SSamira Bazuzi int accessor() {return member;} 36840381d12SSamira Bazuzi 36940381d12SSamira Bazuzi int member = 0; 37040381d12SSamira Bazuzi }; 37140381d12SSamira Bazuzi 37240381d12SSamira Bazuzi template <auto method> 37340381d12SSamira Bazuzi int Target(S* S) { 37440381d12SSamira Bazuzi return (S->*method)(); 37540381d12SSamira Bazuzi } 37640381d12SSamira Bazuzi 37740381d12SSamira Bazuzi // We want to analyze the instantiation of Target for the accessor. 37840381d12SSamira Bazuzi int Instantiator () {S S; return Target<&S::accessor>(&S); } 37940381d12SSamira Bazuzi )cc"; 38040381d12SSamira Bazuzi 38140381d12SSamira Bazuzi auto Unit = 38240381d12SSamira Bazuzi // C++17 for the simplifying use of auto in the template declaration. 38340381d12SSamira Bazuzi tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"}); 38440381d12SSamira Bazuzi auto &Context = Unit->getASTContext(); 38540381d12SSamira Bazuzi 38640381d12SSamira Bazuzi ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 38740381d12SSamira Bazuzi 38840381d12SSamira Bazuzi auto Results = match( 38940381d12SSamira Bazuzi decl(anyOf(functionDecl(hasName("Target"), isTemplateInstantiation()) 39040381d12SSamira Bazuzi .bind("target"), 39140381d12SSamira Bazuzi fieldDecl(hasName("member")).bind("member"), 39240381d12SSamira Bazuzi recordDecl(hasName("S")).bind("struct"))), 39340381d12SSamira Bazuzi Context); 39440381d12SSamira Bazuzi const auto *Fun = selectFirst<FunctionDecl>("target", Results); 39540381d12SSamira Bazuzi const auto *Struct = selectFirst<RecordDecl>("struct", Results); 39640381d12SSamira Bazuzi const auto *Member = selectFirst<FieldDecl>("member", Results); 39740381d12SSamira Bazuzi ASSERT_THAT(Fun, NotNull()); 39840381d12SSamira Bazuzi ASSERT_THAT(Struct, NotNull()); 39940381d12SSamira Bazuzi ASSERT_THAT(Member, NotNull()); 40040381d12SSamira Bazuzi 40140381d12SSamira Bazuzi // Verify that `member` is modeled for `S` when we analyze 40240381d12SSamira Bazuzi // `Target<&S::accessor>`. 40340381d12SSamira Bazuzi Environment Env(DAContext, *Fun); 40440381d12SSamira Bazuzi Env.initialize(); 40540381d12SSamira Bazuzi EXPECT_THAT(DAContext.getModeledFields(QualType(Struct->getTypeForDecl(), 0)), 40640381d12SSamira Bazuzi Contains(Member)); 40740381d12SSamira Bazuzi } 40840381d12SSamira Bazuzi 4093fed312dSSamira Bazuzi // This is a repro of a failure case seen in the wild. 4103fed312dSSamira Bazuzi TEST_F(EnvironmentTest, CXXDefaultInitExprResultObjIsWrappedExprResultObj) { 4113fed312dSSamira Bazuzi using namespace ast_matchers; 4123fed312dSSamira Bazuzi 4133fed312dSSamira Bazuzi std::string Code = R"cc( 4143fed312dSSamira Bazuzi struct Inner {}; 4153fed312dSSamira Bazuzi 4163fed312dSSamira Bazuzi struct S { 4173fed312dSSamira Bazuzi S() {} 4183fed312dSSamira Bazuzi 4193fed312dSSamira Bazuzi Inner i = {}; 4203fed312dSSamira Bazuzi }; 4213fed312dSSamira Bazuzi )cc"; 4223fed312dSSamira Bazuzi 4233fed312dSSamira Bazuzi auto Unit = 4243fed312dSSamira Bazuzi tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 4253fed312dSSamira Bazuzi auto &Context = Unit->getASTContext(); 4263fed312dSSamira Bazuzi 4273fed312dSSamira Bazuzi ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 4283fed312dSSamira Bazuzi 4293fed312dSSamira Bazuzi auto Results = 4303fed312dSSamira Bazuzi match(cxxConstructorDecl( 4313fed312dSSamira Bazuzi hasAnyConstructorInitializer(cxxCtorInitializer( 4323fed312dSSamira Bazuzi withInitializer(expr().bind("default_init_expr"))))) 4333fed312dSSamira Bazuzi .bind("ctor"), 4343fed312dSSamira Bazuzi Context); 4353fed312dSSamira Bazuzi const auto *Constructor = selectFirst<CXXConstructorDecl>("ctor", Results); 4363fed312dSSamira Bazuzi const auto *DefaultInit = 4373fed312dSSamira Bazuzi selectFirst<CXXDefaultInitExpr>("default_init_expr", Results); 4383fed312dSSamira Bazuzi 4393fed312dSSamira Bazuzi Environment Env(DAContext, *Constructor); 4403fed312dSSamira Bazuzi Env.initialize(); 4413fed312dSSamira Bazuzi EXPECT_EQ(&Env.getResultObjectLocation(*DefaultInit), 4423fed312dSSamira Bazuzi &Env.getResultObjectLocation(*DefaultInit->getExpr())); 4433fed312dSSamira Bazuzi } 4443fed312dSSamira Bazuzi 445*49cb1701SPasquale Riello // This test verifies the behavior of `getResultObjectLocation()` in 446*49cb1701SPasquale Riello // scenarios involving inherited constructors. 447*49cb1701SPasquale Riello // Since the specific AST node of interest `CXXConstructorDecl` is implicitly 448*49cb1701SPasquale Riello // generated, we cannot annotate any statements inside of it as we do in tests 449*49cb1701SPasquale Riello // within TransferTest. Thus, the only way to get the right `Environment` is by 450*49cb1701SPasquale Riello // explicitly initializing it as we do in tests within EnvironmentTest. 451*49cb1701SPasquale Riello // This is why this test is not inside TransferTest, where most of the tests for 452*49cb1701SPasquale Riello // `getResultObjectLocation()` are located. 453*49cb1701SPasquale Riello TEST_F(EnvironmentTest, ResultObjectLocationForInheritedCtorInitExpr) { 454*49cb1701SPasquale Riello using namespace ast_matchers; 455*49cb1701SPasquale Riello 456*49cb1701SPasquale Riello std::string Code = R"( 457*49cb1701SPasquale Riello struct Base { 458*49cb1701SPasquale Riello Base(int b) {} 459*49cb1701SPasquale Riello }; 460*49cb1701SPasquale Riello struct Derived : Base { 461*49cb1701SPasquale Riello using Base::Base; 462*49cb1701SPasquale Riello }; 463*49cb1701SPasquale Riello 464*49cb1701SPasquale Riello Derived d = Derived(0); 465*49cb1701SPasquale Riello )"; 466*49cb1701SPasquale Riello 467*49cb1701SPasquale Riello auto Unit = 468*49cb1701SPasquale Riello tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++20"}); 469*49cb1701SPasquale Riello auto &Context = Unit->getASTContext(); 470*49cb1701SPasquale Riello 471*49cb1701SPasquale Riello ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 472*49cb1701SPasquale Riello 473*49cb1701SPasquale Riello auto Results = 474*49cb1701SPasquale Riello match(cxxConstructorDecl( 475*49cb1701SPasquale Riello hasAnyConstructorInitializer(cxxCtorInitializer( 476*49cb1701SPasquale Riello withInitializer(expr().bind("inherited_ctor_init_expr"))))) 477*49cb1701SPasquale Riello .bind("ctor"), 478*49cb1701SPasquale Riello Context); 479*49cb1701SPasquale Riello const auto *Constructor = selectFirst<CXXConstructorDecl>("ctor", Results); 480*49cb1701SPasquale Riello const auto *InheritedCtorInit = selectFirst<CXXInheritedCtorInitExpr>( 481*49cb1701SPasquale Riello "inherited_ctor_init_expr", Results); 482*49cb1701SPasquale Riello 483*49cb1701SPasquale Riello EXPECT_EQ(InheritedCtorInit->child_begin(), InheritedCtorInit->child_end()); 484*49cb1701SPasquale Riello 485*49cb1701SPasquale Riello Environment Env(DAContext, *Constructor); 486*49cb1701SPasquale Riello Env.initialize(); 487*49cb1701SPasquale Riello 488*49cb1701SPasquale Riello RecordStorageLocation &Loc = Env.getResultObjectLocation(*InheritedCtorInit); 489*49cb1701SPasquale Riello EXPECT_NE(&Loc, nullptr); 490*49cb1701SPasquale Riello 491*49cb1701SPasquale Riello EXPECT_EQ(&Loc, Env.getThisPointeeStorageLocation()); 492*49cb1701SPasquale Riello } 493*49cb1701SPasquale Riello 49480d9ae9cSSamira Bazuzi TEST_F(EnvironmentTest, Stmt) { 49580d9ae9cSSamira Bazuzi using namespace ast_matchers; 49680d9ae9cSSamira Bazuzi 49780d9ae9cSSamira Bazuzi std::string Code = R"cc( 49880d9ae9cSSamira Bazuzi struct S { int i; }; 49980d9ae9cSSamira Bazuzi void foo() { 50080d9ae9cSSamira Bazuzi S AnS = S{1}; 50180d9ae9cSSamira Bazuzi } 50280d9ae9cSSamira Bazuzi )cc"; 50380d9ae9cSSamira Bazuzi auto Unit = 50480d9ae9cSSamira Bazuzi tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 50580d9ae9cSSamira Bazuzi auto &Context = Unit->getASTContext(); 50680d9ae9cSSamira Bazuzi 50780d9ae9cSSamira Bazuzi ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 50880d9ae9cSSamira Bazuzi 50980d9ae9cSSamira Bazuzi auto *DeclStatement = const_cast<DeclStmt *>(selectFirst<DeclStmt>( 51080d9ae9cSSamira Bazuzi "d", match(declStmt(hasSingleDecl(varDecl(hasName("AnS")))).bind("d"), 51180d9ae9cSSamira Bazuzi Context))); 51280d9ae9cSSamira Bazuzi ASSERT_THAT(DeclStatement, NotNull()); 51380d9ae9cSSamira Bazuzi auto *Init = (cast<VarDecl>(*DeclStatement->decl_begin()))->getInit(); 51480d9ae9cSSamira Bazuzi ASSERT_THAT(Init, NotNull()); 51580d9ae9cSSamira Bazuzi 51680d9ae9cSSamira Bazuzi // Verify that we can retrieve the result object location for the initializer 51780d9ae9cSSamira Bazuzi // expression when we analyze the DeclStmt for `AnS`. 51880d9ae9cSSamira Bazuzi Environment Env(DAContext, *DeclStatement); 51980d9ae9cSSamira Bazuzi // Don't crash when initializing. 52080d9ae9cSSamira Bazuzi Env.initialize(); 52180d9ae9cSSamira Bazuzi // And don't crash when retrieving the result object location. 52280d9ae9cSSamira Bazuzi Env.getResultObjectLocation(*Init); 52380d9ae9cSSamira Bazuzi } 52480d9ae9cSSamira Bazuzi 52583c2bfdaSSamira Bazuzi // This is a crash repro. 52683c2bfdaSSamira Bazuzi TEST_F(EnvironmentTest, LambdaCapturingThisInFieldInitializer) { 52783c2bfdaSSamira Bazuzi using namespace ast_matchers; 52883c2bfdaSSamira Bazuzi std::string Code = R"cc( 52983c2bfdaSSamira Bazuzi struct S { 53083c2bfdaSSamira Bazuzi int f{[this]() { return 1; }()}; 53183c2bfdaSSamira Bazuzi }; 53283c2bfdaSSamira Bazuzi )cc"; 53383c2bfdaSSamira Bazuzi 53483c2bfdaSSamira Bazuzi auto Unit = 53583c2bfdaSSamira Bazuzi tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 53683c2bfdaSSamira Bazuzi auto &Context = Unit->getASTContext(); 53783c2bfdaSSamira Bazuzi 53883c2bfdaSSamira Bazuzi ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 53983c2bfdaSSamira Bazuzi 54083c2bfdaSSamira Bazuzi auto *LambdaCallOperator = selectFirst<CXXMethodDecl>( 54183c2bfdaSSamira Bazuzi "method", match(cxxMethodDecl(hasName("operator()"), 54283c2bfdaSSamira Bazuzi ofClass(cxxRecordDecl(isLambda()))) 54383c2bfdaSSamira Bazuzi .bind("method"), 54483c2bfdaSSamira Bazuzi Context)); 54583c2bfdaSSamira Bazuzi 54683c2bfdaSSamira Bazuzi Environment Env(DAContext, *LambdaCallOperator); 54783c2bfdaSSamira Bazuzi // Don't crash when initializing. 54883c2bfdaSSamira Bazuzi Env.initialize(); 54983c2bfdaSSamira Bazuzi // And initialize the captured `this` pointee. 55083c2bfdaSSamira Bazuzi ASSERT_NE(nullptr, Env.getThisPointeeStorageLocation()); 55183c2bfdaSSamira Bazuzi } 55283c2bfdaSSamira Bazuzi 553ae60884dSStanislav Gatev } // namespace 554