]> granicus.if.org Git - clang/commitdiff
Implement has(), hasDescendant(), forEach() and forEachDescendant() for
authorDaniel Jasper <djasper@google.com>
Mon, 29 Oct 2012 10:14:44 +0000 (10:14 +0000)
committerDaniel Jasper <djasper@google.com>
Mon, 29 Oct 2012 10:14:44 +0000 (10:14 +0000)
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
include/clang/ASTMatchers/ASTMatchersInternal.h
include/clang/ASTMatchers/ASTTypeTraits.h
lib/ASTMatchers/ASTMatchFinder.cpp
unittests/ASTMatchers/ASTMatchersTest.cpp

index 259be8680af7cb63bd3bef1b4b231af0796b3f1b..a70dd5c378bd82f38159823d68f5eded41a4c4fd 100644 (file)
@@ -1086,6 +1086,15 @@ const internal::VariadicDynCastAllOfMatcher<
   Stmt,
   CXXFunctionalCastExpr> functionalCastExpr;
 
+/// \brief Matches \c QualTypes in the clang AST.
+const internal::VariadicAllOfMatcher<QualType> qualType;
+
+/// \brief Matches \c Types in the clang AST.
+const internal::VariadicDynCastAllOfMatcher<Type, Type> type;
+
+/// \brief Matches \c TypeLocs in the clang AST.
+const internal::VariadicDynCastAllOfMatcher<TypeLoc, TypeLoc> typeLoc;
+
 /// \brief Various overloads for the anyOf matcher.
 /// @{
 
@@ -1406,7 +1415,8 @@ internal::ArgumentAdaptingMatcher<internal::ForEachMatcher, ChildT> forEach(
 ///
 /// Usable as: Any Matcher
 template <typename DescendantT>
-internal::ArgumentAdaptingMatcher<internal::ForEachDescendantMatcher, DescendantT>
+internal::ArgumentAdaptingMatcher<internal::ForEachDescendantMatcher,
+                                  DescendantT>
 forEachDescendant(
     const internal::Matcher<DescendantT> &DescendantMatcher) {
   return internal::ArgumentAdaptingMatcher<
@@ -2470,15 +2480,6 @@ isExplicitTemplateSpecialization() {
     internal::IsExplicitTemplateSpecializationMatcher>();
 }
 
-/// \brief Matches \c QualTypes in the clang AST.
-const internal::VariadicAllOfMatcher<QualType> qualType;
-
-/// \brief Matches \c Types in the clang AST.
-const internal::VariadicDynCastAllOfMatcher<Type, Type> type;
-
-/// \brief Matches \c TypeLocs in the clang AST.
-const internal::VariadicDynCastAllOfMatcher<TypeLoc, TypeLoc> typeLoc;
-
 /// \brief Matches \c TypeLocs for which the given inner
 /// QualType-matcher matches.
 inline internal::BindableMatcher<TypeLoc> loc(
@@ -2673,6 +2674,17 @@ AST_TYPE_MATCHER(AutoType, autoType);
 /// Usable as: Matcher<AutoType>
 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)".
 ///
index 49867f1342495f091159dd445e5186b0818f52de..59972ccfa8e198bf0809319e0b585b38e21acac1 100644 (file)
@@ -418,14 +418,15 @@ private:
 };
 
 /// \brief IsBaseType<T>::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 <typename T>
 struct IsBaseType {
   static const bool value =
       (llvm::is_same<T, Decl>::value ||
        llvm::is_same<T, Stmt>::value ||
        llvm::is_same<T, QualType>::value ||
+       llvm::is_same<T, Type>::value ||
+       llvm::is_same<T, TypeLoc>::value ||
        llvm::is_same<T, NestedNameSpecifier>::value ||
        llvm::is_same<T, NestedNameSpecifierLoc>::value ||
        llvm::is_same<T, CXXCtorInitializer>::value);
@@ -495,8 +496,10 @@ public:
                       TraversalKind Traverse,
                       BindKind Bind) {
     TOOLING_COMPILE_ASSERT((llvm::is_base_of<Decl, T>::value ||
-                            llvm::is_base_of<Stmt, T>::value),
-                           only_Decl_or_Stmt_allowed_for_recursive_matching);
+                            llvm::is_base_of<Stmt, T>::value ||
+                            llvm::is_base_of<TypeLoc, T>::value ||
+                            llvm::is_base_of<QualType, T>::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<Decl, T>::value ||
-                            llvm::is_base_of<Stmt, T>::value),
-                           only_Decl_or_Stmt_allowed_for_recursive_matching);
+                            llvm::is_base_of<Stmt, T>::value ||
+                            llvm::is_base_of<TypeLoc, T>::value ||
+                            llvm::is_base_of<QualType, T>::value),
+                           unsupported_type_for_recursive_matching);
     return matchesDescendantOf(ast_type_traits::DynTypedNode::create(Node),
                                Matcher, Builder, Bind);
   }
index ab67b17178806738ef4b218998b04e03e6d38a7e..bda53eaf70f6cb3c3be298eb03adbb485b91252c 100644 (file)
@@ -74,10 +74,10 @@ private:
   enum NodeTypeTag {
     NT_Decl,
     NT_Stmt,
-    NT_Type,
     NT_NestedNameSpecifier,
     NT_NestedNameSpecifierLoc,
     NT_QualType,
+    NT_Type,
     NT_TypeLoc
   } Tag;
 
index 218b78187db6ad3eda010c0f4cba6b8a5434ab15..38df2a199bdeac220bfb82c2ecbfae548874666a 100644 (file)
@@ -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<Stmt>())
       traverse(*S);
+    else if (const QualType *Q = DynNode.get<QualType>())
+      traverse(*Q);
+    else if (const TypeLoc *T = DynNode.get<TypeLoc>())
+      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 <typename T>
-  bool traverse(const T &Node) {
-    TOOLING_COMPILE_ASSERT(IsBaseType<T>::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 <typename T>
+  bool traverse(const T &Node) {
+    TOOLING_COMPILE_ASSERT(IsBaseType<T>::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<MemoizationMap::iterator, bool> InsertResult
       = ResultCache.insert(std::make_pair(input, MemoizedMatchResult()));
     if (InsertResult.second) {
index 689c91f4767267ef78f126c376ab4364f61207a5..2602e87840eb325a0d098e7b7eea41681060ca3e 100644 (file)
@@ -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 <typename T>
+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<T>(Id)) {
+      ++Count;
+      if (const NamedDecl *Named = Nodes->getNodeAs<NamedDecl>(Id)) {
+        Name = Named->getNameAsString();
+      } else if (const NestedNameSpecifier *NNS =
+                 Nodes->getNodeAs<NestedNameSpecifier>(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<Decl>("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<Type>("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<QualType>("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 <typename T>
-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<T>(Id)) {
-      ++Count;
-      if (const NamedDecl *Named = Nodes->getNodeAs<NamedDecl>(Id)) {
-        Name = Named->getNameAsString();
-      } else if (const NestedNameSpecifier *NNS =
-                 Nodes->getNodeAs<NestedNameSpecifier>(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