diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h index 7c1d01ce816e2..5bd2e5518a546 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -644,6 +644,10 @@ getImplicitObjectLocation(const CXXMemberCallExpr &MCE, const Environment &Env); AggregateStorageLocation *getBaseObjectLocation(const MemberExpr &ME, const Environment &Env); +/// Returns the fields of `RD` that are initialized by an `InitListExpr`, in the +/// order in which they appear in `InitListExpr::inits()`. +std::vector getFieldsForInitListExpr(const RecordDecl *RD); + } // namespace dataflow } // namespace clang diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp index 73e20705cff50..9b1fca41e911c 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -215,6 +215,10 @@ getFieldsGlobalsAndFuncs(const Stmt &S, FieldSet &Fields, insertIfFunction(*VD, Funcs); if (const auto *FD = dyn_cast(VD)) Fields.insert(FD); + } else if (auto *InitList = dyn_cast(&S)) { + if (RecordDecl *RD = InitList->getType()->getAsRecordDecl()) + for (const auto *FD : getFieldsForInitListExpr(RD)) + Fields.insert(FD); } } @@ -958,5 +962,17 @@ AggregateStorageLocation *getBaseObjectLocation(const MemberExpr &ME, return cast(Loc); } +std::vector getFieldsForInitListExpr(const RecordDecl *RD) { + // Unnamed bitfields are only used for padding and do not appear in + // `InitListExpr`'s inits. However, those fields do appear in `RecordDecl`'s + // field list, and we thus need to remove them before mapping inits to + // fields to avoid mapping inits to the wrongs fields. + std::vector Fields; + llvm::copy_if( + RD->fields(), std::back_inserter(Fields), + [](const FieldDecl *Field) { return !Field->isUnnamedBitfield(); }); + return Fields; +} + } // namespace dataflow } // namespace clang diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp index ac971f69150f1..e1a2606de8103 100644 --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -715,14 +715,8 @@ class TransferVisitor : public ConstStmtVisitor { Env.setValue(Loc, *Val); if (Type->isStructureOrClassType()) { - // Unnamed bitfields are only used for padding and are not appearing in - // `InitListExpr`'s inits. However, those fields do appear in RecordDecl's - // field list, and we thus need to remove them before mapping inits to - // fields to avoid mapping inits to the wrongs fields. - std::vector Fields; - llvm::copy_if( - Type->getAsRecordDecl()->fields(), std::back_inserter(Fields), - [](const FieldDecl *Field) { return !Field->isUnnamedBitfield(); }); + std::vector Fields = + getFieldsForInitListExpr(Type->getAsRecordDecl()); for (auto It : llvm::zip(Fields, S->inits())) { const FieldDecl *Field = std::get<0>(It); assert(Field != nullptr); diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp index 3ae3106bca467..254226fd6d3ee 100644 --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -2833,6 +2833,7 @@ TEST(TransferTest, AggregateInitialization) { void target(int BarArg, int FooArg, int QuxArg) { B Quux{BarArg, {FooArg}, QuxArg}; + B OtherB; /*[[p]]*/ } )"; @@ -2849,6 +2850,7 @@ TEST(TransferTest, AggregateInitialization) { void target(int BarArg, int FooArg, int QuxArg) { B Quux = {BarArg, FooArg, QuxArg}; + B OtherB; /*[[p]]*/ } )"; @@ -2898,6 +2900,14 @@ TEST(TransferTest, AggregateInitialization) { EXPECT_EQ(getFieldValue(QuuxVal, *BarDecl, Env), BarArgVal); EXPECT_EQ(getFieldValue(BazVal, *FooDecl, Env), FooArgVal); EXPECT_EQ(getFieldValue(QuuxVal, *QuxDecl, Env), QuxArgVal); + + // Check that fields initialized in an initializer list are always + // modeled in other instances of the same type. + const auto &OtherBVal = + getValueForDecl(ASTCtx, Env, "OtherB"); + EXPECT_THAT(OtherBVal.getChild(*BarDecl), NotNull()); + EXPECT_THAT(OtherBVal.getChild(*BazDecl), NotNull()); + EXPECT_THAT(OtherBVal.getChild(*QuxDecl), NotNull()); }); } }