From d2bd58907f77e1c1b68a6fa8fc72e1c5b057a5b1 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Thu, 7 Nov 2013 22:30:32 +0000 Subject: [PATCH] Re-introduce MatchFinder::addDynamicMatcher. Differential Revision: http://llvm-reviews.chandlerc.com/D2114 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@194222 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/ASTMatchers/ASTMatchFinder.h | 11 ++++++++++ lib/ASTMatchers/ASTMatchFinder.cpp | 24 ++++++++++++++++++++++ unittests/ASTMatchers/ASTMatchersTest.cpp | 21 ++++++++++++++++++- unittests/ASTMatchers/ASTMatchersTest.h | 16 ++++++++++++++- 4 files changed, 70 insertions(+), 2 deletions(-) diff --git a/include/clang/ASTMatchers/ASTMatchFinder.h b/include/clang/ASTMatchers/ASTMatchFinder.h index 8817d3eb0c..eddc7ca3ac 100644 --- a/include/clang/ASTMatchers/ASTMatchFinder.h +++ b/include/clang/ASTMatchers/ASTMatchFinder.h @@ -136,6 +136,17 @@ public: MatchCallback *Action); /// @} + /// \brief Adds a matcher to execute when running over the AST. + /// + /// This is similar to \c addMatcher(), but it uses the dynamic interface. It + /// is more flexible, but the lost type information enables a caller to pass + /// a matcher that cannot match anything. + /// + /// \returns \c true if the matcher is a valid top-level matcher, \c false + /// otherwise. + bool addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch, + MatchCallback *Action); + /// \brief Creates a clang ASTConsumer that finds all matches. clang::ASTConsumer *newASTConsumer(); diff --git a/lib/ASTMatchers/ASTMatchFinder.cpp b/lib/ASTMatchers/ASTMatchFinder.cpp index fbb696f464..6ca5afc249 100644 --- a/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/lib/ASTMatchers/ASTMatchFinder.cpp @@ -811,6 +811,30 @@ void MatchFinder::addMatcher(const TypeLocMatcher &NodeMatch, MatcherCallbackPairs.push_back(std::make_pair(NodeMatch, Action)); } +bool MatchFinder::addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch, + MatchCallback *Action) { + if (NodeMatch.canConvertTo()) { + addMatcher(NodeMatch.convertTo(), Action); + return true; + } else if (NodeMatch.canConvertTo()) { + addMatcher(NodeMatch.convertTo(), Action); + return true; + } else if (NodeMatch.canConvertTo()) { + addMatcher(NodeMatch.convertTo(), Action); + return true; + } else if (NodeMatch.canConvertTo()) { + addMatcher(NodeMatch.convertTo(), Action); + return true; + } else if (NodeMatch.canConvertTo()) { + addMatcher(NodeMatch.convertTo(), Action); + return true; + } else if (NodeMatch.canConvertTo()) { + addMatcher(NodeMatch.convertTo(), Action); + return true; + } + return false; +} + ASTConsumer *MatchFinder::newASTConsumer() { return new internal::MatchASTConsumer(&MatcherCallbackPairs, ParsingDone); } diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index a848d55f98..5649ad897e 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -40,6 +40,18 @@ TEST(IsDerivedFromDeathTest, DiesOnEmptyBaseName) { } #endif +TEST(Finder, DynamicOnlyAcceptsSomeMatchers) { + MatchFinder Finder; + EXPECT_TRUE(Finder.addDynamicMatcher(decl(), NULL)); + EXPECT_TRUE(Finder.addDynamicMatcher(callExpr(), NULL)); + EXPECT_TRUE(Finder.addDynamicMatcher(constantArrayType(hasSize(42)), NULL)); + + // Do not accept non-toplevel matchers. + EXPECT_FALSE(Finder.addDynamicMatcher(isArrow(), NULL)); + EXPECT_FALSE(Finder.addDynamicMatcher(hasSize(2), NULL)); + EXPECT_FALSE(Finder.addDynamicMatcher(hasName("x"), NULL)); +} + TEST(Decl, MatchesDeclarations) { EXPECT_TRUE(notMatches("", decl(usingDecl()))); EXPECT_TRUE(matches("namespace x { class X {}; } using x::X;", @@ -651,11 +663,18 @@ public: : Id(Id), ExpectedCount(ExpectedCount), Count(0), ExpectedName(ExpectedName) {} - ~VerifyIdIsBoundTo() { + void onEndOfTranslationUnit() LLVM_OVERRIDE { if (ExpectedCount != -1) EXPECT_EQ(ExpectedCount, Count); if (!ExpectedName.empty()) EXPECT_EQ(ExpectedName, Name); + Count = 0; + Name.clear(); + } + + ~VerifyIdIsBoundTo() { + EXPECT_EQ(0, Count); + EXPECT_EQ("", Name); } virtual bool run(const BoundNodes *Nodes) { diff --git a/unittests/ASTMatchers/ASTMatchersTest.h b/unittests/ASTMatchers/ASTMatchersTest.h index 5fed85bb30..e65d8e792d 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.h +++ b/unittests/ASTMatchers/ASTMatchersTest.h @@ -26,6 +26,7 @@ public: virtual ~BoundNodesCallback() {} virtual bool run(const BoundNodes *BoundNodes) = 0; virtual bool run(const BoundNodes *BoundNodes, ASTContext *Context) = 0; + virtual void onEndOfTranslationUnit() {} }; // If 'FindResultVerifier' is not NULL, sets *Verified to the result of @@ -44,6 +45,11 @@ public: } } + void onEndOfTranslationUnit() LLVM_OVERRIDE { + if (FindResultReviewer) + FindResultReviewer->onEndOfTranslationUnit(); + } + private: bool *const Verified; BoundNodesCallback *const FindResultReviewer; @@ -54,15 +60,23 @@ testing::AssertionResult matchesConditionally(const std::string &Code, const T &AMatcher, bool ExpectMatch, llvm::StringRef CompileArg) { - bool Found = false; + bool Found = false, DynamicFound = false; MatchFinder Finder; Finder.addMatcher(AMatcher, new VerifyMatch(0, &Found)); + if (!Finder.addDynamicMatcher(AMatcher, new VerifyMatch(0, &DynamicFound))) + return testing::AssertionFailure() << "Could not add dynamic matcher"; OwningPtr Factory(newFrontendActionFactory(&Finder)); // Some tests use typeof, which is a gnu extension. std::vector Args(1, CompileArg); if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) { return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; } + if (Found != DynamicFound) { + return testing::AssertionFailure() << "Dynamic match result (" + << DynamicFound + << ") does not match static result (" + << Found << ")"; + } if (!Found && ExpectMatch) { return testing::AssertionFailure() << "Could not find match in \"" << Code << "\""; -- 2.40.0