diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -422,7 +422,7 @@ -
Matches Objective-C instance variable declarations. Example matches _enabled @@ -447,7 +447,7 @@
Matches Objective-C property declarations. Example matches enabled @@ -487,7 +487,7 @@
Matches a C++ static_assert declaration. Example: @@ -762,7 +762,7 @@
Matches binary conditional operator expressions (GNU extension). Example matches a ?: b @@ -935,7 +935,7 @@
Matches a const_cast expression. Example: Matches const_cast<int*>(&r) in @@ -989,7 +989,7 @@
Matches a dynamic_cast expression. Example: @@ -1077,7 +1077,7 @@
Matches a reinterpret_cast expression. Either the source expression or the destination type can be matched @@ -1089,7 +1089,7 @@
Matches a C++ static_cast expression. See also: hasDestinationType @@ -1241,7 +1241,7 @@
Matches expressions that introduce cleanups to be run at the end of the sub-expression's evaluation. @@ -1398,7 +1398,7 @@
Matches Objective-C @catch statements. Example matches @catch @@ -1407,7 +1407,7 @@
Matches Objective-C @finally statements. Example matches @finally @@ -1440,14 +1440,14 @@
Matches Objective-C statements. Example matches @throw obj;
Matches Objective-C @try statements. Example matches @try @@ -1470,7 +1470,7 @@
Matches opaque value expressions. They are used as helpers to reference another expressions and can be met in BinaryConditionalOperators, for example. @@ -2157,6 +2157,19 @@
Matches AST 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: Any Matcher
+Matches if the provided matcher does not match. @@ -7239,7 +7252,7 @@
Matches if the cast's source expression
or opaque value's source expression matches the given matcher.
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: