aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormartinboehme <mboehme@google.com>2024-04-19 09:06:13 +0200
committerGitHub <noreply@github.com>2024-04-19 09:06:13 +0200
commitca7d9442baf638f020c9594dc2af388d24c4a3e1 (patch)
treee6cafe1d3f84eb28d1a756887acdd95f972c5082
parentaffcaf622d8e900abab1d39d41d9f2d335f32614 (diff)
downloadllvm-ca7d9442baf638f020c9594dc2af388d24c4a3e1.zip
llvm-ca7d9442baf638f020c9594dc2af388d24c4a3e1.tar.gz
llvm-ca7d9442baf638f020c9594dc2af388d24c4a3e1.tar.bz2
[clang][dataflow] Support `CXXParenListInitExpr` in `PropagateResultObject()`. (#89235)
-rw-r--r--clang/include/clang/Analysis/FlowSensitive/ASTOps.h4
-rw-r--r--clang/lib/Analysis/FlowSensitive/ASTOps.cpp35
-rw-r--r--clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp48
-rw-r--r--clang/unittests/Analysis/FlowSensitive/TransferTest.cpp73
4 files changed, 131 insertions, 29 deletions
diff --git a/clang/include/clang/Analysis/FlowSensitive/ASTOps.h b/clang/include/clang/Analysis/FlowSensitive/ASTOps.h
index 27ad32c..f9fd3db 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<std::pair<const CXXBaseSpecifier *, Expr *>> base_inits() const {
@@ -68,6 +69,9 @@ public:
}
private:
+ RecordInitListHelper(QualType Ty, std::vector<const FieldDecl *> Fields,
+ ArrayRef<Expr *> Inits);
+
SmallVector<std::pair<const CXXBaseSpecifier *, Expr *>> BaseInits;
SmallVector<std::pair<const FieldDecl *, Expr *>> FieldInits;
diff --git a/clang/lib/Analysis/FlowSensitive/ASTOps.cpp b/clang/lib/Analysis/FlowSensitive/ASTOps.cpp
index 1982c6c..6f179c1 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 <class InitListT>
static std::vector<const FieldDecl *>
-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<const FieldDecl *> Fields = getFieldsForInitListExpr(InitList);
- ArrayRef<Expr *> Inits = InitList->inits();
+RecordInitListHelper::RecordInitListHelper(
+ QualType Ty, std::vector<const FieldDecl *> Fields,
+ ArrayRef<Expr *> 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<Expr *> 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<CXXParenListInitExpr>(&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 3f1600d..1387734 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -401,6 +401,28 @@ public:
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<RecordStorageLocation>(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 @@ public:
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<RecordStorageLocation>(Loc->getChild(*Field)));
- }
+ if (auto *ParenInitList = dyn_cast<CXXParenListInitExpr>(E)) {
+ PropagateResultObjectToRecordInitList(RecordInitListHelper(ParenInitList),
+ Loc);
return;
}
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index 97ec321..01b8fd0 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<DataflowAnalysisState<NoopLattice>> &Results,
+ ASTContext &ASTCtx) {
+ const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
+
+ auto *Construct = selectFirst<CXXConstructExpr>(
+ "construct",
+ match(
+ cxxConstructExpr(hasType(asString("Inner"))).bind("construct"),
+ ASTCtx));
+
+ EXPECT_EQ(
+ &Env.getResultObjectLocation(*Construct),
+ &getFieldLoc(getLocForDecl<RecordStorageLocation>(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<DataflowAnalysisState<NoopLattice>> &Results,
+ ASTContext &ASTCtx) {
+ const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
+
+ auto *Construct = selectFirst<CXXConstructExpr>(
+ "construct",
+ match(
+ cxxConstructExpr(hasType(asString("Inner"))).bind("construct"),
+ ASTCtx));
+
+ EXPECT_EQ(
+ &Env.getResultObjectLocation(*Construct),
+ &getFieldLoc(getLocForDecl<RecordStorageLocation>(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) {