From 60969f5f6be1cbf7ac2384cc5ad571d28adf6bf1 Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Fri, 1 Feb 2013 13:41:35 +0000 Subject: [PATCH] Re-design the convenience interfaces on MatchFinder. First, this implements a match() method on MatchFinder; this allows us to get rid of the findAll implementation, as findAll is really a special case of recursive matchers on match. Instead of findAll, provide a convenience function match() that lets users iterate easily over the results instead of needing to implement callbacks. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@174172 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/ASTMatchers/ASTMatchFinder.h | 58 +++++++++++++++++++-- lib/ASTMatchers/ASTMatchFinder.cpp | 49 +++++++++--------- unittests/ASTMatchers/ASTMatchersTest.cpp | 59 +++++++++++++--------- 3 files changed, 112 insertions(+), 54 deletions(-) diff --git a/include/clang/ASTMatchers/ASTMatchFinder.h b/include/clang/ASTMatchers/ASTMatchFinder.h index 3d1c13f85c..44e1731646 100644 --- a/include/clang/ASTMatchers/ASTMatchFinder.h +++ b/include/clang/ASTMatchers/ASTMatchFinder.h @@ -134,11 +134,17 @@ public: /// \brief Creates a clang ASTConsumer that finds all matches. clang::ASTConsumer *newASTConsumer(); - /// \brief Finds all matches on the given \c Node. + /// \brief Calls the registered callbacks on all matches on the given \p Node. + /// + /// Note that there can be multiple matches on a single node, for + /// example when using decl(forEachDescendant(stmt())). /// /// @{ - void findAll(const Decl &Node, ASTContext &Context); - void findAll(const Stmt &Node, ASTContext &Context); + template void match(const T &Node, ASTContext &Context) { + match(clang::ast_type_traits::DynTypedNode::create(Node), Context); + } + void match(const clang::ast_type_traits::DynTypedNode &Node, + ASTContext &Context); /// @} /// \brief Registers a callback to notify the end of parsing. @@ -158,6 +164,52 @@ private: ParsingDoneTestCallback *ParsingDone; }; +/// \brief Returns the results of matching \p Matcher on \p Node. +/// +/// Collects the \c BoundNodes of all callback invocations when matching +/// \p Matcher on \p Node and returns the collected results. +/// +/// Multiple results occur when using matchers like \c forEachDescendant, +/// which generate a result for each sub-match. +/// +/// @{ +template +SmallVector +match(MatcherT Matcher, const NodeT &Node, ASTContext &Context); + +template +SmallVector +match(MatcherT Matcher, const ast_type_traits::DynTypedNode &Node, + ASTContext &Context); +/// @} + +namespace internal { +class CollectMatchesCallback : public MatchFinder::MatchCallback { +public: + virtual void run(const MatchFinder::MatchResult &Result) { + Nodes.push_back(Result.Nodes); + } + SmallVector Nodes; +}; +} + +template +SmallVector +match(MatcherT Matcher, const ast_type_traits::DynTypedNode &Node, + ASTContext &Context) { + internal::CollectMatchesCallback Callback; + MatchFinder Finder; + Finder.addMatcher(Matcher, &Callback); + Finder.match(Node, Context); + return Callback.Nodes; +} + +template +SmallVector +match(MatcherT Matcher, const NodeT &Node, ASTContext &Context) { + return match(Matcher, ast_type_traits::DynTypedNode::create(Node), Context); +} + } // end namespace ast_matchers } // end namespace clang diff --git a/lib/ASTMatchers/ASTMatchFinder.cpp b/lib/ASTMatchers/ASTMatchFinder.cpp index 3c797982ad..3a3c1fb6e1 100644 --- a/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/lib/ASTMatchers/ASTMatchFinder.cpp @@ -467,6 +467,26 @@ public: return matchesAncestorOfRecursively(Node, Matcher, Builder, MatchMode); } + // Matches all registered matchers on the given node and calls the + // result callback for every node that matches. + void match(const ast_type_traits::DynTypedNode& Node) { + for (std::vector >::const_iterator + I = MatcherCallbackPairs->begin(), E = MatcherCallbackPairs->end(); + I != E; ++I) { + BoundNodesTreeBuilder Builder; + if (I->first->matches(Node, this, &Builder)) { + BoundNodesTree BoundNodes = Builder.build(); + MatchVisitor Visitor(ActiveASTContext, I->second); + BoundNodes.visitMatches(&Visitor); + } + } + } + + template void match(const T &Node) { + match(ast_type_traits::DynTypedNode::create(Node)); + } + // Implements ASTMatchFinder::getASTContext. virtual ASTContext &getASTContext() const { return *ActiveASTContext; } @@ -544,24 +564,6 @@ private: return false; } - // Matches all registered matchers on the given node and calls the - // result callback for every node that matches. - template - void match(const T &node) { - for (std::vector >::const_iterator - I = MatcherCallbackPairs->begin(), E = MatcherCallbackPairs->end(); - I != E; ++I) { - BoundNodesTreeBuilder Builder; - if (I->first->matches(ast_type_traits::DynTypedNode::create(node), - this, &Builder)) { - BoundNodesTree BoundNodes = Builder.build(); - MatchVisitor Visitor(ActiveASTContext, I->second); - BoundNodes.visitMatches(&Visitor); - } - } - } - std::vector > *const MatcherCallbackPairs; ASTContext *ActiveASTContext; @@ -777,16 +779,11 @@ ASTConsumer *MatchFinder::newASTConsumer() { return new internal::MatchASTConsumer(&MatcherCallbackPairs, ParsingDone); } -void MatchFinder::findAll(const Decl &Node, ASTContext &Context) { - internal::MatchASTVisitor Visitor(&MatcherCallbackPairs); - Visitor.set_active_ast_context(&Context); - Visitor.TraverseDecl(const_cast(&Node)); -} - -void MatchFinder::findAll(const Stmt &Node, ASTContext &Context) { +void MatchFinder::match(const clang::ast_type_traits::DynTypedNode &Node, + ASTContext &Context) { internal::MatchASTVisitor Visitor(&MatcherCallbackPairs); Visitor.set_active_ast_context(&Context); - Visitor.TraverseStmt(const_cast(&Node)); + Visitor.match(Node); } void MatchFinder::registerTestCallbackAfterParsing( diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index 4fad5446ae..670f0d4052 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -3439,24 +3439,18 @@ TEST(NNSLoc, NestedNameSpecifierLocsAsDescendants) { new VerifyIdIsBoundTo("x", 3))); } -template -class VerifyRecursiveMatch : public BoundNodesCallback { +template class VerifyMatchOnNode : public BoundNodesCallback { public: - explicit VerifyRecursiveMatch(StringRef Id, - const internal::Matcher &InnerMatcher) - : Id(Id), InnerMatcher(InnerMatcher) {} - - virtual bool run(const BoundNodes *Nodes) { - return false; + VerifyMatchOnNode(StringRef Id, const internal::Matcher &InnerMatcher) + : Id(Id), InnerMatcher(InnerMatcher) { } + virtual bool run(const BoundNodes *Nodes) { return false; } + virtual bool run(const BoundNodes *Nodes, ASTContext *Context) { const T *Node = Nodes->getNodeAs(Id); - bool Found = false; - MatchFinder Finder; - Finder.addMatcher(InnerMatcher, new VerifyMatch(0, &Found)); - Finder.findAll(*Node, *Context); - return Found; + SmallVector Result = match(InnerMatcher, *Node, *Context); + return !Result.empty(); } private: std::string Id; @@ -3464,21 +3458,36 @@ private: }; TEST(MatchFinder, CanMatchDeclarationsRecursively) { - EXPECT_TRUE(matchAndVerifyResultTrue("class X { class Y {}; };", - recordDecl(hasName("::X")).bind("X"), - new VerifyRecursiveMatch("X", recordDecl(hasName("X::Y"))))); - EXPECT_TRUE(matchAndVerifyResultFalse("class X { class Y {}; };", - recordDecl(hasName("::X")).bind("X"), - new VerifyRecursiveMatch("X", recordDecl(hasName("X::Z"))))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "class X { class Y {}; };", recordDecl(hasName("::X")).bind("X"), + new VerifyMatchOnNode( + "X", decl(hasDescendant(recordDecl(hasName("X::Y"))))))); + EXPECT_TRUE(matchAndVerifyResultFalse( + "class X { class Y {}; };", recordDecl(hasName("::X")).bind("X"), + new VerifyMatchOnNode( + "X", decl(hasDescendant(recordDecl(hasName("X::Z"))))))); } TEST(MatchFinder, CanMatchStatementsRecursively) { - EXPECT_TRUE(matchAndVerifyResultTrue("void f() { if (1) { for (;;) { } } }", - ifStmt().bind("if"), - new VerifyRecursiveMatch("if", forStmt()))); - EXPECT_TRUE(matchAndVerifyResultFalse("void f() { if (1) { for (;;) { } } }", - ifStmt().bind("if"), - new VerifyRecursiveMatch("if", declStmt()))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "void f() { if (1) { for (;;) { } } }", ifStmt().bind("if"), + new VerifyMatchOnNode("if", + stmt(hasDescendant(forStmt()))))); + EXPECT_TRUE(matchAndVerifyResultFalse( + "void f() { if (1) { for (;;) { } } }", ifStmt().bind("if"), + new VerifyMatchOnNode("if", + stmt(hasDescendant(declStmt()))))); +} + +TEST(MatchFinder, CanMatchSingleNodesRecursively) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "class X { class Y {}; };", recordDecl(hasName("::X")).bind("X"), + new VerifyMatchOnNode( + "X", recordDecl(has(recordDecl(hasName("X::Y"))))))); + EXPECT_TRUE(matchAndVerifyResultFalse( + "class X { class Y {}; };", recordDecl(hasName("::X")).bind("X"), + new VerifyMatchOnNode( + "X", recordDecl(has(recordDecl(hasName("X::Z"))))))); } class VerifyStartOfTranslationUnit : public MatchFinder::MatchCallback { -- 2.40.0