From cf87bffc346244f0ed8eae7fffb52a0f03cd0413 Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Wed, 6 Feb 2013 10:33:21 +0000 Subject: [PATCH] Adds a convenience function selectFirst to simplify matching. A very common use case is to search for the first occurrence of a certain node that is a descendant of another node. In that case, selectFirst significantly simplifies the code at the client side. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@174499 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/ASTMatchers/ASTMatchFinder.h | 23 ++++++++++++++++++ unittests/ASTMatchers/ASTMatchersTest.cpp | 28 ++++++++++++---------- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/include/clang/ASTMatchers/ASTMatchFinder.h b/include/clang/ASTMatchers/ASTMatchFinder.h index 44e1731646..870a39b391 100644 --- a/include/clang/ASTMatchers/ASTMatchFinder.h +++ b/include/clang/ASTMatchers/ASTMatchFinder.h @@ -172,6 +172,7 @@ private: /// Multiple results occur when using matchers like \c forEachDescendant, /// which generate a result for each sub-match. /// +/// \see selectFirst /// @{ template SmallVector @@ -183,6 +184,28 @@ match(MatcherT Matcher, const ast_type_traits::DynTypedNode &Node, ASTContext &Context); /// @} +/// \brief Returns the first result of type \c NodeT bound to \p BoundTo. +/// +/// Returns \c NULL if there is no match, or if the matching node cannot be +/// casted to \c NodeT. +/// +/// This is useful in combanation with \c match(): +/// \code +/// Decl *D = selectFirst("id", match(Matcher.bind("id"), +/// Node, Context)); +/// \endcode +template +NodeT * +selectFirst(StringRef BoundTo, const SmallVectorImpl &Results) { + for (SmallVectorImpl::const_iterator I = Results.begin(), + E = Results.end(); + I != E; ++I) { + if (NodeT *Node = I->getNodeAs(BoundTo)) + return Node; + } + return NULL; +} + namespace internal { class CollectMatchesCallback : public MatchFinder::MatchCallback { public: diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index 8668b4970e..60a79e86de 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -3493,53 +3493,57 @@ TEST(NNSLoc, NestedNameSpecifierLocsAsDescendants) { template class VerifyMatchOnNode : public BoundNodesCallback { public: - VerifyMatchOnNode(StringRef Id, const internal::Matcher &InnerMatcher) - : Id(Id), InnerMatcher(InnerMatcher) { + VerifyMatchOnNode(StringRef Id, const internal::Matcher &InnerMatcher, + StringRef InnerId) + : Id(Id), InnerMatcher(InnerMatcher), InnerId(InnerId) { } virtual bool run(const BoundNodes *Nodes) { return false; } virtual bool run(const BoundNodes *Nodes, ASTContext *Context) { const T *Node = Nodes->getNodeAs(Id); - SmallVector Result = match(InnerMatcher, *Node, *Context); - return !Result.empty(); + return selectFirst(InnerId, + match(InnerMatcher, *Node, *Context)) != NULL; } private: std::string Id; internal::Matcher InnerMatcher; + std::string InnerId; }; TEST(MatchFinder, CanMatchDeclarationsRecursively) { EXPECT_TRUE(matchAndVerifyResultTrue( "class X { class Y {}; };", recordDecl(hasName("::X")).bind("X"), new VerifyMatchOnNode( - "X", decl(hasDescendant(recordDecl(hasName("X::Y"))))))); + "X", decl(hasDescendant(recordDecl(hasName("X::Y")).bind("Y"))), + "Y"))); EXPECT_TRUE(matchAndVerifyResultFalse( "class X { class Y {}; };", recordDecl(hasName("::X")).bind("X"), new VerifyMatchOnNode( - "X", decl(hasDescendant(recordDecl(hasName("X::Z"))))))); + "X", decl(hasDescendant(recordDecl(hasName("X::Z")).bind("Z"))), + "Z"))); } TEST(MatchFinder, CanMatchStatementsRecursively) { EXPECT_TRUE(matchAndVerifyResultTrue( "void f() { if (1) { for (;;) { } } }", ifStmt().bind("if"), - new VerifyMatchOnNode("if", - stmt(hasDescendant(forStmt()))))); + new VerifyMatchOnNode( + "if", stmt(hasDescendant(forStmt().bind("for"))), "for"))); EXPECT_TRUE(matchAndVerifyResultFalse( "void f() { if (1) { for (;;) { } } }", ifStmt().bind("if"), - new VerifyMatchOnNode("if", - stmt(hasDescendant(declStmt()))))); + new VerifyMatchOnNode( + "if", stmt(hasDescendant(declStmt().bind("decl"))), "decl"))); } TEST(MatchFinder, CanMatchSingleNodesRecursively) { EXPECT_TRUE(matchAndVerifyResultTrue( "class X { class Y {}; };", recordDecl(hasName("::X")).bind("X"), new VerifyMatchOnNode( - "X", recordDecl(has(recordDecl(hasName("X::Y"))))))); + "X", recordDecl(has(recordDecl(hasName("X::Y")).bind("Y"))), "Y"))); EXPECT_TRUE(matchAndVerifyResultFalse( "class X { class Y {}; };", recordDecl(hasName("::X")).bind("X"), new VerifyMatchOnNode( - "X", recordDecl(has(recordDecl(hasName("X::Z"))))))); + "X", recordDecl(has(recordDecl(hasName("X::Z")).bind("Z"))), "Z"))); } class VerifyStartOfTranslationUnit : public MatchFinder::MatchCallback { -- 2.40.0