]> granicus.if.org Git - clang/commitdiff
Change the behavior of the isDerivedFrom-matcher to not match on the
authorDaniel Jasper <djasper@google.com>
Fri, 7 Sep 2012 12:48:17 +0000 (12:48 +0000)
committerDaniel Jasper <djasper@google.com>
Fri, 7 Sep 2012 12:48:17 +0000 (12:48 +0000)
class itself. This caused some confusion (intuitively, a class is not
derived from itself) and makes it hard to write certain matchers, e.g.
"match and bind any pair of base and subclass".

The original behavior can be achieved with a new isA-matcher.  Similar
to all other matchers, this matcher has the same behavior and name as
the corresponding AST-entity - in this case the isa<>() function.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@163385 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/ASTMatchers/ASTMatchers.h
lib/ASTMatchers/ASTMatchFinder.cpp
unittests/ASTMatchers/ASTMatchersTest.cpp

index 23743e529bd8c9018a22617bf33a1cd15fae78b0..91a3ecfd492798f3cef2ca5e7dd360044d493023 100644 (file)
@@ -1108,11 +1108,11 @@ AST_MATCHER_P(CXXOperatorCallExpr,
 /// \brief Matches C++ classes that are directly or indirectly derived from
 /// a class matching \c Base.
 ///
-/// Note that a class is considered to be also derived from itself.
+/// Note that a class is not considered to be derived from itself.
 ///
-/// Example matches X, Y, Z, C (Base == hasName("X"))
+/// Example matches Y, Z, C (Base == hasName("X"))
 /// \code
-///   class X;                // A class is considered to be derived from itself
+///   class X;
 ///   class Y : public X {};  // directly derived
 ///   class Z : public Y {};  // indirectly derived
 ///   typedef X A;
@@ -1137,6 +1137,18 @@ inline internal::Matcher<CXXRecordDecl> isDerivedFrom(StringRef BaseName) {
   return isDerivedFrom(hasName(BaseName));
 }
 
+/// \brief Similar to \c isDerivedFrom(), but also matches classes that directly
+/// match \c Base.
+inline internal::Matcher<CXXRecordDecl> isA(internal::Matcher<NamedDecl> Base) {
+  return anyOf(Base, isDerivedFrom(Base));
+}
+
+/// \brief Overloaded method as shortcut for \c isA(hasName(...)).
+inline internal::Matcher<CXXRecordDecl> isA(StringRef BaseName) {
+  assert(!BaseName.empty());
+  return isA(hasName(BaseName));
+}
+
 /// \brief Matches AST nodes that have child AST nodes that match the
 /// provided matcher.
 ///
index 54b05b3939034d359221041ecc6b9dcd779eb9a6..afab1df4a63a0ae070613d97f4ddf7bb589969b8 100644 (file)
@@ -277,7 +277,7 @@ public:
   bool VisitTypedefDecl(TypedefDecl *DeclNode) {
     // When we see 'typedef A B', we add name 'B' to the set of names
     // A's canonical type maps to.  This is necessary for implementing
-    // IsDerivedFrom(x) properly, where x can be the name of the base
+    // isDerivedFrom(x) properly, where x can be the name of the base
     // class or any of its aliases.
     //
     // In general, the is-alias-of (as defined by typedefs) relation
@@ -298,7 +298,7 @@ public:
     //      `- E
     //
     // It is wrong to assume that the relation is a chain.  A correct
-    // implementation of IsDerivedFrom() needs to recognize that B and
+    // implementation of isDerivedFrom() needs to recognize that B and
     // E are aliases, even though neither is a typedef of the other.
     // Therefore, we cannot simply walk through one typedef chain to
     // find out whether the type name matches.
@@ -468,13 +468,11 @@ private:
 };
 
 // Returns true if the given class is directly or indirectly derived
-// from a base type with the given name.  A class is considered to be
-// also derived from itself.
+// from a base type with the given name.  A class is not considered to be
+// derived from itself.
 bool MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *Declaration,
                                          const Matcher<NamedDecl> &Base,
                                          BoundNodesTreeBuilder *Builder) {
-  if (Base.matches(*Declaration, this, Builder))
-    return true;
   if (!Declaration->hasDefinition())
     return false;
   typedef CXXRecordDecl::base_class_const_iterator BaseIterator;
@@ -523,6 +521,8 @@ bool MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *Declaration,
     }
     assert(ClassDecl != NULL);
     assert(ClassDecl != Declaration);
+    if (Base.matches(*ClassDecl, this, Builder))
+      return true;
     if (classIsDerivedFrom(ClassDecl, Base, Builder))
       return true;
   }
index 7adc71837d786fd6d7e36c1932551bf395fc89af..55e22e927a0a5151a4a3b2fbf8d98fcf902223aa 100644 (file)
@@ -101,12 +101,19 @@ TEST(DeclarationMatcher, ClassIsDerived) {
   DeclarationMatcher IsDerivedFromX = recordDecl(isDerivedFrom("X"));
 
   EXPECT_TRUE(matches("class X {}; class Y : public X {};", IsDerivedFromX));
-  EXPECT_TRUE(matches("class X {}; class Y : public X {};", IsDerivedFromX));
-  EXPECT_TRUE(matches("class X {};", IsDerivedFromX));
-  EXPECT_TRUE(matches("class X;", IsDerivedFromX));
+  EXPECT_TRUE(notMatches("class X {};", IsDerivedFromX));
+  EXPECT_TRUE(notMatches("class X;", IsDerivedFromX));
   EXPECT_TRUE(notMatches("class Y;", IsDerivedFromX));
   EXPECT_TRUE(notMatches("", IsDerivedFromX));
 
+  DeclarationMatcher IsAX = recordDecl(isA("X"));
+
+  EXPECT_TRUE(matches("class X {}; class Y : public X {};", IsAX));
+  EXPECT_TRUE(matches("class X {};", IsAX));
+  EXPECT_TRUE(matches("class X;", IsAX));
+  EXPECT_TRUE(notMatches("class Y;", IsAX));
+  EXPECT_TRUE(notMatches("", IsAX));
+
   DeclarationMatcher ZIsDerivedFromX =
       recordDecl(hasName("Z"), isDerivedFrom("X"));
   EXPECT_TRUE(
@@ -458,7 +465,6 @@ TEST(DeclarationMatcher, MatchNot) {
   DeclarationMatcher NotClassX =
       recordDecl(
           isDerivedFrom("Y"),
-          unless(hasName("Y")),
           unless(hasName("X")));
   EXPECT_TRUE(notMatches("", NotClassX));
   EXPECT_TRUE(notMatches("class Y {};", NotClassX));