diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -250,11 +250,12 @@ } getFieldsAndGlobalVars(*FuncDecl->getBody(), Fields, Vars); - initVars(Vars); - // These have to be set before the lines that follow to ensure that create* - // work correctly for structs. + // These have to be added before the lines that follow to ensure that + // `create*` work correctly for structs. DACtx.addModeledFields(Fields); + initVars(Vars); + for (const auto *ParamDecl : FuncDecl->parameters()) { assert(ParamDecl != nullptr); auto &ParamLoc = createStorageLocation(*ParamDecl); @@ -341,9 +342,13 @@ } } getFieldsAndGlobalVars(*FuncDecl->getBody(), Fields, Vars); - initVars(Vars); + + // These have to be added before the lines that follow to ensure that + // `create*` work correctly for structs. DACtx->addModeledFields(Fields); + initVars(Vars); + const auto *ParamIt = FuncDecl->param_begin(); // FIXME: Parameters don't always map to arguments 1:1; examples include diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -461,6 +461,10 @@ if (Member->isFunctionOrFunctionTemplate()) return; + // FIXME: if/when we add support for modeling enums, use that support here. + if (isa(Member)) + return; + if (auto *D = dyn_cast(Member)) { if (D->hasGlobalStorage()) { auto *VarDeclLoc = Env.getStorageLocation(*D, SkipPast::None); diff --git a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp @@ -118,7 +118,7 @@ Context); const auto *Fun = selectFirst("target", Results); const auto *Var = selectFirst("global", Results); - ASSERT_TRUE(Fun != nullptr); + ASSERT_THAT(Fun, NotNull()); ASSERT_THAT(Var, NotNull()); // Verify the global variable is populated when we analyze `Target`. @@ -126,6 +126,53 @@ EXPECT_THAT(Env.getValue(*Var, SkipPast::None), NotNull()); } +TEST_F(EnvironmentTest, InitGlobalVarsFieldFun) { + using namespace ast_matchers; + + std::string Code = R"cc( + struct S { int Bar; }; + S Global = {0}; + int Target () { return Global.Bar; } + )cc"; + + auto Unit = + tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); + auto &Context = Unit->getASTContext(); + + ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); + + auto Results = + match(decl(anyOf(varDecl(hasName("Global")).bind("global"), + functionDecl(hasName("Target")).bind("target"))), + Context); + const auto *Fun = selectFirst("target", Results); + const auto *GlobalDecl = selectFirst("global", Results); + ASSERT_THAT(Fun, NotNull()); + ASSERT_THAT(GlobalDecl, NotNull()); + + ASSERT_TRUE(GlobalDecl->getType()->isStructureType()); + auto GlobalFields = GlobalDecl->getType()->getAsRecordDecl()->fields(); + + FieldDecl *BarDecl = nullptr; + for (FieldDecl *Field : GlobalFields) { + if (Field->getNameAsString() == "Bar") { + BarDecl = Field; + break; + } + FAIL() << "Unexpected field: " << Field->getNameAsString(); + } + ASSERT_THAT(BarDecl, NotNull()); + + // Verify the global variable is populated when we analyze `Target`. + Environment Env(DAContext, *Fun); + const auto *GlobalLoc = cast( + Env.getStorageLocation(*GlobalDecl, SkipPast::None)); + const auto *GlobalVal = cast(Env.getValue(*GlobalLoc)); + const auto *BarVal = GlobalVal->getChild(*BarDecl); + ASSERT_THAT(BarVal, NotNull()); + EXPECT_TRUE(isa(BarVal)); +} + TEST_F(EnvironmentTest, InitGlobalVarsConstructor) { using namespace ast_matchers; diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -1066,6 +1066,28 @@ }); } +TEST(TransferTest, StructMemberEnum) { + std::string Code = R"( + struct A { + int Bar; + enum E { ONE, TWO }; + }; + + void target(A Foo) { + A::E Baz = Foo.ONE; + // [[p]] + } + )"; + // Minimal expectations -- we're just testing that it doesn't crash, since + // enums aren't interpreted. + runDataflow( + Code, + [](const llvm::StringMap> &Results, + ASTContext &ASTCtx) { + EXPECT_THAT(Results.keys(), UnorderedElementsAre("p")); + }); +} + TEST(TransferTest, DerivedBaseMemberClass) { std::string Code = R"( class A {