From 88b95872935719214fdd45760df33942cceec42f Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Mon, 4 Feb 2013 09:42:38 +0000 Subject: [PATCH] Add an eachOf matcher. eachOf gives closure on the forEach and forEachDescendant matchers. Before, it was impossible to implement a findAll matcher, as matching the node or any of its descendants was not expressible (since anyOf only triggers the first match). llvm-svn: 174315 --- clang/include/clang/ASTMatchers/ASTMatchers.h | 26 ++++++++++++++ .../clang/ASTMatchers/ASTMatchersInternal.h | 41 ++++++++++++++++++++-- clang/unittests/ASTMatchers/ASTMatchersTest.cpp | 24 +++++++++++++ 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index 9dbd5d1..bd26d09 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -1095,6 +1095,32 @@ const internal::VariadicDynCastAllOfMatcher type; /// \brief Matches \c TypeLocs in the clang AST. const internal::VariadicDynCastAllOfMatcher typeLoc; +/// \brief Matches if any of the given matchers matches. +/// +/// Unlike \c anyOf, \c eachOf will generate a match result for each +/// matching submatcher. +/// +/// For example, in: +/// \code +/// class A { int a; int b; }; +/// \endcode +/// The matcher: +/// \code +/// recordDecl(eachOf(has(fieldDecl(hasName("a")).bind("v")), +/// has(fieldDecl(hasName("b")).bind("v")))) +/// \endcode +/// will generate two results binding "v", the first of which binds +/// the field declaration of \c a, the second the field declaration of +/// \c b. +/// +/// Usable as: Any Matcher +template +internal::PolymorphicMatcherWithParam2 +eachOf(const M1 &P1, const M2 &P2) { + return internal::PolymorphicMatcherWithParam2(P1, P2); +} + /// \brief Various overloads for the anyOf matcher. /// @{ diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h index ac0e30e..a6661f5 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -832,21 +832,56 @@ private: /// used. They will always be instantiated with types convertible to /// Matcher. template +class EachOfMatcher : public MatcherInterface { +public: + EachOfMatcher(const Matcher &InnerMatcher1, + const Matcher &InnerMatcher2) + : InnerMatcher1(InnerMatcher1), InnerMatcher2(InnerMatcher2) { + } + + virtual bool matches(const T &Node, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + BoundNodesTreeBuilder Builder1; + bool Matched1 = InnerMatcher1.matches(Node, Finder, &Builder1); + if (Matched1) + Builder->addMatch(Builder1.build()); + + BoundNodesTreeBuilder Builder2; + bool Matched2 = InnerMatcher2.matches(Node, Finder, &Builder2); + if (Matched2) + Builder->addMatch(Builder2.build()); + + return Matched1 || Matched2; + } + +private: + const Matcher InnerMatcher1; + const Matcher InnerMatcher2; +}; + +/// \brief Matches nodes of type T for which at least one of the two provided +/// matchers matches. +/// +/// Type arguments MatcherT1 and MatcherT2 are +/// required by PolymorphicMatcherWithParam2 but not actually +/// used. They will always be instantiated with types convertible to +/// Matcher. +template class AnyOfMatcher : public MatcherInterface { public: AnyOfMatcher(const Matcher &InnerMatcher1, const Matcher &InnerMatcher2) - : InnerMatcher1(InnerMatcher1), InnertMatcher2(InnerMatcher2) {} + : InnerMatcher1(InnerMatcher1), InnerMatcher2(InnerMatcher2) {} virtual bool matches(const T &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const { return InnerMatcher1.matches(Node, Finder, Builder) || - InnertMatcher2.matches(Node, Finder, Builder); + InnerMatcher2.matches(Node, Finder, Builder); } private: const Matcher InnerMatcher1; - const Matcher InnertMatcher2; + const Matcher InnerMatcher2; }; /// \brief Creates a Matcher that matches if all inner matchers match. diff --git a/clang/unittests/ASTMatchers/ASTMatchersTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTest.cpp index 670f0d4..dc8b15f 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -2854,6 +2854,30 @@ TEST(ForEachDescendant, BindsCorrectNodes) { new VerifyIdIsBoundTo("decl", 1))); } +TEST(EachOf, TriggersForEachMatch) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "class A { int a; int b; };", + recordDecl(eachOf(has(fieldDecl(hasName("a")).bind("v")), + has(fieldDecl(hasName("b")).bind("v")))), + new VerifyIdIsBoundTo("v", 2))); +} + +TEST(EachOf, BehavesLikeAnyOfUnlessBothMatch) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "class A { int a; int c; };", + recordDecl(eachOf(has(fieldDecl(hasName("a")).bind("v")), + has(fieldDecl(hasName("b")).bind("v")))), + new VerifyIdIsBoundTo("v", 1))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "class A { int c; int b; };", + recordDecl(eachOf(has(fieldDecl(hasName("a")).bind("v")), + has(fieldDecl(hasName("b")).bind("v")))), + new VerifyIdIsBoundTo("v", 1))); + EXPECT_TRUE(notMatches( + "class A { int c; int d; };", + recordDecl(eachOf(has(fieldDecl(hasName("a")).bind("v")), + has(fieldDecl(hasName("b")).bind("v")))))); +} TEST(IsTemplateInstantiation, MatchesImplicitClassTemplateInstantiation) { // Make sure that we can both match the class by name (::X) and by the type -- 2.7.4