diff options
author | Manuel Klimek <klimek@google.com> | 2013-02-06 10:33:21 +0000 |
---|---|---|
committer | Manuel Klimek <klimek@google.com> | 2013-02-06 10:33:21 +0000 |
commit | cf87bffc346244f0ed8eae7fffb52a0f03cd0413 (patch) | |
tree | a4330235837fae6bb6880f23fcafb99655fb55e2 | |
parent | 4e778091b0ff42895644ca8bef30f1a91ba6b32b (diff) |
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
-rw-r--r-- | include/clang/ASTMatchers/ASTMatchFinder.h | 23 | ||||
-rw-r--r-- | 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 <typename MatcherT, typename NodeT> SmallVector<BoundNodes, 1> @@ -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<Decl>("id", match(Matcher.bind("id"), +/// Node, Context)); +/// \endcode +template <typename NodeT> +NodeT * +selectFirst(StringRef BoundTo, const SmallVectorImpl<BoundNodes> &Results) { + for (SmallVectorImpl<BoundNodes>::const_iterator I = Results.begin(), + E = Results.end(); + I != E; ++I) { + if (NodeT *Node = I->getNodeAs<NodeT>(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 <typename T> class VerifyMatchOnNode : public BoundNodesCallback { public: - VerifyMatchOnNode(StringRef Id, const internal::Matcher<T> &InnerMatcher) - : Id(Id), InnerMatcher(InnerMatcher) { + VerifyMatchOnNode(StringRef Id, const internal::Matcher<T> &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<T>(Id); - SmallVector<BoundNodes, 1> Result = match(InnerMatcher, *Node, *Context); - return !Result.empty(); + return selectFirst<const T>(InnerId, + match(InnerMatcher, *Node, *Context)) != NULL; } private: std::string Id; internal::Matcher<T> InnerMatcher; + std::string InnerId; }; TEST(MatchFinder, CanMatchDeclarationsRecursively) { EXPECT_TRUE(matchAndVerifyResultTrue( "class X { class Y {}; };", recordDecl(hasName("::X")).bind("X"), new VerifyMatchOnNode<clang::Decl>( - "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<clang::Decl>( - "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<clang::Stmt>("if", - stmt(hasDescendant(forStmt()))))); + new VerifyMatchOnNode<clang::Stmt>( + "if", stmt(hasDescendant(forStmt().bind("for"))), "for"))); EXPECT_TRUE(matchAndVerifyResultFalse( "void f() { if (1) { for (;;) { } } }", ifStmt().bind("if"), - new VerifyMatchOnNode<clang::Stmt>("if", - stmt(hasDescendant(declStmt()))))); + new VerifyMatchOnNode<clang::Stmt>( + "if", stmt(hasDescendant(declStmt().bind("decl"))), "decl"))); } TEST(MatchFinder, CanMatchSingleNodesRecursively) { EXPECT_TRUE(matchAndVerifyResultTrue( "class X { class Y {}; };", recordDecl(hasName("::X")).bind("X"), new VerifyMatchOnNode<clang::Decl>( - "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<clang::Decl>( - "X", recordDecl(has(recordDecl(hasName("X::Z"))))))); + "X", recordDecl(has(recordDecl(hasName("X::Z")).bind("Z"))), "Z"))); } class VerifyStartOfTranslationUnit : public MatchFinder::MatchCallback { |