njames93 created this revision.
njames93 added reviewers: klimek, aaron.ballman, jkorous.
Herald added subscribers: cfe-commits, dexonsmith.
Herald added a project: clang.

Adds a matcher called `hasDirectBase` for matching the `CXXBaseSpecifier` of a 
class that directly derives from another class.
Adds a matcher called `hasClass` that matches on the class that a 
`CXXBaseSpecifier` refers to.
Also removed the `CXXBaseSpecifier` overload for the `hasType` Matcher in 
favour of this `hasClass` matcher.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D81552

Files:
  clang/docs/LibASTMatchersReference.html
  clang/include/clang/ASTMatchers/ASTMatchers.h
  clang/include/clang/ASTMatchers/ASTMatchersInternal.h
  clang/lib/ASTMatchers/Dynamic/Registry.cpp
  clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp

Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===================================================================
--- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -2997,26 +2997,24 @@
 }
 
 TEST(HasAnyBase, DirectBase) {
-  EXPECT_TRUE(matches(
-      "struct Base {};"
-      "struct ExpectedMatch : Base {};",
-      cxxRecordDecl(hasName("ExpectedMatch"),
-                    hasAnyBase(hasType(cxxRecordDecl(hasName("Base")))))));
+  EXPECT_TRUE(matches("struct Base {};"
+                      "struct ExpectedMatch : Base {};",
+                      cxxRecordDecl(hasName("ExpectedMatch"),
+                                    hasAnyBase(hasClass(hasName("Base"))))));
 }
 
 TEST(HasAnyBase, IndirectBase) {
-  EXPECT_TRUE(matches(
-      "struct Base {};"
-      "struct Intermediate : Base {};"
-      "struct ExpectedMatch : Intermediate {};",
-      cxxRecordDecl(hasName("ExpectedMatch"),
-                    hasAnyBase(hasType(cxxRecordDecl(hasName("Base")))))));
+  EXPECT_TRUE(matches("struct Base {};"
+                      "struct Intermediate : Base {};"
+                      "struct ExpectedMatch : Intermediate {};",
+                      cxxRecordDecl(hasName("ExpectedMatch"),
+                                    hasAnyBase(hasClass(hasName("Base"))))));
 }
 
 TEST(HasAnyBase, NoBase) {
   EXPECT_TRUE(notMatches("struct Foo {};"
                          "struct Bar {};",
-                         cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl())))));
+                         cxxRecordDecl(hasAnyBase(hasClass(cxxRecordDecl())))));
 }
 
 TEST(IsPublicBase, Public) {
@@ -3117,5 +3115,57 @@
                          cxxRecordDecl(hasAnyBase(isVirtual()))));
 }
 
+TEST(BaseSpecifier, hasDirectBase) {
+  EXPECT_TRUE(matches(
+      R"cc(
+    class Base {};
+    class Derived : Base{};
+    )cc",
+      cxxRecordDecl(hasName("Derived"),
+                    hasDirectBase(hasClass(hasName("Base"))))));
+
+  StringRef MultiDerived = R"cc(
+    class Base {};
+    class Base2 {};
+    class Derived : Base, Base2{};
+    )cc";
+
+  EXPECT_TRUE(matches(MultiDerived,
+                      cxxRecordDecl(hasName("Derived"),
+                                    hasDirectBase(hasClass(hasName("Base"))))));
+  EXPECT_TRUE(matches(
+      MultiDerived, cxxRecordDecl(hasName("Derived"),
+                                  hasDirectBase(hasClass(hasName("Base2"))))));
+
+  StringRef Indirect = R"cc(
+    class Base {};
+    class Intermediate : Base {};
+    class Derived : Intermediate{};
+    )cc";
+
+  EXPECT_TRUE(
+      matches(Indirect,
+              cxxRecordDecl(hasName("Derived"),
+                            hasDirectBase(hasClass(hasName("Intermediate"))))));
+  EXPECT_TRUE(notMatches(
+      Indirect, cxxRecordDecl(hasName("Derived"),
+                              hasDirectBase(hasClass(hasName("Base"))))));
+
+  // Only really here to make sure the asserts don't fire
+  EXPECT_FALSE(
+      matches(R"cc(
+    class Base {};
+    class Derived : BAse {};
+  )cc",
+              cxxRecordDecl(hasDirectBase(hasClass(hasName("Base"))))));
+
+  EXPECT_FALSE(
+      matches(R"cc(
+    using Base = int;
+    class Derived : Base {};
+  )cc",
+              cxxRecordDecl(hasDirectBase(hasClass(hasName("Base"))))));
+}
+
 } // namespace ast_matchers
 } // namespace clang
Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp
===================================================================
--- clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -261,6 +261,7 @@
   REGISTER_MATCHER(hasCanonicalType);
   REGISTER_MATCHER(hasCaseConstant);
   REGISTER_MATCHER(hasCastKind);
+  REGISTER_MATCHER(hasClass);
   REGISTER_MATCHER(hasCondition);
   REGISTER_MATCHER(hasConditionVariableStatement);
   REGISTER_MATCHER(hasDecayedType);
@@ -271,6 +272,7 @@
   REGISTER_MATCHER(hasDefinition);
   REGISTER_MATCHER(hasDescendant);
   REGISTER_MATCHER(hasDestinationType);
+  REGISTER_MATCHER(hasDirectBase);
   REGISTER_MATCHER(hasDynamicExceptionSpec);
   REGISTER_MATCHER(hasEitherOperand);
   REGISTER_MATCHER(hasElementType);
Index: clang/include/clang/ASTMatchers/ASTMatchersInternal.h
===================================================================
--- clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -130,9 +130,6 @@
     return TSI->getType();
   return QualType();
 }
-inline QualType getUnderlyingType(const CXXBaseSpecifier &Node) {
-  return Node.getType();
-}
 
 /// Unifies obtaining the FunctionProtoType pointer from both
 /// FunctionProtoType and FunctionDecl nodes..
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===================================================================
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -2864,7 +2864,7 @@
 /// BaseSpecMatcher.
 ///
 /// Example:
-/// matcher hasAnyBase(hasType(cxxRecordDecl(hasName("SpecialBase")))))
+/// matcher hasAnyBase(hasClass(hasName("SpecialBase")))
 /// \code
 ///   class Foo;
 ///   class Bar : Foo {};
@@ -2880,6 +2880,27 @@
   return internal::matchesAnyBase(Node, BaseSpecMatcher, Finder, Builder);
 }
 
+/// Matches C++ classes that have a direct base matching \p BaseSpecMatcher.
+///
+/// Example:
+/// matcher hasDirectBase(hasClass(hasName("SpecialBase")))
+/// \code
+///   class Foo;
+///   class Bar : Foo {};
+///   class Baz : Bar {};
+///   class SpecialBase;
+///   class Proxy : SpecialBase {};  // matches Proxy
+///   class IndirectlyDerived : Proxy {};  // doesn't match
+/// \endcode
+AST_MATCHER_P(CXXRecordDecl, hasDirectBase, internal::Matcher<CXXBaseSpecifier>,
+              BaseSpecMatcher) {
+
+  return Node.hasDefinition() &&
+         llvm::any_of(Node.bases(), [&](const CXXBaseSpecifier &Base) {
+           return BaseSpecMatcher.matches(Base, Finder, Builder);
+         });
+}
+
 /// Similar to \c isDerivedFrom(), but also matches classes that directly
 /// match \c Base.
 AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
@@ -3510,19 +3531,10 @@
 ///  class Y { friend class X; };
 /// \endcode
 ///
-/// Example matches class Derived
-/// (matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))
-/// \code
-/// class Base {};
-/// class Derived : Base {};
-/// \endcode
 ///
-/// Usable as: Matcher<Expr>, Matcher<FriendDecl>, Matcher<ValueDecl>,
-/// Matcher<CXXBaseSpecifier>
+/// Usable as: Matcher<Expr>, Matcher<FriendDecl>, Matcher<ValueDecl>
 AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
-    hasType,
-    AST_POLYMORPHIC_SUPPORTED_TYPES(Expr, FriendDecl, ValueDecl,
-                                    CXXBaseSpecifier),
+    hasType, AST_POLYMORPHIC_SUPPORTED_TYPES(Expr, FriendDecl, ValueDecl),
     internal::Matcher<Decl>, InnerMatcher, 1) {
   QualType QT = internal::getUnderlyingType(Node);
   if (!QT.isNull())
@@ -3530,6 +3542,23 @@
   return false;
 }
 
+/// Matches if the Base specifier refers to the given class matcher.
+///
+/// Example matches class Derived
+/// (matcher = cxxRecordDecl(hasAnyBase(hasClass(hasName("Base")))))
+/// \code
+/// class Base {};
+/// class Derived : Base {};
+/// \endcode
+AST_MATCHER_P(CXXBaseSpecifier, hasClass, internal::Matcher<CXXRecordDecl>,
+              InnerMatcher) {
+  QualType QT = Node.getType();
+  assert(!QT.isNull() && "CXXBaseSpecifier shouldn't have a null type");
+  const CXXRecordDecl *RD = QT->getAsCXXRecordDecl();
+  assert(RD && "CXXBaseSpecifier should refer to a CXXRecordDecl");
+  return InnerMatcher.matches(*RD, Finder, Builder);
+}
+
 /// Matches if the type location of the declarator decl's type matches
 /// the inner matcher.
 ///
Index: clang/docs/LibASTMatchersReference.html
===================================================================
--- clang/docs/LibASTMatchersReference.html
+++ clang/docs/LibASTMatchersReference.html
@@ -1226,6 +1226,11 @@
 </pre></td></tr>
 
 
+<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html";>Stmt</a>&gt;</td><td class="name" onclick="toggle('fixedPointLiteral0')"><a name="fixedPointLiteral0Anchor">fixedPointLiteral</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1FixedPointLiteral.html";>FixedPointLiteral</a>&gt;...</td></tr>
+<tr><td colspan="4" class="doc" id="fixedPointLiteral0"><pre>Matches fixed point literals
+</pre></td></tr>
+
+
 <tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html";>Stmt</a>&gt;</td><td class="name" onclick="toggle('floatLiteral0')"><a name="floatLiteral0Anchor">floatLiteral</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1FloatingLiteral.html";>FloatingLiteral</a>&gt;...</td></tr>
 <tr><td colspan="4" class="doc" id="floatLiteral0"><pre>Matches float literals of all sizes / encodings, e.g.
 1.0, 1.0f, 1.0L and 1e10.
@@ -5219,30 +5224,13 @@
 </pre></td></tr>
 
 
-<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html";>CXXBaseSpecifier</a>&gt;</td><td class="name" onclick="toggle('hasType7')"><a name="hasType7Anchor">hasType</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html";>Decl</a>&gt; InnerMatcher</td></tr>
-<tr><td colspan="4" class="doc" id="hasType7"><pre>Overloaded to match the declaration of the expression's or value
-declaration's type.
-
-In case of a value declaration (for example a variable declaration),
-this resolves one layer of indirection. For example, in the value
-declaration "X x;", cxxRecordDecl(hasName("X")) matches the declaration of
-X, while varDecl(hasType(cxxRecordDecl(hasName("X")))) matches the
-declaration of x.
-
-Example matches x (matcher = expr(hasType(cxxRecordDecl(hasName("X")))))
-            and z (matcher = varDecl(hasType(cxxRecordDecl(hasName("X")))))
-            and friend class X (matcher = friendDecl(hasType("X"))
- class X {};
- void y(X &amp;x) { x; X z; }
- class Y { friend class X; };
+<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html";>CXXBaseSpecifier</a>&gt;</td><td class="name" onclick="toggle('hasClass0')"><a name="hasClass0Anchor">hasClass</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html";>CXXRecordDecl</a>&gt; InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="hasClass0"><pre>Matches if the Base specifier refers to the given class matcher.
 
 Example matches class Derived
-(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))
+(matcher = cxxRecordDecl(hasAnyBase(hasClass(hasName("Base")))))
 class Base {};
 class Derived : Base {};
-
-Usable as: Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html";>Expr</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1FriendDecl.html";>FriendDecl</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html";>ValueDecl</a>&gt;,
-Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html";>CXXBaseSpecifier</a>&gt;
 </pre></td></tr>
 
 
@@ -5632,18 +5620,33 @@
 <tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html";>CXXRecordDecl</a>&gt;</td><td class="name" onclick="toggle('hasAnyBase0')"><a name="hasAnyBase0Anchor">hasAnyBase</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html";>CXXBaseSpecifier</a>&gt; BaseSpecMatcher</td></tr>
 <tr><td colspan="4" class="doc" id="hasAnyBase0"><pre>Matches C++ classes that have a direct or indirect base matching BaseSpecMatcher.
 
-Example matches DirectlyDerived, IndirectlyDerived (BaseSpecMatcher ==
-hasType(cxxRecordDecl(hasName("SpecialBase")))) class Foo;
+Example:
+matcher hasAnyBase(hasClass(hasName("SpecialBase")))
+  class Foo;
   class Bar : Foo {};
   class Baz : Bar {};
   class SpecialBase;
-  class DirectlyDerived : SpecialBase {};  // directly derived
-  class IndirectlyDerived : DirectlyDerived {};  // indirectly derived
+  class Proxy : SpecialBase {};  // matches Proxy
+  class IndirectlyDerived : Proxy {};  //matches IndirectlyDerived
 
 FIXME: Refactor this and isDerivedFrom to reuse implementation.
 </pre></td></tr>
 
 
+<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html";>CXXRecordDecl</a>&gt;</td><td class="name" onclick="toggle('hasDirectBase0')"><a name="hasDirectBase0Anchor">hasDirectBase</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html";>CXXBaseSpecifier</a>&gt; BaseSpecMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="hasDirectBase0"><pre>Matches C++ classes that have a direct base matching BaseSpecMatcher.
+
+Example:
+matcher hasDirectBase(hasClass(hasName("SpecialBase")))
+  class Foo;
+  class Bar : Foo {};
+  class Baz : Bar {};
+  class SpecialBase;
+  class Proxy : SpecialBase {};  // matches Proxy
+  class IndirectlyDerived : Proxy {};  // doesn't match
+</pre></td></tr>
+
+
 <tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html";>CXXRecordDecl</a>&gt;</td><td class="name" onclick="toggle('hasMethod0')"><a name="hasMethod0Anchor">hasMethod</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html";>CXXMethodDecl</a>&gt; InnerMatcher</td></tr>
 <tr><td colspan="4" class="doc" id="hasMethod0"><pre>Matches the first method of a class or struct that satisfies InnerMatcher.
 
@@ -6201,13 +6204,8 @@
  void y(X &amp;x) { x; X z; }
  class Y { friend class X; };
 
-Example matches class Derived
-(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))
-class Base {};
-class Derived : Base {};
 
-Usable as: Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html";>Expr</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1FriendDecl.html";>FriendDecl</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html";>ValueDecl</a>&gt;,
-Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html";>CXXBaseSpecifier</a>&gt;
+Usable as: Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html";>Expr</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1FriendDecl.html";>FriendDecl</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html";>ValueDecl</a>&gt;
 </pre></td></tr>
 
 
@@ -6421,13 +6419,8 @@
  void y(X &amp;x) { x; X z; }
  class Y { friend class X; };
 
-Example matches class Derived
-(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))
-class Base {};
-class Derived : Base {};
 
-Usable as: Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html";>Expr</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1FriendDecl.html";>FriendDecl</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html";>ValueDecl</a>&gt;,
-Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html";>CXXBaseSpecifier</a>&gt;
+Usable as: Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html";>Expr</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1FriendDecl.html";>FriendDecl</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html";>ValueDecl</a>&gt;
 </pre></td></tr>
 
 
@@ -7815,13 +7808,8 @@
  void y(X &amp;x) { x; X z; }
  class Y { friend class X; };
 
-Example matches class Derived
-(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))
-class Base {};
-class Derived : Base {};
 
-Usable as: Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html";>Expr</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1FriendDecl.html";>FriendDecl</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html";>ValueDecl</a>&gt;,
-Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html";>CXXBaseSpecifier</a>&gt;
+Usable as: Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html";>Expr</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1FriendDecl.html";>FriendDecl</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html";>ValueDecl</a>&gt;
 </pre></td></tr>
 
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to