[clang] 1a5c97f - [ASTMatchers] Matchers related to C++ inheritance
Jan Korous via cfe-commits
cfe-commits at lists.llvm.org
Fri May 29 12:38:22 PDT 2020
Author: Jan Korous
Date: 2020-05-29T12:38:01-07:00
New Revision: 1a5c97f3a4b88438b19ff34a285e559e57b1e9d4
URL: https://github.com/llvm/llvm-project/commit/1a5c97f3a4b88438b19ff34a285e559e57b1e9d4
DIFF: https://github.com/llvm/llvm-project/commit/1a5c97f3a4b88438b19ff34a285e559e57b1e9d4.diff
LOG: [ASTMatchers] Matchers related to C++ inheritance
Differential Revision: https://reviews.llvm.org/D79063
Added:
Modified:
clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp
clang/docs/LibASTMatchersReference.html
clang/include/clang/AST/ASTTypeTraits.h
clang/include/clang/ASTMatchers/ASTMatchers.h
clang/include/clang/ASTMatchers/ASTMatchersInternal.h
clang/lib/AST/ASTTypeTraits.cpp
clang/lib/ASTMatchers/ASTMatchersInternal.cpp
clang/lib/ASTMatchers/Dynamic/Registry.cpp
clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp b/clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp
index 12fb7d8a7ae8..5b6988509742 100644
--- a/clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp
@@ -59,8 +59,9 @@ void NonPrivateMemberVariablesInClassesCheck::registerMatchers(
// If we are ok with public fields, then we only want to complain about
// protected fields, else we want to complain about all non-private fields.
// We can ignore public member variables in structs/classes, in unions.
- auto InterestingField = fieldDecl(
- IgnorePublicMemberVariables ? isProtected() : unless(isPrivate()));
+ auto InterestingField = IgnorePublicMemberVariables
+ ? fieldDecl(isProtected())
+ : fieldDecl(unless(isPrivate()));
// We only want the records that not only contain the mutable data (non-static
// member variables), but also have some logic (non-static, non-implicit
diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html
index 9db6795eb5fa..bb5e4984fcdf 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -2175,6 +2175,75 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>></td><td class="name" onclick="toggle('isPrivate1')"><a name="isPrivate1Anchor">isPrivate</a></td><td></td></tr>
+<tr><td colspan="4" class="doc" id="isPrivate1"><pre>Matches private C++ declarations and C++ base specifers that specify private
+inheritance.
+
+Examples:
+ class C {
+ public: int a;
+ protected: int b;
+ private: int c; // fieldDecl(isPrivate()) matches 'c'
+ };
+
+ struct Base {};
+ struct Derived1 : private Base {}; // matches 'Base'
+ class Derived2 : Base {}; // matches 'Base'
+</pre></td></tr>
+
+
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>></td><td class="name" onclick="toggle('isProtected1')"><a name="isProtected1Anchor">isProtected</a></td><td></td></tr>
+<tr><td colspan="4" class="doc" id="isProtected1"><pre>Matches protected C++ declarations and C++ base specifers that specify
+protected inheritance.
+
+Examples:
+ class C {
+ public: int a;
+ protected: int b; // fieldDecl(isProtected()) matches 'b'
+ private: int c;
+ };
+
+ class Base {};
+ class Derived : protected Base {}; // matches 'Base'
+</pre></td></tr>
+
+
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>></td><td class="name" onclick="toggle('isPublic1')"><a name="isPublic1Anchor">isPublic</a></td><td></td></tr>
+<tr><td colspan="4" class="doc" id="isPublic1"><pre>Matches public C++ declarations and C++ base specifers that specify public
+inheritance.
+
+Examples:
+ class C {
+ public: int a; // fieldDecl(isPublic()) matches 'a'
+ protected: int b;
+ private: int c;
+ };
+
+ class Base {};
+ class Derived1 : public Base {}; // matches 'Base'
+ struct Derived2 : Base {}; // matches 'Base'
+</pre></td></tr>
+
+
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>></td><td class="name" onclick="toggle('isVirtual1')"><a name="isVirtual1Anchor">isVirtual</a></td><td></td></tr>
+<tr><td colspan="4" class="doc" id="isVirtual1"><pre>Matches declarations of virtual methods and C++ base specifers that specify
+virtual inheritance.
+
+Example:
+ class A {
+ public:
+ virtual void x(); // matches x
+ };
+
+Example:
+ class Base {};
+ class DirectlyDerived : virtual Base {}; // matches Base
+ class IndirectlyDerived : DirectlyDerived, Base {}; // matches Base
+
+Usable as: Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html">CXXMethodDecl</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>>
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBoolLiteralExpr.html">CXXBoolLiteralExpr</a>></td><td class="name" onclick="toggle('equals5')"><a name="equals5Anchor">equals</a></td><td>bool Value</td></tr>
<tr><td colspan="4" class="doc" id="equals5"><pre></pre></td></tr>
@@ -2562,14 +2631,21 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html">CXXMethodDecl</a>></td><td class="name" onclick="toggle('isVirtual0')"><a name="isVirtual0Anchor">isVirtual</a></td><td></td></tr>
-<tr><td colspan="4" class="doc" id="isVirtual0"><pre>Matches if the given method declaration is virtual.
+<tr><td colspan="4" class="doc" id="isVirtual0"><pre>Matches declarations of virtual methods and C++ base specifers that specify
+virtual inheritance.
-Given
+Example:
class A {
public:
- virtual void x();
+ virtual void x(); // matches x
};
- matches A::x
+
+Example:
+ class Base {};
+ class DirectlyDerived : virtual Base {}; // matches Base
+ class IndirectlyDerived : DirectlyDerived, Base {}; // matches Base
+
+Usable as: Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html">CXXMethodDecl</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>>
</pre></td></tr>
@@ -3012,44 +3088,52 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('isPrivate0')"><a name="isPrivate0Anchor">isPrivate</a></td><td></td></tr>
-<tr><td colspan="4" class="doc" id="isPrivate0"><pre>Matches private C++ declarations.
+<tr><td colspan="4" class="doc" id="isPrivate0"><pre>Matches private C++ declarations and C++ base specifers that specify private
+inheritance.
-Given
+Examples:
class C {
public: int a;
protected: int b;
- private: int c;
+ private: int c; // fieldDecl(isPrivate()) matches 'c'
};
-fieldDecl(isPrivate())
- matches 'int c;'
+
+ struct Base {};
+ struct Derived1 : private Base {}; // matches 'Base'
+ class Derived2 : Base {}; // matches 'Base'
</pre></td></tr>
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('isProtected0')"><a name="isProtected0Anchor">isProtected</a></td><td></td></tr>
-<tr><td colspan="4" class="doc" id="isProtected0"><pre>Matches protected C++ declarations.
+<tr><td colspan="4" class="doc" id="isProtected0"><pre>Matches protected C++ declarations and C++ base specifers that specify
+protected inheritance.
-Given
+Examples:
class C {
public: int a;
- protected: int b;
+ protected: int b; // fieldDecl(isProtected()) matches 'b'
private: int c;
};
-fieldDecl(isProtected())
- matches 'int b;'
+
+ class Base {};
+ class Derived : protected Base {}; // matches 'Base'
</pre></td></tr>
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('isPublic0')"><a name="isPublic0Anchor">isPublic</a></td><td></td></tr>
-<tr><td colspan="4" class="doc" id="isPublic0"><pre>Matches public C++ declarations.
+<tr><td colspan="4" class="doc" id="isPublic0"><pre>Matches public C++ declarations and C++ base specifers that specify public
+inheritance.
-Given
+Examples:
class C {
- public: int a;
+ public: int a; // fieldDecl(isPublic()) matches 'a'
protected: int b;
private: int c;
};
-fieldDecl(isPublic())
- matches 'int a;'
+
+ class Base {};
+ class Derived1 : public Base {}; // matches 'Base'
+ struct Derived2 : Base {}; // matches 'Base'
</pre></td></tr>
@@ -5135,6 +5219,33 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>></td><td class="name" onclick="toggle('hasType7')"><a name="hasType7Anchor">hasType</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>> 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 &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<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1FriendDecl.html">FriendDecl</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html">ValueDecl</a>>,
+Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>>
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXConstructExpr.html">CXXConstructExpr</a>></td><td class="name" onclick="toggle('forEachArgumentWithParam1')"><a name="forEachArgumentWithParam1Anchor">forEachArgumentWithParam</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> ArgMatcher, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ParmVarDecl.html">ParmVarDecl</a>> ParamMatcher</td></tr>
<tr><td colspan="4" class="doc" id="forEachArgumentWithParam1"><pre>Matches all arguments and their respective ParmVarDecl.
@@ -5518,6 +5629,21 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>></td><td class="name" onclick="toggle('hasAnyBase0')"><a name="hasAnyBase0Anchor">hasAnyBase</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>> 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;
+ class Bar : Foo {};
+ class Baz : Bar {};
+ class SpecialBase;
+ class DirectlyDerived : SpecialBase {}; // directly derived
+ class IndirectlyDerived : DirectlyDerived {}; // indirectly derived
+
+FIXME: Refactor this and isDerivedFrom to reuse implementation.
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>></td><td class="name" onclick="toggle('hasMethod0')"><a name="hasMethod0Anchor">hasMethod</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html">CXXMethodDecl</a>> InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasMethod0"><pre>Matches the first method of a class or struct that satisfies InnerMatcher.
@@ -6075,7 +6201,13 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
void y(X &x) { x; X z; }
class Y { friend class X; };
-Usable as: Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html">ValueDecl</a>>
+Example matches class Derived
+(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))
+class Base {};
+class Derived : Base {};
+
+Usable as: Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1FriendDecl.html">FriendDecl</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html">ValueDecl</a>>,
+Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>>
</pre></td></tr>
@@ -6289,7 +6421,13 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
void y(X &x) { x; X z; }
class Y { friend class X; };
-Usable as: Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html">ValueDecl</a>>
+Example matches class Derived
+(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))
+class Base {};
+class Derived : Base {};
+
+Usable as: Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1FriendDecl.html">FriendDecl</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html">ValueDecl</a>>,
+Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>>
</pre></td></tr>
@@ -7677,7 +7815,13 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
void y(X &x) { x; X z; }
class Y { friend class X; };
-Usable as: Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html">ValueDecl</a>>
+Example matches class Derived
+(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))
+class Base {};
+class Derived : Base {};
+
+Usable as: Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1FriendDecl.html">FriendDecl</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html">ValueDecl</a>>,
+Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>>
</pre></td></tr>
diff --git a/clang/include/clang/AST/ASTTypeTraits.h b/clang/include/clang/AST/ASTTypeTraits.h
index cd80e9bc3808..67fa4ab1b6a4 100644
--- a/clang/include/clang/AST/ASTTypeTraits.h
+++ b/clang/include/clang/AST/ASTTypeTraits.h
@@ -16,6 +16,7 @@
#define LLVM_CLANG_AST_ASTTYPETRAITS_H
#include "clang/AST/ASTFwd.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TypeLoc.h"
@@ -136,6 +137,7 @@ class ASTNodeKind {
NKI_QualType,
NKI_TypeLoc,
NKI_LastKindWithoutPointerIdentity = NKI_TypeLoc,
+ NKI_CXXBaseSpecifier,
NKI_CXXCtorInitializer,
NKI_NestedNameSpecifier,
NKI_Decl,
@@ -198,6 +200,7 @@ KIND_TO_KIND_ID(Decl)
KIND_TO_KIND_ID(Stmt)
KIND_TO_KIND_ID(Type)
KIND_TO_KIND_ID(OMPClause)
+KIND_TO_KIND_ID(CXXBaseSpecifier)
#define DECL(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED##Decl)
#include "clang/AST/DeclNodes.inc"
#define STMT(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED)
@@ -510,6 +513,10 @@ template <>
struct DynTypedNode::BaseConverter<
TypeLoc, void> : public ValueConverter<TypeLoc> {};
+template <>
+struct DynTypedNode::BaseConverter<CXXBaseSpecifier, void>
+ : public PtrConverter<CXXBaseSpecifier> {};
+
// The only operation we allow on unsupported types is \c get.
// This allows to conveniently use \c DynTypedNode when having an arbitrary
// AST node that is not supported, but prevents misuse - a user cannot create
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index a3747faa139c..6dbcc01442ed 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -47,6 +47,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/Attr.h"
+#include "clang/AST/CXXInheritance.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclFriend.h"
@@ -548,52 +549,72 @@ extern const internal::VariadicDynCastAllOfMatcher<Decl,
extern const internal::VariadicDynCastAllOfMatcher<Decl, TemplateTypeParmDecl>
templateTypeParmDecl;
-/// Matches public C++ declarations.
+/// Matches public C++ declarations and C++ base specifers that specify public
+/// inheritance.
///
-/// Given
+/// Examples:
/// \code
/// class C {
-/// public: int a;
+/// public: int a; // fieldDecl(isPublic()) matches 'a'
/// protected: int b;
/// private: int c;
/// };
/// \endcode
-/// fieldDecl(isPublic())
-/// matches 'int a;'
-AST_MATCHER(Decl, isPublic) {
- return Node.getAccess() == AS_public;
+///
+/// \code
+/// class Base {};
+/// class Derived1 : public Base {}; // matches 'Base'
+/// struct Derived2 : Base {}; // matches 'Base'
+/// \endcode
+AST_POLYMORPHIC_MATCHER(isPublic,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(Decl,
+ CXXBaseSpecifier)) {
+ return getAccessSpecifier(Node) == AS_public;
}
-/// Matches protected C++ declarations.
+/// Matches protected C++ declarations and C++ base specifers that specify
+/// protected inheritance.
///
-/// Given
+/// Examples:
/// \code
/// class C {
/// public: int a;
-/// protected: int b;
+/// protected: int b; // fieldDecl(isProtected()) matches 'b'
/// private: int c;
/// };
/// \endcode
-/// fieldDecl(isProtected())
-/// matches 'int b;'
-AST_MATCHER(Decl, isProtected) {
- return Node.getAccess() == AS_protected;
+///
+/// \code
+/// class Base {};
+/// class Derived : protected Base {}; // matches 'Base'
+/// \endcode
+AST_POLYMORPHIC_MATCHER(isProtected,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(Decl,
+ CXXBaseSpecifier)) {
+ return getAccessSpecifier(Node) == AS_protected;
}
-/// Matches private C++ declarations.
+/// Matches private C++ declarations and C++ base specifers that specify private
+/// inheritance.
///
-/// Given
+/// Examples:
/// \code
/// class C {
/// public: int a;
/// protected: int b;
-/// private: int c;
+/// private: int c; // fieldDecl(isPrivate()) matches 'c'
/// };
/// \endcode
-/// fieldDecl(isPrivate())
-/// matches 'int c;'
-AST_MATCHER(Decl, isPrivate) {
- return Node.getAccess() == AS_private;
+///
+/// \code
+/// struct Base {};
+/// struct Derived1 : private Base {}; // matches 'Base'
+/// class Derived2 : Base {}; // matches 'Base'
+/// \endcode
+AST_POLYMORPHIC_MATCHER(isPrivate,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(Decl,
+ CXXBaseSpecifier)) {
+ return getAccessSpecifier(Node) == AS_private;
}
/// Matches non-static data members that are bit-fields.
@@ -2839,6 +2860,26 @@ AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
return Matcher<ObjCInterfaceDecl>(M).matches(*InterfaceDecl, Finder, Builder);
}
+/// Matches C++ classes that have a direct or indirect base matching \p
+/// BaseSpecMatcher.
+///
+/// Example:
+/// matcher hasAnyBase(hasType(cxxRecordDecl(hasName("SpecialBase")))))
+/// \code
+/// class Foo;
+/// class Bar : Foo {};
+/// class Baz : Bar {};
+/// class SpecialBase;
+/// class Proxy : SpecialBase {}; // matches Proxy
+/// class IndirectlyDerived : Proxy {}; //matches IndirectlyDerived
+/// \endcode
+///
+// FIXME: Refactor this and isDerivedFrom to reuse implementation.
+AST_MATCHER_P(CXXRecordDecl, hasAnyBase, internal::Matcher<CXXBaseSpecifier>,
+ BaseSpecMatcher) {
+ return internal::matchesAnyBase(Node, BaseSpecMatcher, Finder, Builder);
+}
+
/// Similar to \c isDerivedFrom(), but also matches classes that directly
/// match \c Base.
AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
@@ -3469,9 +3510,19 @@ AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
/// class Y { friend class X; };
/// \endcode
///
-/// Usable as: Matcher<Expr>, Matcher<ValueDecl>
+/// 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>
AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
- hasType, AST_POLYMORPHIC_SUPPORTED_TYPES(Expr, FriendDecl, ValueDecl),
+ hasType,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(Expr, FriendDecl, ValueDecl,
+ CXXBaseSpecifier),
internal::Matcher<Decl>, InnerMatcher, 1) {
QualType QT = internal::getUnderlyingType(Node);
if (!QT.isNull())
@@ -5177,17 +5228,28 @@ AST_MATCHER_P(CXXMethodDecl, forEachOverridden,
return Matched;
}
-/// Matches if the given method declaration is virtual.
+/// Matches declarations of virtual methods and C++ base specifers that specify
+/// virtual inheritance.
///
-/// Given
+/// Example:
/// \code
/// class A {
/// public:
-/// virtual void x();
+/// virtual void x(); // matches x
/// };
/// \endcode
-/// matches A::x
-AST_MATCHER(CXXMethodDecl, isVirtual) {
+///
+/// Example:
+/// \code
+/// class Base {};
+/// class DirectlyDerived : virtual Base {}; // matches Base
+/// class IndirectlyDerived : DirectlyDerived, Base {}; // matches Base
+/// \endcode
+///
+/// Usable as: Matcher<CXXMethodDecl>, Matcher<CXXBaseSpecifier>
+AST_POLYMORPHIC_MATCHER(isVirtual,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(CXXMethodDecl,
+ CXXBaseSpecifier)) {
return Node.isVirtual();
}
diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
index e064b28b84f9..fc41407ba296 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -130,6 +130,9 @@ inline QualType getUnderlyingType(const FriendDecl &Node) {
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..
@@ -142,6 +145,15 @@ inline const FunctionProtoType *getFunctionProtoType(const FunctionDecl &Node) {
return Node.getType()->getAs<FunctionProtoType>();
}
+/// Unifies obtaining the access specifier from Decl and CXXBaseSpecifier nodes.
+inline clang::AccessSpecifier getAccessSpecifier(const Decl &Node) {
+ return Node.getAccess();
+}
+
+inline clang::AccessSpecifier getAccessSpecifier(const CXXBaseSpecifier &Node) {
+ return Node.getAccessSpecifier();
+}
+
/// Internal version of BoundNodes. Holds all the bound nodes.
class BoundNodesMap {
public:
@@ -1929,6 +1941,13 @@ using HasOverloadOpNameMatcher = PolymorphicMatcherWithParam1<
HasOverloadOpNameMatcher
hasAnyOverloadedOperatorNameFunc(ArrayRef<const StringRef *> NameRefs);
+/// Returns true if \p Node has a base specifier matching \p BaseSpec.
+///
+/// A class is not considered to be derived from itself.
+bool matchesAnyBase(const CXXRecordDecl &Node,
+ const Matcher<CXXBaseSpecifier> &BaseSpecMatcher,
+ ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder);
+
} // namespace internal
} // namespace ast_matchers
diff --git a/clang/lib/AST/ASTTypeTraits.cpp b/clang/lib/AST/ASTTypeTraits.cpp
index 6404edd79679..37e81e5813ec 100644
--- a/clang/lib/AST/ASTTypeTraits.cpp
+++ b/clang/lib/AST/ASTTypeTraits.cpp
@@ -27,6 +27,7 @@ const ASTNodeKind::KindInfo ASTNodeKind::AllKindInfo[] = {
{ NKI_None, "NestedNameSpecifierLoc" },
{ NKI_None, "QualType" },
{ NKI_None, "TypeLoc" },
+ { NKI_None, "CXXBaseSpecifier" },
{ NKI_None, "CXXCtorInitializer" },
{ NKI_None, "NestedNameSpecifier" },
{ NKI_None, "Decl" },
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index 1ee89ccd3c11..9b69734d075d 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -68,6 +68,30 @@ bool OptionallyVariadicOperator(const DynTypedNode &DynNode,
BoundNodesTreeBuilder *Builder,
ArrayRef<DynTypedMatcher> InnerMatchers);
+bool matchesAnyBase(const CXXRecordDecl &Node,
+ const Matcher<CXXBaseSpecifier> &BaseSpecMatcher,
+ ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) {
+ if (!Node.hasDefinition())
+ return false;
+
+ CXXBasePaths Paths;
+ Paths.setOrigin(&Node);
+
+ const auto basePredicate =
+ [Finder, Builder, &BaseSpecMatcher](const CXXBaseSpecifier *BaseSpec,
+ CXXBasePath &IgnoredParam) {
+ BoundNodesTreeBuilder Result(*Builder);
+ if (BaseSpecMatcher.matches(*BaseSpec, Finder, Builder)) {
+ *Builder = std::move(Result);
+ return true;
+ }
+ return false;
+ };
+
+ return Node.lookupInBases(basePredicate, Paths,
+ /*LookupInDependent =*/true);
+}
+
void BoundNodesTreeBuilder::visitMatches(Visitor *ResultVisitor) {
if (Bindings.empty())
Bindings.push_back(BoundNodesMap());
diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
index 14d9bbb3e52d..950a56f79551 100644
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -237,6 +237,7 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(has);
REGISTER_MATCHER(hasAncestor);
REGISTER_MATCHER(hasAnyArgument);
+ REGISTER_MATCHER(hasAnyBase);
REGISTER_MATCHER(hasAnyClause);
REGISTER_MATCHER(hasAnyConstructorInitializer);
REGISTER_MATCHER(hasAnyDeclaration);
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index 929188abf6ac..9f8538edb35b 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -3006,5 +3006,126 @@ void x() {
EXPECT_TRUE(matchesWithOpenMP(Source6, Matcher));
}
+TEST(HasAnyBase, DirectBase) {
+ EXPECT_TRUE(matches(
+ "struct Base {};"
+ "struct ExpectedMatch : Base {};",
+ cxxRecordDecl(hasName("ExpectedMatch"),
+ hasAnyBase(hasType(cxxRecordDecl(hasName("Base")))))));
+}
+
+TEST(HasAnyBase, IndirectBase) {
+ EXPECT_TRUE(matches(
+ "struct Base {};"
+ "struct Intermediate : Base {};"
+ "struct ExpectedMatch : Intermediate {};",
+ cxxRecordDecl(hasName("ExpectedMatch"),
+ hasAnyBase(hasType(cxxRecordDecl(hasName("Base")))))));
+}
+
+TEST(HasAnyBase, NoBase) {
+ EXPECT_TRUE(notMatches("struct Foo {};"
+ "struct Bar {};",
+ cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl())))));
+}
+
+TEST(IsPublicBase, Public) {
+ EXPECT_TRUE(matches("class Base {};"
+ "class Derived : public Base {};",
+ cxxRecordDecl(hasAnyBase(isPublic()))));
+}
+
+TEST(IsPublicBase, DefaultAccessSpecifierPublic) {
+ EXPECT_TRUE(matches("class Base {};"
+ "struct Derived : Base {};",
+ cxxRecordDecl(hasAnyBase(isPublic()))));
+}
+
+TEST(IsPublicBase, Private) {
+ EXPECT_TRUE(notMatches("class Base {};"
+ "class Derived : private Base {};",
+ cxxRecordDecl(hasAnyBase(isPublic()))));
+}
+
+TEST(IsPublicBase, DefaultAccessSpecifierPrivate) {
+ EXPECT_TRUE(notMatches("class Base {};"
+ "class Derived : Base {};",
+ cxxRecordDecl(hasAnyBase(isPublic()))));
+}
+
+TEST(IsPublicBase, Protected) {
+ EXPECT_TRUE(notMatches("class Base {};"
+ "class Derived : protected Base {};",
+ cxxRecordDecl(hasAnyBase(isPublic()))));
+}
+
+TEST(IsPrivateBase, Private) {
+ EXPECT_TRUE(matches("class Base {};"
+ "class Derived : private Base {};",
+ cxxRecordDecl(hasAnyBase(isPrivate()))));
+}
+
+TEST(IsPrivateBase, DefaultAccessSpecifierPrivate) {
+ EXPECT_TRUE(matches("struct Base {};"
+ "class Derived : Base {};",
+ cxxRecordDecl(hasAnyBase(isPrivate()))));
+}
+
+TEST(IsPrivateBase, Public) {
+ EXPECT_TRUE(notMatches("class Base {};"
+ "class Derived : public Base {};",
+ cxxRecordDecl(hasAnyBase(isPrivate()))));
+}
+
+TEST(IsPrivateBase, DefaultAccessSpecifierPublic) {
+ EXPECT_TRUE(notMatches("class Base {};"
+ "struct Derived : Base {};",
+ cxxRecordDecl(hasAnyBase(isPrivate()))));
+}
+
+TEST(IsPrivateBase, Protected) {
+ EXPECT_TRUE(notMatches("class Base {};"
+ "class Derived : protected Base {};",
+ cxxRecordDecl(hasAnyBase(isPrivate()))));
+}
+
+TEST(IsProtectedBase, Protected) {
+ EXPECT_TRUE(matches("class Base {};"
+ "class Derived : protected Base {};",
+ cxxRecordDecl(hasAnyBase(isProtected()))));
+}
+
+TEST(IsProtectedBase, Public) {
+ EXPECT_TRUE(notMatches("class Base {};"
+ "class Derived : public Base {};",
+ cxxRecordDecl(hasAnyBase(isProtected()))));
+}
+
+TEST(IsProtectedBase, Private) {
+ EXPECT_TRUE(notMatches("class Base {};"
+ "class Derived : private Base {};",
+ cxxRecordDecl(hasAnyBase(isProtected()))));
+}
+
+TEST(IsVirtual, Directly) {
+ EXPECT_TRUE(matches("class Base {};"
+ "class Derived : virtual Base {};",
+ cxxRecordDecl(hasAnyBase(isVirtual()))));
+}
+
+TEST(IsVirtual, Indirectly) {
+ EXPECT_TRUE(
+ matches("class Base {};"
+ "class Intermediate : virtual Base {};"
+ "class Derived : Intermediate {};",
+ cxxRecordDecl(hasName("Derived"), hasAnyBase(isVirtual()))));
+}
+
+TEST(IsVirtual, NoVirtualBase) {
+ EXPECT_TRUE(notMatches("class Base {};"
+ "class Derived : Base {};",
+ cxxRecordDecl(hasAnyBase(isVirtual()))));
+}
+
} // namespace ast_matchers
} // namespace clang
More information about the cfe-commits
mailing list