diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -4999,6 +4999,19 @@ + +Matcher<*>hasParentIgnoringImplicitMatcher<*> +
Matches Stmt nodes that have an ancestor, reachable only through
+implicit-expression nodes, that matches the provided matcher.
+
+Given
+float f() { return 3; }
+integerLiteral(hasParentIgnoringImplicit(returnStmt())) matches "3",
+while integerLiteral(hasParent(returnStmt())) does not.
+
+Usable as: Matcher<Stmt>
+
+ Matcher<*>hasParentMatcher<*>
Matches AST nodes that have a parent that matches the provided
 matcher.
@@ -8011,5 +8024,3 @@
 
 
 
-
-
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3176,6 +3176,23 @@
     internal::TypeList>
     hasParent;
 
+/// Matches \c Stmt nodes that have an ancestor, reachable only through
+/// implicit-expression nodes, that matches the provided matcher.
+///
+/// Given
+/// \code
+/// float f() { return 3; }
+/// \endcode
+/// \c integerLiteral(hasParentIgnoringImplicit(returnStmt())) matches "3",
+/// while \c integerLiteral(hasParent(returnStmt())) does not.
+///
+/// Usable as: Matcher
+extern const internal::ArgumentAdaptingMatcherFunc<
+    internal::HasParentIgnoringImplicitMatcher,
+    internal::TypeList,
+    internal::TypeList>
+    hasParentIgnoringImplicit;
+
 /// Matches AST nodes that have an ancestor that matches the provided
 /// matcher.
 ///
diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -985,7 +985,11 @@
     AMM_All,
 
     /// Direct parent only.
-    AMM_ParentOnly
+    AMM_ParentOnly,
+
+    /// Considers the first non-implicit `Expr` ancestor. Intuitively, like
+    /// `ignoringImplicit` for matching parents.
+    AMM_FirstExplicitOnly
   };
 
   virtual ~ASTMatchFinder() = default;
@@ -1515,6 +1519,33 @@
   }
 };
 
+/// Matches \c Stmt nodes that have an ancestor note that is separated from
+/// `Node` only by implicit expression nodes and matches `ParentMatcher`. We
+/// limit to \c Stmt because implicit expressions only ever contain statements
+/// (expressions, in particular).
+///
+/// \c ParentT must be an AST base type.
+template 
+class HasParentIgnoringImplicitMatcher : public MatcherInterface {
+  static_assert(IsBaseType::value,
+                "has-parent-ignoring-implicit only accepts base type matcher");
+  static_assert(std::is_base_of::value,
+                "has-parent-ignoring-implict only matches statements");
+
+  const DynTypedMatcher ParentMatcher;
+
+public:
+  explicit HasParentIgnoringImplicitMatcher(
+      const Matcher &ParentMatcher)
+      : ParentMatcher(ParentMatcher) {}
+
+  bool matches(const T &Node, ASTMatchFinder *Finder,
+               BoundNodesTreeBuilder *Builder) const override {
+    return Finder->matchesAncestorOf(Node, this->ParentMatcher, Builder,
+                                     ASTMatchFinder::AMM_FirstExplicitOnly);
+  }
+};
+
 /// Matches nodes of type \c T that have at least one ancestor node of
 /// type \c AncestorT for which the given inner matcher matches.
 ///
diff --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
--- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
@@ -544,9 +544,14 @@
     // don't invalidate any iterators.
     if (ResultCache.size() > MaxMemoizationEntries)
       ResultCache.clear();
-    if (MatchMode == AncestorMatchMode::AMM_ParentOnly)
+    switch (MatchMode) {
+    case AncestorMatchMode::AMM_ParentOnly:
       return matchesParentOf(Node, Matcher, Builder);
-    return matchesAnyAncestorOf(Node, Ctx, Matcher, Builder);
+    case AncestorMatchMode::AMM_FirstExplicitOnly:
+      return matchesFirstExplicitAncestorOf(Node, Matcher, Builder);
+    case AncestorMatchMode::AMM_All:
+      return matchesAnyAncestorOf(Node, Ctx, Matcher, Builder);
+    }
   }
 
   // Matches all registered matchers on the given node and calls the
@@ -714,6 +719,33 @@
     return false;
   }
 
+  // Returns whether the first explicit (`Expr`) ancestor of \p Node matches \p
+  // Matcher. That is, like matchesParentOf but skipping implicit parents.
+  // Unlike matchesAnyAncestorOf there's no memoization: it doesn't save much.
+  bool matchesFirstExplicitAncestorOf(const DynTypedNode &Node,
+                                      const DynTypedMatcher &Matcher,
+                                      BoundNodesTreeBuilder *Builder) {
+    for (const auto &Parent : ActiveASTContext->getParents(Node)) {
+      if (const auto *E = Parent.get())
+        // If the parent is an implicit node, match on *its* parents
+        // instead. Use DFS, since we expect that expressions are relatively
+        // shallow.
+        if (clang::isa(E) || clang::isa(E) ||
+            clang::isa(E) ||
+            clang::isa(E)) {
+          if (matchesFirstExplicitAncestorOf(Parent, Matcher, Builder))
+            return true;
+          continue;
+        }
+      BoundNodesTreeBuilder BuilderCopy = *Builder;
+      if (Matcher.matches(Parent, this, &BuilderCopy)) {
+        *Builder = std::move(BuilderCopy);
+        return true;
+      }
+    }
+    return false;
+  }
+
   // Returns whether an ancestor of \p Node matches \p Matcher.
   //
   // The order of matching (which can lead to different nodes being bound in
diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -311,6 +311,7 @@
   REGISTER_MATCHER(hasOverloadedOperatorName);
   REGISTER_MATCHER(hasParameter);
   REGISTER_MATCHER(hasParent);
+  REGISTER_MATCHER(hasParentIgnoringImplicit);
   REGISTER_MATCHER(hasQualifier);
   REGISTER_MATCHER(hasRHS);
   REGISTER_MATCHER(hasRangeInit);
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -3190,6 +3190,26 @@
                compoundStmt(hasParent(recordDecl()))));
 }
 
+TEST(HasParentIgnoringImplicit, MatchesExplicitParents) {
+  std::string Input = R"cc(
+    float f() {
+        int x = 3;
+        int y = 3.0;
+        return y;
+    }
+  )cc";
+  EXPECT_TRUE(
+      matches(Input, declRefExpr(hasParentIgnoringImplicit(returnStmt()))));
+  EXPECT_TRUE(
+      matches(Input, floatLiteral(hasParentIgnoringImplicit(varDecl()))));
+  EXPECT_TRUE(
+      matches(Input, integerLiteral(hasParentIgnoringImplicit(varDecl()))));
+
+  // Make sure it only ignores implicit ancestors.
+  EXPECT_TRUE(
+      notMatches(Input, integerLiteral(hasParentIgnoringImplicit(declStmt()))));
+}
+
 TEST(HasParent, NoDuplicateParents) {
   class HasDuplicateParents : public BoundNodesCallback {
   public: