llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-analysis

Author: Jan Voung (jvoung)

<details>
<summary>Changes</summary>

When `this` is under a CXXDefaultInitExpr it could refer to the location of an 
InitListExpr object instead of the `this` of a member function. E.g.:
```
struct S {
  int x;
  int y = this-&gt;x;
};
struct R {
  int foo() {
    // `this` for `a` refers to an R, but `this`
    // for `x` refers to an S.
    return S{this-&gt;a}.y;
  }
  int a;
};
```
Prepares for https://github.com/llvm/llvm-project/issues/128068


---

Patch is 22.37 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/146900.diff


5 Files Affected:

- (modified) clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h 
(+41) 
- (modified) clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp (+134) 
- (modified) clang/lib/Analysis/FlowSensitive/Transfer.cpp (+1-1) 
- (modified) clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp 
(+222) 
- (modified) 
clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp 
(+21) 


``````````diff
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h 
b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
index 097ff2bdfe7ad..e9b85093d8e3e 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -18,6 +18,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
 #include "clang/AST/Type.h"
 #include "clang/Analysis/FlowSensitive/ASTOps.h"
 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
@@ -351,10 +352,34 @@ class Environment {
   /// Returns the storage location assigned to the `this` pointee in the
   /// environment or null if the `this` pointee has no assigned storage 
location
   /// in the environment.
+  /// If you want to look up the storage location for a specific `CXXThisExpr`,
+  /// use the overload that takes a `CXXThisExpr`.
   RecordStorageLocation *getThisPointeeStorageLocation() const {
     return ThisPointeeLoc;
   }
 
+  /// Returns the storage location assigned to the `this` pointee in the
+  /// environment given a specific `CXXThisExpr`. Returns null if the `this`
+  /// pointee has no assigned storage location in the environment.
+  /// Note that `this` can be used in a non-member context, e.g.:
+  ///
+  /// \code
+  ///   struct S {
+  ///     int x;
+  ///     int y = this->x;
+  ///   };
+  ///   int foo() {
+  ///     return S{10}.y;  // will have a `this` for initializing `S::y`.
+  ///   }
+  /// \endcode
+  RecordStorageLocation *
+  getThisPointeeStorageLocation(const CXXThisExpr &ThisExpr) const {
+    auto It = ThisExprOverrides->find(&ThisExpr);
+    if (It == ThisExprOverrides->end())
+      return ThisPointeeLoc;
+    return It->second;
+  }
+
   /// Sets the storage location assigned to the `this` pointee in the
   /// environment.
   void setThisPointeeStorageLocation(RecordStorageLocation &Loc) {
@@ -684,6 +709,8 @@ class Environment {
 private:
   using PrValueToResultObject =
       llvm::DenseMap<const Expr *, RecordStorageLocation *>;
+  using ThisExprOverridesMap =
+      llvm::DenseMap<const CXXThisExpr *, RecordStorageLocation *>;
 
   // The copy-constructor is for use in fork() only.
   Environment(const Environment &) = default;
@@ -747,6 +774,15 @@ class Environment {
                        RecordStorageLocation *ThisPointeeLoc,
                        RecordStorageLocation *LocForRecordReturnVal);
 
+  static ThisExprOverridesMap
+  buildThisExprOverridesMap(const FunctionDecl *FuncDecl,
+                            RecordStorageLocation *ThisPointeeLoc,
+                            const PrValueToResultObject &ResultObjectMap);
+
+  static ThisExprOverridesMap
+  buildThisExprOverridesMap(Stmt *S, RecordStorageLocation *ThisPointeeLoc,
+                            const PrValueToResultObject &ResultObjectMap);
+
   // `DACtx` is not null and not owned by this object.
   DataflowAnalysisContext *DACtx;
 
@@ -793,6 +829,11 @@ class Environment {
   // analysis target is not a method.
   RecordStorageLocation *ThisPointeeLoc = nullptr;
 
+  // Maps from `CXXThisExpr`s to their storage locations, if it should be
+  // different from `ThisPointeeLoc` (for example, CXXThisExpr that are
+  // under a CXXDefaultInitExpr under an InitListExpr).
+  std::shared_ptr<ThisExprOverridesMap> ThisExprOverrides;
+
   // Maps from declarations and glvalue expression to storage locations that 
are
   // assigned to them. Unlike the maps in `DataflowAnalysisContext`, these
   // include only storage locations that are in scope for a particular basic
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp 
b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index 256ea18284189..9c6d40d3e1525 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -15,12 +15,14 @@
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/Type.h"
 #include "clang/Analysis/FlowSensitive/ASTOps.h"
 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
 #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
+#include "clang/Analysis/FlowSensitive/StorageLocation.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/DenseSet.h"
@@ -30,6 +32,7 @@
 #include "llvm/Support/ErrorHandling.h"
 #include <cassert>
 #include <memory>
+#include <stack>
 #include <utility>
 
 #define DEBUG_TYPE "dataflow"
@@ -486,6 +489,97 @@ class ResultObjectVisitor : public AnalysisASTVisitor {
   DataflowAnalysisContext &DACtx;
 };
 
+/// A visitor that finds `CXXThisExpr` that can refer to an object other than
+/// the `this` of a member function.
+class ThisExprOverridesVisitor : public AnalysisASTVisitor {
+  using BaseVisitor = AnalysisASTVisitor;
+
+public:
+  ThisExprOverridesVisitor(
+      RecordStorageLocation *ThisPointeeLoc,
+      const llvm::DenseMap<const Expr *, RecordStorageLocation *>
+          &ResultObjectMap,
+      llvm::DenseMap<const CXXThisExpr *, RecordStorageLocation *>
+          &ThisExprOverrides)
+      : DefaultThisPointeeLoc(ThisPointeeLoc), 
ResultObjectMap(ResultObjectMap),
+        ThisExprOverrides(ThisExprOverrides) {
+    ThisLocations.push(DefaultThisPointeeLoc);
+  }
+
+  void traverseConstructorInits(const CXXConstructorDecl *Ctor) {
+    for (const CXXCtorInitializer *Init : Ctor->inits()) {
+      TraverseStmt(Init->getInit());
+    }
+  }
+
+  bool TraverseInitListExpr(InitListExpr *ILE) override {
+    if (!ILE->isSemanticForm() || ILE->isTransparent()) {
+      BaseVisitor::TraverseInitListExpr(ILE);
+      return true;
+    }
+    bool IsRecordType = ILE->getType()->isRecordType();
+    if (IsRecordType) {
+      auto It = ResultObjectMap.find(ILE);
+      if (It == ResultObjectMap.end()) {
+        llvm_unreachable("InitListExpr not found in ResultObjectMap");
+        return false;
+      }
+      InitListLocations.push(It->second);
+    }
+    BaseVisitor::TraverseInitListExpr(ILE);
+    if (IsRecordType)
+      InitListLocations.pop();
+    return true;
+  }
+
+  bool TraverseCXXParenListInitExpr(CXXParenListInitExpr *PLIE) override {
+    auto It = ResultObjectMap.find(PLIE);
+    if (It == ResultObjectMap.end()) {
+      llvm_unreachable("CXXParenListInitExpr not found in ResultObjectMap");
+      return false;
+    }
+    InitListLocations.push(It->second);
+    BaseVisitor::TraverseCXXParenListInitExpr(PLIE);
+    InitListLocations.pop();
+    return true;
+  }
+
+  bool TraverseCXXDefaultInitExpr(CXXDefaultInitExpr *CDIE) override {
+    bool HasInitListLocations = !InitListLocations.empty();
+    if (HasInitListLocations) {
+      auto *Loc = InitListLocations.top();
+      ThisLocations.push(Loc);
+    }
+    BaseVisitor::TraverseCXXDefaultInitExpr(CDIE);
+    if (HasInitListLocations)
+      ThisLocations.pop();
+    return true;
+  }
+
+  bool TraverseCXXThisExpr(CXXThisExpr *This) override {
+    assert(!ThisLocations.empty());
+    auto *Loc = ThisLocations.top();
+    if (Loc != DefaultThisPointeeLoc)
+      ThisExprOverrides[This] = Loc;
+    return true;
+  }
+
+  // The default `this` pointee location (null if not in a member function).
+  RecordStorageLocation *DefaultThisPointeeLoc;
+  // Locations to use for `this`, with the most recent scope on top.
+  std::stack<RecordStorageLocation *> ThisLocations;
+  // A stack of nested InitListExpr and CXXParenListInitExprs storage
+  // locations that may be used for `this` if we enter a CXXDefaultInitExpr.
+  std::stack<RecordStorageLocation *> InitListLocations;
+  // Map to look up a storage location, e.g., when encountering an
+  // InitListExpr.
+  const llvm::DenseMap<const Expr *, RecordStorageLocation *> &ResultObjectMap;
+  // The visitor will update this map with locations to use for `this`,
+  // if different from `DefaultThisPointeeLoc`.
+  llvm::DenseMap<const CXXThisExpr *, RecordStorageLocation *>
+      &ThisExprOverrides;
+};
+
 } // namespace
 
 void Environment::initialize() {
@@ -498,6 +592,11 @@ void Environment::initialize() {
         std::make_shared<PrValueToResultObject>(buildResultObjectMap(
             DACtx, InitialTargetStmt, getThisPointeeStorageLocation(),
             /*LocForRecordReturnValue=*/nullptr));
+
+    ThisExprOverrides =
+        std::make_shared<ThisExprOverridesMap>(buildThisExprOverridesMap(
+            InitialTargetStmt, getThisPointeeStorageLocation(),
+            *ResultObjectMap));
     return;
   }
 
@@ -559,6 +658,11 @@ void Environment::initialize() {
       std::make_shared<PrValueToResultObject>(buildResultObjectMap(
           DACtx, InitialTargetFunc, getThisPointeeStorageLocation(),
           LocForRecordReturnVal));
+
+  ThisExprOverrides =
+      std::make_shared<ThisExprOverridesMap>(buildThisExprOverridesMap(
+          InitialTargetFunc, getThisPointeeStorageLocation(),
+          *ResultObjectMap));
 }
 
 // FIXME: Add support for resetting globals after function calls to enable the
@@ -659,6 +763,9 @@ void Environment::pushCallInternal(const FunctionDecl 
*FuncDecl,
   ResultObjectMap = std::make_shared<PrValueToResultObject>(
       buildResultObjectMap(DACtx, FuncDecl, getThisPointeeStorageLocation(),
                            LocForRecordReturnVal));
+  ThisExprOverrides =
+      std::make_shared<ThisExprOverridesMap>(buildThisExprOverridesMap(
+          FuncDecl, getThisPointeeStorageLocation(), *ResultObjectMap));
 }
 
 void Environment::popCall(const CallExpr *Call, const Environment &CalleeEnv) {
@@ -726,6 +833,7 @@ LatticeEffect Environment::widen(const Environment &PrevEnv,
   assert(ReturnLoc == PrevEnv.ReturnLoc);
   assert(LocForRecordReturnVal == PrevEnv.LocForRecordReturnVal);
   assert(ThisPointeeLoc == PrevEnv.ThisPointeeLoc);
+  assert(ThisExprOverrides == PrevEnv.ThisExprOverrides);
   assert(CallStack == PrevEnv.CallStack);
   assert(ResultObjectMap == PrevEnv.ResultObjectMap);
   assert(InitialTargetFunc == PrevEnv.InitialTargetFunc);
@@ -763,6 +871,7 @@ Environment Environment::join(const Environment &EnvA, 
const Environment &EnvB,
   assert(EnvA.DACtx == EnvB.DACtx);
   assert(EnvA.LocForRecordReturnVal == EnvB.LocForRecordReturnVal);
   assert(EnvA.ThisPointeeLoc == EnvB.ThisPointeeLoc);
+  assert(EnvA.ThisExprOverrides == EnvB.ThisExprOverrides);
   assert(EnvA.CallStack == EnvB.CallStack);
   assert(EnvA.ResultObjectMap == EnvB.ResultObjectMap);
   assert(EnvA.InitialTargetFunc == EnvB.InitialTargetFunc);
@@ -774,6 +883,7 @@ Environment Environment::join(const Environment &EnvA, 
const Environment &EnvB,
   JoinedEnv.ResultObjectMap = EnvA.ResultObjectMap;
   JoinedEnv.LocForRecordReturnVal = EnvA.LocForRecordReturnVal;
   JoinedEnv.ThisPointeeLoc = EnvA.ThisPointeeLoc;
+  JoinedEnv.ThisExprOverrides = EnvA.ThisExprOverrides;
   JoinedEnv.InitialTargetFunc = EnvA.InitialTargetFunc;
   JoinedEnv.InitialTargetStmt = EnvA.InitialTargetStmt;
 
@@ -1225,6 +1335,30 @@ Environment::PrValueToResultObject 
Environment::buildResultObjectMap(
   return Map;
 }
 
+Environment::ThisExprOverridesMap Environment::buildThisExprOverridesMap(
+    const FunctionDecl *FuncDecl, RecordStorageLocation *ThisPointeeLoc,
+    const PrValueToResultObject &ResultObjectMap) {
+  assert(FuncDecl->doesThisDeclarationHaveABody());
+
+  ThisExprOverridesMap Map = buildThisExprOverridesMap(
+      FuncDecl->getBody(), ThisPointeeLoc, ResultObjectMap);
+
+  ThisExprOverridesVisitor Visitor(ThisPointeeLoc, ResultObjectMap, Map);
+  if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FuncDecl)) {
+    Visitor.traverseConstructorInits(Ctor);
+  }
+  return Map;
+}
+
+Environment::ThisExprOverridesMap Environment::buildThisExprOverridesMap(
+    Stmt *S, RecordStorageLocation *ThisPointeeLoc,
+    const PrValueToResultObject &ResultObjectMap) {
+  ThisExprOverridesMap Map;
+  ThisExprOverridesVisitor Visitor(ThisPointeeLoc, ResultObjectMap, Map);
+  Visitor.TraverseStmt(S);
+  return Map;
+}
+
 RecordStorageLocation *getImplicitObjectLocation(const CXXMemberCallExpr &MCE,
                                                  const Environment &Env) {
   Expr *ImplicitObject = MCE.getImplicitObjectArgument();
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp 
b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index 86a816e2e406c..bafbc61de3e29 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -419,7 +419,7 @@ class TransferVisitor : public 
ConstStmtVisitor<TransferVisitor> {
   }
 
   void VisitCXXThisExpr(const CXXThisExpr *S) {
-    auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation();
+    auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation(*S);
     if (ThisPointeeLoc == nullptr)
       // Unions are not supported yet, and will not have a location for the
       // `this` expression's pointee.
diff --git a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
index 737277e167edd..090edef1a610b 100644
--- a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
@@ -8,7 +8,9 @@
 
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "TestingSupport.h"
+#include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/Stmt.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
@@ -550,4 +552,224 @@ TEST_F(EnvironmentTest, 
LambdaCapturingThisInFieldInitializer) {
   ASSERT_NE(nullptr, Env.getThisPointeeStorageLocation());
 }
 
+TEST_F(EnvironmentTest, ThisExprLocInNonMemberIsInitListLoc) {
+  using namespace ast_matchers;
+  std::string Code = R"cc(
+      struct Other {
+        int i = 0;
+        int j = this->i;
+      };
+      void target(int x) {
+        Other o = {x};
+      }
+    )cc";
+
+  auto Unit =
+      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
+  auto &Context = Unit->getASTContext();
+
+  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
+
+  auto *Func = selectFirst<FunctionDecl>(
+      "func", match(functionDecl(hasName("target")).bind("func"), Context));
+  ASSERT_NE(Func, nullptr);
+
+  auto Results = match(
+      initListExpr(
+          hasInit(1, hasDescendant(memberExpr(
+                         hasObjectExpression(cxxThisExpr().bind("this_i")),
+                         member(fieldDecl(hasName("i")))))))
+          .bind("init_list"),
+      Context);
+
+  auto *ThisForI = selectFirst<CXXThisExpr>("this_i", Results);
+  ASSERT_NE(ThisForI, nullptr);
+  auto *InitList = selectFirst<InitListExpr>("init_list", Results);
+  ASSERT_NE(InitList, nullptr);
+
+  Environment Env(DAContext, *Func);
+  Env.initialize();
+  auto *DefaultThis = Env.getThisPointeeStorageLocation();
+  EXPECT_EQ(DefaultThis, nullptr);
+
+  RecordStorageLocation &InitListLoc = Env.getResultObjectLocation(*InitList);
+  EXPECT_EQ(&InitListLoc, Env.getThisPointeeStorageLocation(*ThisForI));
+}
+
+TEST_F(EnvironmentTest, ThisExprLocInNonMemberIsParenListInitLoc) {
+  using namespace ast_matchers;
+  std::string Code = R"cc(
+      struct Other {
+        int i = 0;
+        int j = this->i;
+      };
+      void target(int x) {
+        Other o(x);
+      }
+    )cc";
+
+  auto Unit =
+      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++20"});
+  auto &Context = Unit->getASTContext();
+
+  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
+
+  auto *Func = selectFirst<FunctionDecl>(
+      "func", match(functionDecl(hasName("target")).bind("func"), Context));
+  ASSERT_NE(Func, nullptr);
+
+  const ast_matchers::internal::VariadicDynCastAllOfMatcher<
+      Stmt, CXXParenListInitExpr>
+      cxxParenListInitExpr;
+
+  auto Results =
+      match(cxxParenListInitExpr(
+                hasDescendant(memberExpr(
+                    hasObjectExpression(cxxThisExpr().bind("this_i")),
+                    member(fieldDecl(hasName("i"))))))
+                .bind("init_list"),
+            Context);
+
+  auto *ThisForI = selectFirst<CXXThisExpr>("this_i", Results);
+  ASSERT_NE(ThisForI, nullptr);
+  auto *InitList = selectFirst<CXXParenListInitExpr>("init_list", Results);
+  ASSERT_NE(InitList, nullptr);
+
+  Environment Env(DAContext, *Func);
+  Env.initialize();
+  auto *DefaultThis = Env.getThisPointeeStorageLocation();
+  EXPECT_EQ(DefaultThis, nullptr);
+
+  RecordStorageLocation &InitListLoc = Env.getResultObjectLocation(*InitList);
+  EXPECT_EQ(&InitListLoc, Env.getThisPointeeStorageLocation(*ThisForI));
+}
+
+TEST_F(EnvironmentTest, ThisExprLocInMemberIsInitListLoc) {
+  using namespace ast_matchers;
+  std::string Code = R"cc(
+      struct Other {
+        int i = 0;
+        int j = this->i;
+      };
+      struct Foo {
+        void target() {
+          Other o = {this->x};
+        }
+        int x = 0;
+      };
+    )cc";
+
+  auto Unit =
+      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
+  auto &Context = Unit->getASTContext();
+
+  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
+
+  auto *Method = selectFirst<CXXMethodDecl>(
+      "method",
+      match(cxxMethodDecl(hasName("target")).bind("method"), Context));
+  ASSERT_NE(Method, nullptr);
+
+  auto Results = match(
+      initListExpr(
+          hasInit(0, hasDescendant(memberExpr(
+                         hasObjectExpression(cxxThisExpr().bind("this_x")),
+                         member(fieldDecl(hasName("x")))))),
+          hasInit(1, hasDescendant(memberExpr(
+                         hasObjectExpression(cxxThisExpr().bind("this_i")),
+                         member(fieldDecl(hasName("i")))))))
+          .bind("init_list"),
+      Context);
+
+  auto *ThisForX = selectFirst<CXXThisExpr>("this_x", Results);
+  ASSERT_NE(ThisForX, nullptr);
+  auto *ThisForI = selectFirst<CXXThisExpr>("this_i", Results);
+  ASSERT_NE(ThisForI, nullptr);
+  auto *InitList = selectFirst<InitListExpr>("init_list", Results);
+  ASSERT_NE(InitList, nullptr);
+
+  Environment Env(DAContext, *Method);
+  Env.initialize();
+  auto *DefaultThis = Env.getThisPointeeStorageLocation();
+  EXPECT_NE(DefaultThis, nullptr);
+
+  EXPECT_EQ(DefaultThis, Env.getThisPointeeStorageLocation(*ThisForX));
+  RecordStorageLocation &InitListLoc = Env.getResultObjectLocation(*InitList);
+  EXPECT_EQ(&InitListLoc, Env.getThisPointeeStorageLocation(*ThisForI));
+}
+
+TEST_F(EnvironmentTest, ThisExprLocInCtorInitializerIsInitListLoc) {
+  using namespace ast_matchers;
+  std::string Code = R"cc(
+      struct B {
+        int a = 0;
+        int b = this->a;
+      };
+      struct Other {
+        int i = 0;
+        B b = {this->i};
+      };
+      struct target {
+        target() : x(-1) {}
+        int x;
+        Other o = {this->x};
+      };
+    )cc";
+
+  auto Unit =
+      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
+  auto &Context = Unit->getASTContext();
+
+  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
+
+  auto *Ctor = selectFirst<CXXConstructorDecl>(
+      "ctor",
+      match(cxxConstructorDecl(hasName("target")).bind("ctor"), Context));
+  ASSERT_NE(Ctor, nullptr);
+  Ctor->dump();
+
+  auto Results = match(
+      initListExpr(
+          hasInit(0, hasDescendant(memberExpr(
+                         hasObjectExpression(cxxThisExpr().bind("this_x")),
+                         member(fieldDecl(hasName("x")))))),
+          hasInit(1, hasDescendant(
+                         initListExpr(
+                             hasInit(0, hasDescendant(memberExpr(
+                                            hasObjectExpression(
+                                                cxxThisExpr().bind("this_i")),
+                                            member(fieldDecl(hasName("i")))))),
+                             hasInit(1, hasDescendant(memberExpr(
+                                            hasObjectExpression(
+                                                cxxThisExpr().bind("this_a")),
+                       ...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/146900
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to