ymandel updated this revision to Diff 294363. ymandel added a comment. restored changes to unrelated parts of html docs.
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D88275/new/ https://reviews.llvm.org/D88275 Files: clang/docs/LibASTMatchersReference.html clang/include/clang/ASTMatchers/ASTMatchers.h clang/include/clang/ASTMatchers/ASTMatchersInternal.h clang/lib/ASTMatchers/ASTMatchFinder.cpp clang/lib/ASTMatchers/Dynamic/Registry.cpp clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
Index: clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ 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: Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp =================================================================== --- clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ 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); Index: clang/lib/ASTMatchers/ASTMatchFinder.cpp =================================================================== --- clang/lib/ASTMatchers/ASTMatchFinder.cpp +++ 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<clang::Expr>()) + // 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<ImplicitCastExpr>(E) || clang::isa<FullExpr>(E) || + clang::isa<MaterializeTemporaryExpr>(E) || + clang::isa<CXXBindTemporaryExpr>(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 Index: clang/include/clang/ASTMatchers/ASTMatchersInternal.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ 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 <typename T, typename ParentT> +class HasParentIgnoringImplicitMatcher : public MatcherInterface<T> { + static_assert(IsBaseType<ParentT>::value, + "has-parent-ignoring-implicit only accepts base type matcher"); + static_assert(std::is_base_of<Stmt, T>::value, + "has-parent-ignoring-implict only matches statements"); + + const DynTypedMatcher ParentMatcher; + +public: + explicit HasParentIgnoringImplicitMatcher( + const Matcher<ParentT> &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. /// Index: clang/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchers.h +++ clang/include/clang/ASTMatchers/ASTMatchers.h @@ -3176,6 +3176,23 @@ internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc>> 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<Stmt> +extern const internal::ArgumentAdaptingMatcherFunc< + internal::HasParentIgnoringImplicitMatcher, + internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc>, + internal::TypeList<Stmt>> + hasParentIgnoringImplicit; + /// Matches AST nodes that have an ancestor that matches the provided /// matcher. /// Index: clang/docs/LibASTMatchersReference.html =================================================================== --- clang/docs/LibASTMatchersReference.html +++ clang/docs/LibASTMatchersReference.html @@ -4999,6 +4999,19 @@ </pre></td></tr> + +<tr><td>Matcher<*></td><td class="name" onclick="toggle('hasParentIgnoringImplicit0')"><a name="hasParentIgnoringImplicit0Anchor">hasParentIgnoringImplicit</a></td><td>Matcher<*></td></tr> +<tr><td colspan="4" class="doc" id="hasParentIgnoringImplicit0"><pre>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<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>> +</pre></td></tr> + <tr><td>Matcher<*></td><td class="name" onclick="toggle('hasParent0')"><a name="hasParent0Anchor">hasParent</a></td><td>Matcher<*></td></tr> <tr><td colspan="4" class="doc" id="hasParent0"><pre>Matches AST nodes that have a parent that matches the provided matcher. @@ -8011,5 +8024,3 @@ </div> </body> </html> - -
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits