From cf52ca6bb6dd76a1bd967bc422287fafafa1e45a Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Thu, 20 Jun 2013 14:06:32 +0000 Subject: [PATCH] Adds the equalsBoundNode matcher. Most of the tests contributed by Edwin Vane. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@184427 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LibASTMatchersReference.html | 92 +++++++++++++++++++ include/clang/AST/ASTTypeTraits.h | 37 +++++--- include/clang/ASTMatchers/ASTMatchers.h | 49 ++++++++++ .../clang/ASTMatchers/ASTMatchersInternal.h | 15 +++ lib/AST/ASTTypeTraits.cpp | 4 +- unittests/ASTMatchers/ASTMatchersTest.cpp | 89 ++++++++++++++++++ 6 files changed, 272 insertions(+), 14 deletions(-) diff --git a/docs/LibASTMatchersReference.html b/docs/LibASTMatchersReference.html index 8e31d583cb..50ff38edfa 100644 --- a/docs/LibASTMatchersReference.html +++ b/docs/LibASTMatchersReference.html @@ -1730,6 +1730,29 @@ declCountIs(2) +Matcher<Decl>equalsBoundNodestd::string ID +
Matches if a node equals a previously bound node.
+
+Matches a node if it equals the node previously bound to ID.
+
+Given
+  class X { int a; int b; };
+recordDecl(
+    has(fieldDecl(hasName("a"), hasType(type().bind("t")))),
+    has(fieldDecl(hasName("b"), hasType(type(equalsBoundNode("t"))))))
+  matches the class X, as a and b have the same type.
+
+Note that when multiple matches are involved via forEach* matchers,
+equalsBoundNodes acts as a filter.
+For example:
+compoundStmt(
+    forEachDescendant(varDecl().bind("d")),
+    forEachDescendant(declRefExpr(to(decl(equalsBoundNode("d"))))))
+will trigger a match for each combination of variable declaration
+and reference to that variable declaration within a compound statement.
+
+ + Matcher<Decl>equalsNodeDecl* Other
Matches if a node equals another node.
 
@@ -1933,6 +1956,29 @@ callExpr(on(hasType(asString("class Y *"))))
 
+Matcher<QualType>equalsBoundNodestd::string ID +
Matches if a node equals a previously bound node.
+
+Matches a node if it equals the node previously bound to ID.
+
+Given
+  class X { int a; int b; };
+recordDecl(
+    has(fieldDecl(hasName("a"), hasType(type().bind("t")))),
+    has(fieldDecl(hasName("b"), hasType(type(equalsBoundNode("t"))))))
+  matches the class X, as a and b have the same type.
+
+Note that when multiple matches are involved via forEach* matchers,
+equalsBoundNodes acts as a filter.
+For example:
+compoundStmt(
+    forEachDescendant(varDecl().bind("d")),
+    forEachDescendant(declRefExpr(to(decl(equalsBoundNode("d"))))))
+will trigger a match for each combination of variable declaration
+and reference to that variable declaration within a compound statement.
+
+ + Matcher<QualType>hasLocalQualifiers
Matches QualType nodes that have local CV-qualifiers attached to
 the node, not hidden within a typedef.
@@ -1977,6 +2023,29 @@ matches "a(int)", "b(long)", but not "c(double)".
 
+Matcher<Stmt>equalsBoundNodestd::string ID +
Matches if a node equals a previously bound node.
+
+Matches a node if it equals the node previously bound to ID.
+
+Given
+  class X { int a; int b; };
+recordDecl(
+    has(fieldDecl(hasName("a"), hasType(type().bind("t")))),
+    has(fieldDecl(hasName("b"), hasType(type(equalsBoundNode("t"))))))
+  matches the class X, as a and b have the same type.
+
+Note that when multiple matches are involved via forEach* matchers,
+equalsBoundNodes acts as a filter.
+For example:
+compoundStmt(
+    forEachDescendant(varDecl().bind("d")),
+    forEachDescendant(declRefExpr(to(decl(equalsBoundNode("d"))))))
+will trigger a match for each combination of variable declaration
+and reference to that variable declaration within a compound statement.
+
+ + Matcher<Stmt>equalsNodeStmt* Other
Matches if a node equals another node.
 
@@ -2000,6 +2069,29 @@ Usable as: Matcher<Type>equalsBoundNodestd::string ID
+
Matches if a node equals a previously bound node.
+
+Matches a node if it equals the node previously bound to ID.
+
+Given
+  class X { int a; int b; };
+recordDecl(
+    has(fieldDecl(hasName("a"), hasType(type().bind("t")))),
+    has(fieldDecl(hasName("b"), hasType(type(equalsBoundNode("t"))))))
+  matches the class X, as a and b have the same type.
+
+Note that when multiple matches are involved via forEach* matchers,
+equalsBoundNodes acts as a filter.
+For example:
+compoundStmt(
+    forEachDescendant(varDecl().bind("d")),
+    forEachDescendant(declRefExpr(to(decl(equalsBoundNode("d"))))))
+will trigger a match for each combination of variable declaration
+and reference to that variable declaration within a compound statement.
+
+ + Matcher<UnaryExprOrTypeTraitExpr>ofKindUnaryExprOrTypeTrait Kind
Matches unary expressions of a certain kind.
 
diff --git a/include/clang/AST/ASTTypeTraits.h b/include/clang/AST/ASTTypeTraits.h
index d98ad33a2a..fc099874f0 100644
--- a/include/clang/AST/ASTTypeTraits.h
+++ b/include/clang/AST/ASTTypeTraits.h
@@ -43,10 +43,10 @@ public:
   }
 
   /// \brief Returns \c true if \c this and \c Other represent the same kind.
-  bool isSame(ASTNodeKind Other);
+  bool isSame(ASTNodeKind Other) const;
 
   /// \brief Returns \c true if \c this is a base kind of (or same as) \c Other
-  bool isBaseOf(ASTNodeKind Other);
+  bool isBaseOf(ASTNodeKind Other) const;
 
   /// \brief String representation of the kind.
   StringRef asStringRef() const;
@@ -144,8 +144,8 @@ public:
   /// convertible to \c T.
   ///
   /// For types that have identity via their pointer in the AST
-  /// (like \c Stmt and \c Decl) the returned pointer points to the
-  /// referenced AST node.
+  /// (like \c Stmt, \c Decl, \c Type and \c NestedNameSpecifier) the returned
+  /// pointer points to the referenced AST node.
   /// For other types (like \c QualType) the value is stored directly
   /// in the \c DynTypedNode, and the returned pointer points at
   /// the storage inside DynTypedNode. For those nodes, do not
@@ -167,12 +167,20 @@ public:
   ///
   /// Supports comparison of nodes that support memoization.
   /// FIXME: Implement comparsion for other node types (currently
-  /// only Stmt and Decl return memoization data).
+  /// only Stmt, Decl, Type and NestedNameSpecifier return memoization data).
   bool operator<(const DynTypedNode &Other) const {
     assert(getMemoizationData() && Other.getMemoizationData());
     return getMemoizationData() < Other.getMemoizationData();
   }
   bool operator==(const DynTypedNode &Other) const {
+    // Nodes with different types cannot be equal.
+    if (!NodeKind.isSame(Other.NodeKind))
+      return false;
+
+    // FIXME: Implement for other types.
+    if (ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)) {
+      return *get() == *Other.get();
+    }
     assert(getMemoizationData() && Other.getMemoizationData());
     return getMemoizationData() == Other.getMemoizationData();
   }
@@ -189,13 +197,14 @@ private:
 
   /// \brief Stores the data of the node.
   ///
-  /// Note that we can store \c Decls and \c Stmts by pointer as they are
-  /// guaranteed to be unique pointers pointing to dedicated storage in the
-  /// AST. \c QualTypes on the other hand do not have storage or unique
-  /// pointers and thus need to be stored by value.
-  llvm::AlignedCharArrayUnion Storage;
+  /// Note that we can store \c Decls, \c Stmts, \c Types and
+  /// \c NestedNameSpecifiers by pointer as they are guaranteed to be unique
+  /// pointers pointing to dedicated storage in the AST. \c QualTypes on the
+  /// other hand do not have storage or unique pointers and thus need to be
+  /// stored by value.
+  llvm::AlignedCharArrayUnion
+      Storage;
 };
 
 // FIXME: Pull out abstraction for the following.
@@ -311,6 +320,10 @@ inline const void *DynTypedNode::getMemoizationData() const {
     return BaseConverter::get(NodeKind, Storage.buffer);
   } else if (ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)) {
     return BaseConverter::get(NodeKind, Storage.buffer);
+  } else if (ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)) {
+    return BaseConverter::get(NodeKind, Storage.buffer);
+  } else if (ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)) {
+    return BaseConverter::get(NodeKind, Storage.buffer);
   }
   return NULL;
 }
diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h
index 50da7d6a9c..366707b0a4 100644
--- a/include/clang/ASTMatchers/ASTMatchers.h
+++ b/include/clang/ASTMatchers/ASTMatchers.h
@@ -2294,6 +2294,55 @@ AST_POLYMORPHIC_MATCHER_P(hasCondition, internal::Matcher,
           InnerMatcher.matches(*Condition, Finder, Builder));
 }
 
+namespace internal {
+struct NotEqualsBoundNodePredicate {
+  bool operator()(const internal::BoundNodesMap &Nodes) const {
+    return Nodes.getNode(ID) != Node;
+  }
+  std::string ID;
+  ast_type_traits::DynTypedNode Node;
+};
+} // namespace internal
+
+/// \brief Matches if a node equals a previously bound node.
+///
+/// Matches a node if it equals the node previously bound to \p ID.
+///
+/// Given
+/// \code
+///   class X { int a; int b; };
+/// \endcode
+/// recordDecl(
+///     has(fieldDecl(hasName("a"), hasType(type().bind("t")))),
+///     has(fieldDecl(hasName("b"), hasType(type(equalsBoundNode("t"))))))
+///   matches the class \c X, as \c a and \c b have the same type.
+///
+/// Note that when multiple matches are involved via \c forEach* matchers,
+/// \c equalsBoundNodes acts as a filter.
+/// For example:
+/// compoundStmt(
+///     forEachDescendant(varDecl().bind("d")),
+///     forEachDescendant(declRefExpr(to(decl(equalsBoundNode("d"))))))
+/// will trigger a match for each combination of variable declaration
+/// and reference to that variable declaration within a compound statement.
+AST_POLYMORPHIC_MATCHER_P(equalsBoundNode, std::string, ID) {
+  // FIXME: Figure out whether it makes sense to allow this
+  // on any other node types.
+  // For *Loc it probably does not make sense, as those seem
+  // unique. For NestedNameSepcifier it might make sense, as
+  // those also have pointer identity, but I'm not sure whether
+  // they're ever reused.
+  TOOLING_COMPILE_ASSERT((llvm::is_base_of::value ||
+                          llvm::is_base_of::value ||
+                          llvm::is_base_of::value ||
+                          llvm::is_base_of::value),
+                         equals_bound_node_requires_non_unique_node_class);
+  internal::NotEqualsBoundNodePredicate Predicate;
+  Predicate.ID = ID;
+  Predicate.Node = ast_type_traits::DynTypedNode::create(Node);
+  return Builder->removeBindings(Predicate);
+}
+
 /// \brief Matches the condition variable statement in an if statement.
 ///
 /// Given
diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h
index b27ec57f7d..7d09e67cbe 100644
--- a/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -84,6 +84,14 @@ public:
     return It->second.get();
   }
 
+  ast_type_traits::DynTypedNode getNode(StringRef ID) const {
+    IDToNodeMap::const_iterator It = NodeMap.find(ID);
+    if (It == NodeMap.end()) {
+      return ast_type_traits::DynTypedNode();
+    }
+    return It->second;
+  }
+
   /// \brief Imposes an order on BoundNodesMaps.
   bool operator<(const BoundNodesMap &Other) const {
     return NodeMap < Other.NodeMap;
@@ -134,6 +142,13 @@ public:
   /// The ownership of 'ResultVisitor' remains at the caller.
   void visitMatches(Visitor* ResultVisitor);
 
+  template 
+  bool removeBindings(const ExcludePredicate &Predicate) {
+    Bindings.erase(std::remove_if(Bindings.begin(), Bindings.end(), Predicate),
+                   Bindings.end());
+    return !Bindings.empty();
+  }
+
   /// \brief Imposes an order on BoundNodesTreeBuilders.
   bool operator<(const BoundNodesTreeBuilder &Other) const {
     return Bindings < Other.Bindings;
diff --git a/lib/AST/ASTTypeTraits.cpp b/lib/AST/ASTTypeTraits.cpp
index 40e669d7ad..7f930b47b5 100644
--- a/lib/AST/ASTTypeTraits.cpp
+++ b/lib/AST/ASTTypeTraits.cpp
@@ -35,11 +35,11 @@ const ASTNodeKind::KindInfo ASTNodeKind::AllKindInfo[] = {
 #include "clang/AST/TypeNodes.def"
 };
 
-bool ASTNodeKind::isBaseOf(ASTNodeKind Other) {
+bool ASTNodeKind::isBaseOf(ASTNodeKind Other) const {
   return isBaseOf(KindId, Other.KindId);
 }
 
-bool ASTNodeKind::isSame(ASTNodeKind Other) {
+bool ASTNodeKind::isSame(ASTNodeKind Other) const {
   return KindId != NKI_None && KindId == Other.KindId;
 }
 
diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp
index a7ccb56cd6..839c447061 100644
--- a/unittests/ASTMatchers/ASTMatchersTest.cpp
+++ b/unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -4081,5 +4081,94 @@ TEST(MatchFinder, InterceptsEndOfTranslationUnit) {
   EXPECT_TRUE(VerifyCallback.Called);
 }
 
+TEST(EqualsBoundNodeMatcher, QualType) {
+  EXPECT_TRUE(matches(
+      "int i = 1;", varDecl(hasType(qualType().bind("type")),
+                            hasInitializer(ignoringParenImpCasts(
+                                hasType(qualType(equalsBoundNode("type"))))))));
+  EXPECT_TRUE(notMatches("int i = 1.f;",
+                         varDecl(hasType(qualType().bind("type")),
+                                 hasInitializer(ignoringParenImpCasts(hasType(
+                                     qualType(equalsBoundNode("type"))))))));
+}
+
+TEST(EqualsBoundNodeMatcher, NonMatchingTypes) {
+  EXPECT_TRUE(notMatches(
+      "int i = 1;", varDecl(namedDecl(hasName("i")).bind("name"),
+                            hasInitializer(ignoringParenImpCasts(
+                                hasType(qualType(equalsBoundNode("type"))))))));
+}
+
+TEST(EqualsBoundNodeMatcher, Stmt) {
+  EXPECT_TRUE(
+      matches("void f() { if(true) {} }",
+              stmt(allOf(ifStmt().bind("if"),
+                         hasParent(stmt(has(stmt(equalsBoundNode("if")))))))));
+
+  EXPECT_TRUE(notMatches(
+      "void f() { if(true) { if (true) {} } }",
+      stmt(allOf(ifStmt().bind("if"), has(stmt(equalsBoundNode("if")))))));
+}
+
+TEST(EqualsBoundNodeMatcher, Decl) {
+  EXPECT_TRUE(matches(
+      "class X { class Y {}; };",
+      decl(allOf(recordDecl(hasName("::X::Y")).bind("record"),
+                 hasParent(decl(has(decl(equalsBoundNode("record")))))))));
+
+  EXPECT_TRUE(notMatches("class X { class Y {}; };",
+                         decl(allOf(recordDecl(hasName("::X")).bind("record"),
+                                    has(decl(equalsBoundNode("record")))))));
+}
+
+TEST(EqualsBoundNodeMatcher, Type) {
+  EXPECT_TRUE(matches(
+      "class X { int a; int b; };",
+      recordDecl(
+          has(fieldDecl(hasName("a"), hasType(type().bind("t")))),
+          has(fieldDecl(hasName("b"), hasType(type(equalsBoundNode("t"))))))));
+
+  EXPECT_TRUE(notMatches(
+      "class X { int a; double b; };",
+      recordDecl(
+          has(fieldDecl(hasName("a"), hasType(type().bind("t")))),
+          has(fieldDecl(hasName("b"), hasType(type(equalsBoundNode("t"))))))));
+}
+
+TEST(EqualsBoundNodeMatcher, UsingForEachDescendant) {
+
+  EXPECT_TRUE(matchAndVerifyResultTrue(
+      "int f() {"
+      "  if (1) {"
+      "    int i = 9;"
+      "  }"
+      "  int j = 10;"
+      "  {"
+      "    float k = 9.0;"
+      "  }"
+      "  return 0;"
+      "}",
+      // Look for variable declarations within functions whose type is the same
+      // as the function return type.
+      functionDecl(returns(qualType().bind("type")),
+                   forEachDescendant(varDecl(hasType(
+                       qualType(equalsBoundNode("type")))).bind("decl"))),
+      // Only i and j should match, not k.
+      new VerifyIdIsBoundTo("decl", 2)));
+}
+
+TEST(EqualsBoundNodeMatcher, FiltersMatchedCombinations) {
+  EXPECT_TRUE(matchAndVerifyResultTrue(
+      "void f() {"
+      "  int x;"
+      "  double d;"
+      "  x = d + x - d + x;"
+      "}",
+      functionDecl(
+          hasName("f"), forEachDescendant(varDecl().bind("d")),
+          forEachDescendant(declRefExpr(to(decl(equalsBoundNode("d")))))),
+      new VerifyIdIsBoundTo("d", 5)));
+}
+
 } // end namespace ast_matchers
 } // end namespace clang
-- 
2.40.0