xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp (revision 49cb1701389ac3cff3e2c552531bfd3a607311d8)
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