Index: include/clang/ASTMatchers/ASTMatchFinder.h =================================================================== --- include/clang/ASTMatchers/ASTMatchFinder.h +++ include/clang/ASTMatchers/ASTMatchFinder.h @@ -189,6 +189,15 @@ ASTContext &Context); /// @} + /// \brief Finds all matches in the given subtree rooted at \p Node + /// @{ + template void matchSubtree(const T &Node, ASTContext &Context) { + matchSubtree(clang::ast_type_traits::DynTypedNode::create(Node), Context); + } + void matchSubtree(const clang::ast_type_traits::DynTypedNode &Node, + ASTContext &Context); + /// @} + /// Finds all matches in the given AST. void matchAST(ASTContext &Context); Index: lib/ASTMatchers/ASTMatchFinder.cpp =================================================================== --- lib/ASTMatchers/ASTMatchFinder.cpp +++ lib/ASTMatchers/ASTMatchFinder.cpp @@ -1015,6 +1015,32 @@ Visitor.match(Node); } +void MatchFinder::matchSubtree(const clang::ast_type_traits::DynTypedNode &Node, + ASTContext &Context) { + internal::MatchASTVisitor Visitor(&Matchers, Options); + Visitor.set_active_ast_context(&Context); + // FIXME: Improve this with a switch or a visitor pattern. + if (auto *N = Node.get()) { + if (dyn_cast(N)) + Visitor.onStartOfTranslationUnit(); + Visitor.TraverseDecl(const_cast(N)); + if (dyn_cast(N)) + Visitor.onEndOfTranslationUnit(); + } else if (auto *N = Node.get()) { + Visitor.TraverseStmt(const_cast(N)); + } else if (auto *N = Node.get()) { + Visitor.TraverseType(*N); + } else if (auto *N = Node.get()) { + Visitor.TraverseTypeLoc(*N); + } else if (auto *N = Node.get()) { + Visitor.TraverseNestedNameSpecifier(const_cast(N)); + } else if (auto *N = Node.get()) { + Visitor.TraverseNestedNameSpecifierLoc(*N); + } else if (auto *N = Node.get()) { + Visitor.TraverseConstructorInitializer(const_cast(N)); + } +} + void MatchFinder::matchAST(ASTContext &Context) { internal::MatchASTVisitor Visitor(&Matchers, Options); Visitor.set_active_ast_context(&Context); Index: unittests/ASTMatchers/ASTMatchersInternalTest.cpp =================================================================== --- unittests/ASTMatchers/ASTMatchersInternalTest.cpp +++ unittests/ASTMatchers/ASTMatchersInternalTest.cpp @@ -236,5 +236,53 @@ #endif // _WIN32 + +TEST(Matcher, matchSubtree) { + + std::unique_ptr AST = + clang::tooling::buildASTFromCode( + R"( + template + struct X { + void f() {} + void g() {} + }; + void foo() { + X xc; + xc.f(); + X xi; + } + )"); + ASSERT_TRUE(AST.get()); + + // To get the "f" FunctionDecl inside the instantiation ... + auto FullPattern = + functionDecl(hasName("f") + // ... we must specify the parent. + ,hasParent(classTemplateSpecializationDecl())); + auto *FunD = selectFirst( + "dontcare", match(FullPattern.bind("dontcare"), AST->getASTContext())); + + + auto *SpecD = selectFirst( + "dontcare", match(classTemplateSpecializationDecl().bind("dontcare"), + AST->getASTContext())); + MatchFinder Finder; + struct TestCallback : MatchFinder::MatchCallback { + FunctionDecl *Node = nullptr; + void run(const MatchFinder::MatchResult &Result) override { + Node = + const_cast(Result.Nodes.getNodeAs("")); + } + }; + TestCallback Cb; + + // With matchSubtree we can traverse through the given instantiation. + auto SimplePattern = functionDecl(hasName("f")); + Finder.addMatcher(SimplePattern.bind(""), &Cb); + Finder.matchSubtree(*SpecD, AST->getASTContext()); + EXPECT_EQ(FunD, Cb.Node); +} + } // end namespace ast_matchers } // end namespace clang