diff --git a/clang/include/clang/Analysis/FlowSensitive/ASTOps.h b/clang/include/clang/Analysis/FlowSensitive/ASTOps.h index 27ad32c1694f7..f9fd3db1fb67f 100644 --- a/clang/include/clang/Analysis/FlowSensitive/ASTOps.h +++ b/clang/include/clang/Analysis/FlowSensitive/ASTOps.h @@ -56,6 +56,7 @@ class RecordInitListHelper { public: // `InitList` must have record type. RecordInitListHelper(const InitListExpr *InitList); + RecordInitListHelper(const CXXParenListInitExpr *ParenInitList); // Base classes with their associated initializer expressions. ArrayRef> base_inits() const { @@ -68,6 +69,9 @@ class RecordInitListHelper { } private: + RecordInitListHelper(QualType Ty, std::vector Fields, + ArrayRef Inits); + SmallVector> BaseInits; SmallVector> FieldInits; diff --git a/clang/lib/Analysis/FlowSensitive/ASTOps.cpp b/clang/lib/Analysis/FlowSensitive/ASTOps.cpp index 1982c6c9f3830..6f179c1403b6f 100644 --- a/clang/lib/Analysis/FlowSensitive/ASTOps.cpp +++ b/clang/lib/Analysis/FlowSensitive/ASTOps.cpp @@ -80,11 +80,12 @@ bool containsSameFields(const FieldSet &Fields, } /// Returns the fields of a `RecordDecl` that are initialized by an -/// `InitListExpr`, in the order in which they appear in -/// `InitListExpr::inits()`. -/// `Init->getType()` must be a record type. +/// `InitListExpr` or `CXXParenListInitExpr`, in the order in which they appear +/// in `InitListExpr::inits()` / `CXXParenListInitExpr::getInitExprs()`. +/// `InitList->getType()` must be a record type. +template static std::vector -getFieldsForInitListExpr(const InitListExpr *InitList) { +getFieldsForInitListExpr(const InitListT *InitList) { const RecordDecl *RD = InitList->getType()->getAsRecordDecl(); assert(RD != nullptr); @@ -105,19 +106,29 @@ getFieldsForInitListExpr(const InitListExpr *InitList) { return Fields; } -RecordInitListHelper::RecordInitListHelper(const InitListExpr *InitList) { - auto *RD = InitList->getType()->getAsCXXRecordDecl(); - assert(RD != nullptr); +RecordInitListHelper::RecordInitListHelper(const InitListExpr *InitList) + : RecordInitListHelper(InitList->getType(), + getFieldsForInitListExpr(InitList), + InitList->inits()) {} + +RecordInitListHelper::RecordInitListHelper( + const CXXParenListInitExpr *ParenInitList) + : RecordInitListHelper(ParenInitList->getType(), + getFieldsForInitListExpr(ParenInitList), + ParenInitList->getInitExprs()) {} - std::vector Fields = getFieldsForInitListExpr(InitList); - ArrayRef Inits = InitList->inits(); +RecordInitListHelper::RecordInitListHelper( + QualType Ty, std::vector Fields, + ArrayRef Inits) { + auto *RD = Ty->getAsCXXRecordDecl(); + assert(RD != nullptr); // Unions initialized with an empty initializer list need special treatment. // For structs/classes initialized with an empty initializer list, Clang // puts `ImplicitValueInitExpr`s in `InitListExpr::inits()`, but for unions, // it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves. SmallVector InitsForUnion; - if (InitList->getType()->isUnionType() && Inits.empty()) { + if (Ty->isUnionType() && Inits.empty()) { assert(Fields.size() == 1); ImplicitValueInitForUnion.emplace(Fields.front()->getType()); InitsForUnion.push_back(&*ImplicitValueInitForUnion); @@ -217,6 +228,10 @@ static void getReferencedDecls(const Stmt &S, ReferencedDecls &Referenced) { if (InitList->getType()->isRecordType()) for (const auto *FD : getFieldsForInitListExpr(InitList)) Referenced.Fields.insert(FD); + } else if (auto *ParenInitList = dyn_cast(&S)) { + if (ParenInitList->getType()->isRecordType()) + for (const auto *FD : getFieldsForInitListExpr(ParenInitList)) + Referenced.Fields.insert(FD); } } diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp index 3f1600d9ac5d8..138773460dd74 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -401,6 +401,28 @@ class ResultObjectVisitor : public RecursiveASTVisitor { return true; } + void + PropagateResultObjectToRecordInitList(const RecordInitListHelper &InitList, + RecordStorageLocation *Loc) { + for (auto [Base, Init] : InitList.base_inits()) { + assert(Base->getType().getCanonicalType() == + Init->getType().getCanonicalType()); + + // Storage location for the base class is the same as that of the + // derived class because we "flatten" the object hierarchy and put all + // fields in `RecordStorageLocation` of the derived class. + PropagateResultObject(Init, Loc); + } + + for (auto [Field, Init] : InitList.field_inits()) { + // Fields of non-record type are handled in + // `TransferVisitor::VisitInitListExpr()`. + if (Field->getType()->isRecordType()) + PropagateResultObject(Init, + cast(Loc->getChild(*Field))); + } + } + // Assigns `Loc` as the result object location of `E`, then propagates the // location to all lower-level prvalues that initialize the same object as // `E` (or one of its base classes or member variables). @@ -440,26 +462,14 @@ class ResultObjectVisitor : public RecursiveASTVisitor { return; } - RecordInitListHelper InitListHelper(InitList); - - for (auto [Base, Init] : InitListHelper.base_inits()) { - assert(Base->getType().getCanonicalType() == - Init->getType().getCanonicalType()); - - // Storage location for the base class is the same as that of the - // derived class because we "flatten" the object hierarchy and put all - // fields in `RecordStorageLocation` of the derived class. - PropagateResultObject(Init, Loc); - } + PropagateResultObjectToRecordInitList(RecordInitListHelper(InitList), + Loc); + return; + } - for (auto [Field, Init] : InitListHelper.field_inits()) { - // Fields of non-record type are handled in - // `TransferVisitor::VisitInitListExpr()`. - if (!Field->getType()->isRecordType()) - continue; - PropagateResultObject( - Init, cast(Loc->getChild(*Field))); - } + if (auto *ParenInitList = dyn_cast(E)) { + PropagateResultObjectToRecordInitList(RecordInitListHelper(ParenInitList), + Loc); return; } diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp index 97ec32126c1dc..01b8fd0df2351 100644 --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -3098,6 +3098,79 @@ TEST(TransferTest, ResultObjectLocationForCXXOperatorCallExpr) { }); } +TEST(TransferTest, ResultObjectLocationForInitListExpr) { + std::string Code = R"cc( + struct Inner {}; + + struct Outer { Inner I; }; + + void target() { + Outer O = { Inner() }; + // [[p]] + } + )cc"; + using ast_matchers::asString; + using ast_matchers::cxxConstructExpr; + using ast_matchers::hasType; + using ast_matchers::match; + using ast_matchers::selectFirst; + using ast_matchers::traverse; + runDataflow( + Code, + [](const llvm::StringMap> &Results, + ASTContext &ASTCtx) { + const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); + + auto *Construct = selectFirst( + "construct", + match( + cxxConstructExpr(hasType(asString("Inner"))).bind("construct"), + ASTCtx)); + + EXPECT_EQ( + &Env.getResultObjectLocation(*Construct), + &getFieldLoc(getLocForDecl(ASTCtx, Env, "O"), + "I", ASTCtx)); + }); +} + +TEST(TransferTest, ResultObjectLocationForParenInitListExpr) { + std::string Code = R"cc( + struct Inner {}; + + struct Outer { Inner I; }; + + void target() { + Outer O((Inner())); + // [[p]] + } + )cc"; + using ast_matchers::asString; + using ast_matchers::cxxConstructExpr; + using ast_matchers::hasType; + using ast_matchers::match; + using ast_matchers::selectFirst; + using ast_matchers::traverse; + runDataflow( + Code, + [](const llvm::StringMap> &Results, + ASTContext &ASTCtx) { + const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); + + auto *Construct = selectFirst( + "construct", + match( + cxxConstructExpr(hasType(asString("Inner"))).bind("construct"), + ASTCtx)); + + EXPECT_EQ( + &Env.getResultObjectLocation(*Construct), + &getFieldLoc(getLocForDecl(ASTCtx, Env, "O"), + "I", ASTCtx)); + }, + LangStandard::lang_cxx20); +} + // Check that the `std::strong_ordering` object returned by builtin `<=>` has a // correctly modeled result object location. TEST(TransferTest, ResultObjectLocationForBuiltinSpaceshipOperator) {