diff --git a/clang/include/clang/Analysis/FlowSensitive/Value.h b/clang/include/clang/Analysis/FlowSensitive/Value.h --- a/clang/include/clang/Analysis/FlowSensitive/Value.h +++ b/clang/include/clang/Analysis/FlowSensitive/Value.h @@ -100,15 +100,18 @@ return Val->getKind() == Kind::Struct; } - /// Returns the child value for `D`. + /// Returns the child value that is assigned for `D`. Value &getChild(const ValueDecl &D) const { auto It = Children.find(&D); assert(It != Children.end()); return *It->second; } + /// Assigns `Val` as the child value for `D`. + void setChild(const ValueDecl &D, Value &Val) { Children[&D] = &Val; } + private: - const llvm::DenseMap Children; + llvm::DenseMap Children; }; } // namespace dataflow 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 @@ -22,9 +22,11 @@ #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/Basic/OperatorKinds.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/Casting.h" #include #include +#include namespace clang { namespace dataflow { @@ -414,6 +416,28 @@ Env.setValue(Loc, *Val); } + void VisitInitListExpr(const InitListExpr *S) { + QualType Type = S->getType(); + assert(Type->isStructureOrClassType()); + + auto &Loc = Env.createStorageLocation(*S); + Env.setStorageLocation(*S, Loc); + + auto *Val = cast(Env.createValue(Type)); + Env.setValue(Loc, *Val); + + for (auto IT : llvm::zip(Type->getAsRecordDecl()->fields(), S->inits())) { + const FieldDecl *Field = std::get<0>(IT); + assert(Field != nullptr); + + const Expr *Init = std::get<1>(IT); + assert(Init != nullptr); + + if (Value *InitVal = Env.getValue(*Init, SkipPast::None)) + Val->setChild(*Field, *InitVal); + } + } + // FIXME: Add support for: // - CXXBoolLiteralExpr 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 @@ -1865,4 +1865,91 @@ }); } +TEST_F(TransferTest, AggregateInitialization) { + std::string BracesCode = R"( + struct A { + int Foo; + }; + + struct B { + int Bar; + A Baz; + int Qux; + }; + + void target(int BarArg, int FooArg, int QuxArg) { + B Quux{BarArg, {FooArg}, QuxArg}; + /*[[p]]*/ + } + )"; + std::string BraceEllisionCode = R"( + struct A { + int Foo; + }; + + struct B { + int Bar; + A Baz; + int Qux; + }; + + void target(int BarArg, int FooArg, int QuxArg) { + B Quux = {BarArg, FooArg, QuxArg}; + /*[[p]]*/ + } + )"; + for (const std::string &Code : {BracesCode, BraceEllisionCode}) { + runDataflow( + Code, [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz"); + ASSERT_THAT(BazDecl, NotNull()); + + const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux"); + ASSERT_THAT(QuxDecl, NotNull()); + + const ValueDecl *FooArgDecl = findValueDecl(ASTCtx, "FooArg"); + ASSERT_THAT(FooArgDecl, NotNull()); + + const ValueDecl *BarArgDecl = findValueDecl(ASTCtx, "BarArg"); + ASSERT_THAT(BarArgDecl, NotNull()); + + const ValueDecl *QuxArgDecl = findValueDecl(ASTCtx, "QuxArg"); + ASSERT_THAT(QuxArgDecl, NotNull()); + + const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, "Quux"); + ASSERT_THAT(QuuxDecl, NotNull()); + + const auto *FooArgVal = + cast(Env.getValue(*FooArgDecl, SkipPast::None)); + const auto *BarArgVal = + cast(Env.getValue(*BarArgDecl, SkipPast::None)); + const auto *QuxArgVal = + cast(Env.getValue(*QuxArgDecl, SkipPast::None)); + + const auto *QuuxVal = + cast(Env.getValue(*QuuxDecl, SkipPast::None)); + ASSERT_THAT(QuuxVal, NotNull()); + + const auto *BazVal = cast(&QuuxVal->getChild(*BazDecl)); + ASSERT_THAT(BazVal, NotNull()); + + EXPECT_EQ(&QuuxVal->getChild(*BarDecl), BarArgVal); + EXPECT_EQ(&BazVal->getChild(*FooDecl), FooArgVal); + EXPECT_EQ(&QuuxVal->getChild(*QuxDecl), QuxArgVal); + }); + } +} + } // namespace