From a267cf6f87dc695143d65fc61ec1744564f55932 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Mon, 29 Oct 2012 10:14:44 +0000 Subject: [PATCH] Implement has(), hasDescendant(), forEach() and forEachDescendant() for Types, QualTypes and TypeLocs. Review: http://llvm-reviews.chandlerc.com/D83 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@166917 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/ASTMatchers/ASTMatchers.h | 32 ++-- .../clang/ASTMatchers/ASTMatchersInternal.h | 17 +- include/clang/ASTMatchers/ASTTypeTraits.h | 2 +- lib/ASTMatchers/ASTMatchFinder.cpp | 85 ++++++--- unittests/ASTMatchers/ASTMatchersTest.cpp | 165 ++++++++++++------ 5 files changed, 204 insertions(+), 97 deletions(-) diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h index 259be8680a..a70dd5c378 100644 --- a/include/clang/ASTMatchers/ASTMatchers.h +++ b/include/clang/ASTMatchers/ASTMatchers.h @@ -1086,6 +1086,15 @@ const internal::VariadicDynCastAllOfMatcher< Stmt, CXXFunctionalCastExpr> functionalCastExpr; +/// \brief Matches \c QualTypes in the clang AST. +const internal::VariadicAllOfMatcher qualType; + +/// \brief Matches \c Types in the clang AST. +const internal::VariadicDynCastAllOfMatcher type; + +/// \brief Matches \c TypeLocs in the clang AST. +const internal::VariadicDynCastAllOfMatcher typeLoc; + /// \brief Various overloads for the anyOf matcher. /// @{ @@ -1406,7 +1415,8 @@ internal::ArgumentAdaptingMatcher forEach( /// /// Usable as: Any Matcher template -internal::ArgumentAdaptingMatcher +internal::ArgumentAdaptingMatcher forEachDescendant( const internal::Matcher &DescendantMatcher) { return internal::ArgumentAdaptingMatcher< @@ -2470,15 +2480,6 @@ isExplicitTemplateSpecialization() { internal::IsExplicitTemplateSpecializationMatcher>(); } -/// \brief Matches \c QualTypes in the clang AST. -const internal::VariadicAllOfMatcher qualType; - -/// \brief Matches \c Types in the clang AST. -const internal::VariadicDynCastAllOfMatcher type; - -/// \brief Matches \c TypeLocs in the clang AST. -const internal::VariadicDynCastAllOfMatcher typeLoc; - /// \brief Matches \c TypeLocs for which the given inner /// QualType-matcher matches. inline internal::BindableMatcher loc( @@ -2673,6 +2674,17 @@ AST_TYPE_MATCHER(AutoType, autoType); /// Usable as: Matcher AST_TYPE_TRAVERSE_MATCHER(hasDeducedType, getDeducedType); +/// \brief Matches \c FunctionType nodes. +/// +/// Given +/// \code +/// int (*f)(int); +/// void g(); +/// \endcode +/// functionType() +/// matches "int (*f)(int)" and the type of "g". +AST_TYPE_MATCHER(FunctionType, functionType); + /// \brief Matches block pointer types, i.e. types syntactically represented as /// "void (^)(int)". /// diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h index 49867f1342..59972ccfa8 100644 --- a/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -418,14 +418,15 @@ private: }; /// \brief IsBaseType::value is true if T is a "base" type in the AST -/// node class hierarchies (i.e. if T is Decl, Stmt, QualType, or -/// CXXCtorInitializer). +/// node class hierarchies. template struct IsBaseType { static const bool value = (llvm::is_same::value || llvm::is_same::value || llvm::is_same::value || + llvm::is_same::value || + llvm::is_same::value || llvm::is_same::value || llvm::is_same::value || llvm::is_same::value); @@ -495,8 +496,10 @@ public: TraversalKind Traverse, BindKind Bind) { TOOLING_COMPILE_ASSERT((llvm::is_base_of::value || - llvm::is_base_of::value), - only_Decl_or_Stmt_allowed_for_recursive_matching); + llvm::is_base_of::value || + llvm::is_base_of::value || + llvm::is_base_of::value), + unsupported_type_for_recursive_matching); return matchesChildOf(ast_type_traits::DynTypedNode::create(Node), Matcher, Builder, Traverse, Bind); } @@ -507,8 +510,10 @@ public: BoundNodesTreeBuilder *Builder, BindKind Bind) { TOOLING_COMPILE_ASSERT((llvm::is_base_of::value || - llvm::is_base_of::value), - only_Decl_or_Stmt_allowed_for_recursive_matching); + llvm::is_base_of::value || + llvm::is_base_of::value || + llvm::is_base_of::value), + unsupported_type_for_recursive_matching); return matchesDescendantOf(ast_type_traits::DynTypedNode::create(Node), Matcher, Builder, Bind); } diff --git a/include/clang/ASTMatchers/ASTTypeTraits.h b/include/clang/ASTMatchers/ASTTypeTraits.h index ab67b17178..bda53eaf70 100644 --- a/include/clang/ASTMatchers/ASTTypeTraits.h +++ b/include/clang/ASTMatchers/ASTTypeTraits.h @@ -74,10 +74,10 @@ private: enum NodeTypeTag { NT_Decl, NT_Stmt, - NT_Type, NT_NestedNameSpecifier, NT_NestedNameSpecifierLoc, NT_QualType, + NT_Type, NT_TypeLoc } Tag; diff --git a/lib/ASTMatchers/ASTMatchFinder.cpp b/lib/ASTMatchers/ASTMatchFinder.cpp index 218b78187d..38df2a199b 100644 --- a/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/lib/ASTMatchers/ASTMatchFinder.cpp @@ -124,7 +124,7 @@ public: : Matcher(Matcher), Finder(Finder), Builder(Builder), - CurrentDepth(-1), + CurrentDepth(0), MaxDepth(MaxDepth), Traversal(Traversal), Bind(Bind), @@ -147,6 +147,10 @@ public: traverse(*D); else if (const Stmt *S = DynNode.get()) traverse(*S); + else if (const QualType *Q = DynNode.get()) + traverse(*Q); + else if (const TypeLoc *T = DynNode.get()) + traverse(*T); // FIXME: Add other base types after adding tests. return Matches; } @@ -155,9 +159,11 @@ public: // They are public only to allow CRTP to work. They are *not *part // of the public API of this class. bool TraverseDecl(Decl *DeclNode) { + ScopedIncrement ScopedDepth(&CurrentDepth); return (DeclNode == NULL) || traverse(*DeclNode); } bool TraverseStmt(Stmt *StmtNode) { + ScopedIncrement ScopedDepth(&CurrentDepth); const Stmt *StmtToTraverse = StmtNode; if (Traversal == ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses) { @@ -168,9 +174,29 @@ public: } return (StmtToTraverse == NULL) || traverse(*StmtToTraverse); } + // We assume that the QualType and the contained type are on the same + // hierarchy level. Thus, we try to match either of them. bool TraverseType(QualType TypeNode) { + ScopedIncrement ScopedDepth(&CurrentDepth); + // Match the Type. + if (!match(*TypeNode)) + return false; + // The QualType is matched inside traverse. return traverse(TypeNode); } + // We assume that the TypeLoc, contained QualType and contained Type all are + // on the same hierarchy level. Thus, we try to match all of them. + bool TraverseTypeLoc(TypeLoc TypeLocNode) { + ScopedIncrement ScopedDepth(&CurrentDepth); + // Match the Type. + if (!match(*TypeLocNode.getType())) + return false; + // Match the QualType. + if (!match(TypeLocNode.getType())) + return false; + // The TypeLoc is matched inside traverse. + return traverse(TypeLocNode); + } bool shouldVisitTemplateInstantiations() const { return true; } bool shouldVisitImplicitCode() const { return true; } @@ -188,7 +214,7 @@ private: // Resets the state of this object. void reset() { Matches = false; - CurrentDepth = -1; + CurrentDepth = 0; } // Forwards the call to the corresponding Traverse*() method in the @@ -202,18 +228,19 @@ private: bool baseTraverse(QualType TypeNode) { return VisitorBase::TraverseType(TypeNode); } + bool baseTraverse(TypeLoc TypeLocNode) { + return VisitorBase::TraverseTypeLoc(TypeLocNode); + } - // Traverses the subtree rooted at 'node'; returns true if the - // traversal should continue after this function returns; also sets - // matched_ to true if a match is found during the traversal. + // Sets 'Matched' to true if 'Matcher' matches 'Node' and: + // 0 < CurrentDepth <= MaxDepth. + // + // Returns 'true' if traversal should continue after this function + // returns, i.e. if no match is found or 'Bind' is 'BK_All'. template - bool traverse(const T &Node) { - TOOLING_COMPILE_ASSERT(IsBaseType::value, - traverse_can_only_be_instantiated_with_base_type); - ScopedIncrement ScopedDepth(&CurrentDepth); - if (CurrentDepth == 0) { - // We don't want to match the root node, so just recurse. - return baseTraverse(Node); + bool match(const T &Node) { + if (CurrentDepth == 0 || CurrentDepth > MaxDepth) { + return true; } if (Bind != ASTMatchFinder::BK_All) { if (Matcher->matches(ast_type_traits::DynTypedNode::create(Node), @@ -221,15 +248,6 @@ private: Matches = true; return false; // Abort as soon as a match is found. } - if (CurrentDepth < MaxDepth) { - // The current node doesn't match, and we haven't reached the - // maximum depth yet, so recurse. - return baseTraverse(Node); - } - // The current node doesn't match, and we have reached the - // maximum depth, so don't recurse (but continue the traversal - // such that other nodes at the current level can be visited). - return true; } else { BoundNodesTreeBuilder RecursiveBuilder; if (Matcher->matches(ast_type_traits::DynTypedNode::create(Node), @@ -238,12 +256,19 @@ private: Matches = true; Builder->addMatch(RecursiveBuilder.build()); } - if (CurrentDepth < MaxDepth) { - baseTraverse(Node); - } - // In kBindAll mode we always search for more matches. - return true; } + return true; + } + + // Traverses the subtree rooted at 'Node'; returns true if the + // traversal should continue after this function returns. + template + bool traverse(const T &Node) { + TOOLING_COMPILE_ASSERT(IsBaseType::value, + traverse_can_only_be_instantiated_with_base_type); + if (!match(Node)) + return false; + return baseTraverse(Node); } const DynTypedMatcher *const Matcher; @@ -322,8 +347,12 @@ public: BoundNodesTreeBuilder *Builder, int MaxDepth, TraversalKind Traversal, BindKind Bind) { const UntypedMatchInput input(Matcher.getID(), Node.getMemoizationData()); - assert(input.second && - "Fix getMemoizationData once more types allow recursive matching."); + + // For AST-nodes that don't have an identity, we can't memoize. + if (!input.second) + return matchesRecursively(Node, Matcher, Builder, MaxDepth, Traversal, + Bind); + std::pair InsertResult = ResultCache.insert(std::make_pair(input, MemoizedMatchResult())); if (InsertResult.second) { diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index 689c91f476..2602e87840 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -555,6 +555,114 @@ TEST(DeclarationMatcher, HasDescendant) { "};", ZDescendantClassXDescendantClassY)); } +// Implements a run method that returns whether BoundNodes contains a +// Decl bound to Id that can be dynamically cast to T. +// Optionally checks that the check succeeded a specific number of times. +template +class VerifyIdIsBoundTo : public BoundNodesCallback { +public: + // Create an object that checks that a node of type \c T was bound to \c Id. + // Does not check for a certain number of matches. + explicit VerifyIdIsBoundTo(llvm::StringRef Id) + : Id(Id), ExpectedCount(-1), Count(0) {} + + // Create an object that checks that a node of type \c T was bound to \c Id. + // Checks that there were exactly \c ExpectedCount matches. + VerifyIdIsBoundTo(llvm::StringRef Id, int ExpectedCount) + : Id(Id), ExpectedCount(ExpectedCount), Count(0) {} + + // Create an object that checks that a node of type \c T was bound to \c Id. + // Checks that there was exactly one match with the name \c ExpectedName. + // Note that \c T must be a NamedDecl for this to work. + VerifyIdIsBoundTo(llvm::StringRef Id, llvm::StringRef ExpectedName) + : Id(Id), ExpectedCount(1), Count(0), ExpectedName(ExpectedName) {} + + ~VerifyIdIsBoundTo() { + if (ExpectedCount != -1) + EXPECT_EQ(ExpectedCount, Count); + if (!ExpectedName.empty()) + EXPECT_EQ(ExpectedName, Name); + } + + virtual bool run(const BoundNodes *Nodes) { + if (Nodes->getNodeAs(Id)) { + ++Count; + if (const NamedDecl *Named = Nodes->getNodeAs(Id)) { + Name = Named->getNameAsString(); + } else if (const NestedNameSpecifier *NNS = + Nodes->getNodeAs(Id)) { + llvm::raw_string_ostream OS(Name); + NNS->print(OS, PrintingPolicy(LangOptions())); + } + return true; + } + return false; + } + +private: + const std::string Id; + const int ExpectedCount; + int Count; + const std::string ExpectedName; + std::string Name; +}; + +TEST(HasDescendant, MatchesDescendantTypes) { + EXPECT_TRUE(matches("void f() { int i = 3; }", + decl(hasDescendant(loc(builtinType()))))); + EXPECT_TRUE(matches("void f() { int i = 3; }", + stmt(hasDescendant(builtinType())))); + + EXPECT_TRUE(matches("void f() { int i = 3; }", + stmt(hasDescendant(loc(builtinType()))))); + EXPECT_TRUE(matches("void f() { int i = 3; }", + stmt(hasDescendant(qualType(builtinType()))))); + + EXPECT_TRUE(notMatches("void f() { float f = 2.0f; }", + stmt(hasDescendant(isInteger())))); + + EXPECT_TRUE(matchAndVerifyResultTrue( + "void f() { int a; float c; int d; int e; }", + functionDecl(forEachDescendant( + varDecl(hasDescendant(isInteger())).bind("x"))), + new VerifyIdIsBoundTo("x", 3))); +} + +TEST(HasDescendant, MatchesDescendantsOfTypes) { + EXPECT_TRUE(matches("void f() { int*** i; }", + qualType(hasDescendant(builtinType())))); + EXPECT_TRUE(matches("void f() { int*** i; }", + qualType(hasDescendant( + pointerType(pointee(builtinType())))))); + EXPECT_TRUE(matches("void f() { int*** i; }", + typeLoc(hasDescendant(builtinTypeLoc())))); + + EXPECT_TRUE(matchAndVerifyResultTrue( + "void f() { int*** i; }", + qualType(asString("int ***"), forEachDescendant(pointerType().bind("x"))), + new VerifyIdIsBoundTo("x", 2))); +} + +TEST(Has, MatchesChildrenOfTypes) { + EXPECT_TRUE(matches("int i;", + varDecl(hasName("i"), has(isInteger())))); + EXPECT_TRUE(notMatches("int** i;", + varDecl(hasName("i"), has(isInteger())))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "int (*f)(float, int);", + qualType(functionType(), forEach(qualType(isInteger()).bind("x"))), + new VerifyIdIsBoundTo("x", 2))); +} + +TEST(Has, MatchesChildTypes) { + EXPECT_TRUE(matches( + "int* i;", + varDecl(hasName("i"), hasType(qualType(has(builtinType())))))); + EXPECT_TRUE(notMatches( + "int* i;", + varDecl(hasName("i"), hasType(qualType(has(pointerType())))))); +} + TEST(Enum, DoesNotMatchClasses) { EXPECT_TRUE(notMatches("class X {};", enumDecl(hasName("X")))); } @@ -613,58 +721,6 @@ TEST(TypeMatcher, MatchesClassType) { matches("class A { public: A *a; class B {}; };", TypeAHasClassB)); } -// Implements a run method that returns whether BoundNodes contains a -// Decl bound to Id that can be dynamically cast to T. -// Optionally checks that the check succeeded a specific number of times. -template -class VerifyIdIsBoundTo : public BoundNodesCallback { -public: - // Create an object that checks that a node of type \c T was bound to \c Id. - // Does not check for a certain number of matches. - explicit VerifyIdIsBoundTo(llvm::StringRef Id) - : Id(Id), ExpectedCount(-1), Count(0) {} - - // Create an object that checks that a node of type \c T was bound to \c Id. - // Checks that there were exactly \c ExpectedCount matches. - VerifyIdIsBoundTo(llvm::StringRef Id, int ExpectedCount) - : Id(Id), ExpectedCount(ExpectedCount), Count(0) {} - - // Create an object that checks that a node of type \c T was bound to \c Id. - // Checks that there was exactly one match with the name \c ExpectedName. - // Note that \c T must be a NamedDecl for this to work. - VerifyIdIsBoundTo(llvm::StringRef Id, llvm::StringRef ExpectedName) - : Id(Id), ExpectedCount(1), Count(0), ExpectedName(ExpectedName) {} - - ~VerifyIdIsBoundTo() { - if (ExpectedCount != -1) - EXPECT_EQ(ExpectedCount, Count); - if (!ExpectedName.empty()) - EXPECT_EQ(ExpectedName, Name); - } - - virtual bool run(const BoundNodes *Nodes) { - if (Nodes->getNodeAs(Id)) { - ++Count; - if (const NamedDecl *Named = Nodes->getNodeAs(Id)) { - Name = Named->getNameAsString(); - } else if (const NestedNameSpecifier *NNS = - Nodes->getNodeAs(Id)) { - llvm::raw_string_ostream OS(Name); - NNS->print(OS, PrintingPolicy(LangOptions())); - } - return true; - } - return false; - } - -private: - const std::string Id; - const int ExpectedCount; - int Count; - const std::string ExpectedName; - std::string Name; -}; - TEST(Matcher, BindMatchedNodes) { DeclarationMatcher ClassX = has(recordDecl(hasName("::X")).bind("x")); @@ -3018,6 +3074,11 @@ TEST(TypeMatching, MatchesAutoTypes) { autoType(hasDeducedType(isInteger())))); } +TEST(TypeMatching, MatchesFunctionTypes) { + EXPECT_TRUE(matches("int (*f)(int);", functionType())); + EXPECT_TRUE(matches("void f(int i) {}", functionType())); +} + TEST(TypeMatching, PointerTypes) { // FIXME: Reactive when these tests can be more specific (not matching // implicit code on certain platforms), likely when we have hasDescendant for -- 2.50.1