Index: clang/include/clang/AST/ASTTypeTraits.h =================================================================== --- clang/include/clang/AST/ASTTypeTraits.h +++ 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 @@ NKI_QualType, NKI_TypeLoc, NKI_LastKindWithoutPointerIdentity = NKI_TypeLoc, + NKI_CXXBaseSpecifier, NKI_CXXCtorInitializer, NKI_NestedNameSpecifier, NKI_Decl, @@ -198,6 +200,7 @@ 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 @@ struct DynTypedNode::BaseConverter< TypeLoc, void> : public ValueConverter {}; +template <> +struct DynTypedNode::BaseConverter< + CXXBaseSpecifier, void> : public PtrConverter {}; + // 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 Index: clang/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchers.h +++ 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,6 +549,17 @@ extern const internal::VariadicDynCastAllOfMatcher templateTypeParmDecl; +/// Matches C++ base specifier. +/// +/// Given +/// \code +/// struct Base {}; +/// struct Derived : protected Base {}; +/// \endcode +/// matches 'protected Base'. +extern const internal::VariadicAllOfMatcher + cxxBaseSpecifier; + /// Matches public C++ declarations. /// /// Given @@ -2835,6 +2847,89 @@ return Matcher(M).matches(*InterfaceDecl, Finder, Builder); } +/// Matches C++ classes that have a direct or indirect base matching \p BaseSpecMatcher. +/// +/// Example matches DirectlyDerived, IndirectlyDerived (BaseSpecMatcher == hasType(cxxRecordDecl(hasName("SpecialBase")))) +/// \code +/// class Foo; +/// class Bar : Foo {}; +/// class Baz : Bar {}; +/// class SpecialBase; +/// class DirectlyDerived : SpecialBase {}; // directly derived +/// class IndirectlyDerived : DirectlyDerived {}; // indirectly derived +/// \endcode +/// +// FIXME: Refactor this and isDerivedFrom to reuse implementation. +AST_MATCHER_P( + CXXRecordDecl, + hasAnyBase, + internal::Matcher, BaseSpecMatcher) { + return Finder->hasABaseSpec(&Node, BaseSpecMatcher, Builder); +} + +/// Matches C++ base specifers that specify public inheritance. +/// +/// Examples: +/// \code +/// class Base {}; +/// class Derived1 : public Base {}; // matches Base +/// struct Derived2 : Base {}; // matches Base +/// \endcode +/// +AST_MATCHER( + CXXBaseSpecifier, + isPublicBase +) { + return Node.getAccessSpecifier() == AS_public; +} + +/// Matches C++ base specifers that specify public inheritance. +/// +/// Trivial example is: +/// \code +/// struct Base {}; +/// struct Derived1 : private Base {}; // matches Base +/// class Derived2 : Base {}; // matches Base +/// \endcode +/// +AST_MATCHER( + CXXBaseSpecifier, + isPrivateBase +) { + return Node.getAccessSpecifier() == AS_private; +} + +/// Matches C++ base specifers that specify protected inheritance. +/// +/// Example: +/// \code +/// class Base {}; +/// class Derived : protected Base {}; // matches Base +/// \endcode +/// +AST_MATCHER( + CXXBaseSpecifier, + isProtectedBase +) { + return Node.getAccessSpecifier() == AS_protected; +} + +/// Matches C++ base specifers that specify virtual inheritance. +/// +/// Example: +/// \code +/// class Base {}; +/// class DirectlyDerived : virtual Base {}; // matches Base +/// class IndirectlyDerived : DirectlyDerived, Base {}; // matches Base +/// \endcode +/// +AST_MATCHER( + CXXBaseSpecifier, + isVirtualBase +) { + return Node.isVirtual(); +} + /// Similar to \c isDerivedFrom(), but also matches classes that directly /// match \c Base. AST_POLYMORPHIC_MATCHER_P_OVERLOAD( @@ -3467,7 +3562,7 @@ /// /// Usable as: Matcher, Matcher AST_POLYMORPHIC_MATCHER_P_OVERLOAD( - hasType, AST_POLYMORPHIC_SUPPORTED_TYPES(Expr, FriendDecl, ValueDecl), + hasType, AST_POLYMORPHIC_SUPPORTED_TYPES(Expr, FriendDecl, ValueDecl, CXXBaseSpecifier), internal::Matcher, InnerMatcher, 1) { QualType QT = internal::getUnderlyingType(Node); if (!QT.isNull()) Index: clang/include/clang/ASTMatchers/ASTMatchersInternal.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -130,6 +130,9 @@ 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.. @@ -982,6 +985,13 @@ BoundNodesTreeBuilder *Builder, bool Directly) = 0; + /// Returns true if \p Declaration has a base specifier matching \p BaseSpec. + /// + /// A class is not considered to be derived from itself. + virtual bool hasABaseSpec(const CXXRecordDecl *ClassDecl, + const Matcher &BaseSpecMatcher, + BoundNodesTreeBuilder *Builder) = 0; + /// Returns true if the given Objective-C class is directly or indirectly /// derived from a base class matching \c base. /// Index: clang/lib/AST/ASTTypeTraits.cpp =================================================================== --- clang/lib/AST/ASTTypeTraits.cpp +++ clang/lib/AST/ASTTypeTraits.cpp @@ -27,6 +27,7 @@ { NKI_None, "NestedNameSpecifierLoc" }, { NKI_None, "QualType" }, { NKI_None, "TypeLoc" }, + { NKI_None, "CXXBaseSpecifier" }, { NKI_None, "CXXCtorInitializer" }, { NKI_None, "NestedNameSpecifier" }, { NKI_None, "Decl" }, Index: clang/lib/ASTMatchers/ASTMatchFinder.cpp =================================================================== --- clang/lib/ASTMatchers/ASTMatchFinder.cpp +++ clang/lib/ASTMatchers/ASTMatchFinder.cpp @@ -18,6 +18,7 @@ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/CXXInheritance.h" #include "clang/AST/RecursiveASTVisitor.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" @@ -495,6 +496,10 @@ BoundNodesTreeBuilder *Builder, bool Directly) override; + bool hasABaseSpec(const CXXRecordDecl *ClassDecl, + const Matcher &BaseSpecMatcher, + BoundNodesTreeBuilder *Builder) override; + // Implements ASTMatchFinder::matchesChildOf. bool matchesChildOf(const DynTypedNode &Node, ASTContext &Ctx, const DynTypedMatcher &Matcher, @@ -931,6 +936,29 @@ return false; } +bool MatchASTVisitor::hasABaseSpec(const CXXRecordDecl *ClassDecl, + const Matcher &BaseSpecMatcher, + BoundNodesTreeBuilder *Builder) { + if (!ClassDecl->hasDefinition()) + return false; + + CXXBasePaths Paths; + // FIXME: Every time someone casts away const specifier a kitten dies. + Paths.setOrigin(const_cast(ClassDecl)); + + const auto basePredicate = [this, Builder, &BaseSpecMatcher](const CXXBaseSpecifier *BaseSpec, CXXBasePath &IgnoredParam) { + BoundNodesTreeBuilder Result(*Builder); + if (BaseSpecMatcher.matches(*BaseSpec, this, &Result)) { + *Builder = std::move(Result); + return true; + } + return false; + }; + + return ClassDecl->lookupInBases(basePredicate, Paths, + /*LookupInDependent =*/true); +} + // Returns true if the given Objective-C class is directly or indirectly // derived from a matching base class. A class is not considered to be derived // from itself. Index: clang/lib/ASTMatchers/ASTMatchersInternal.cpp =================================================================== --- clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -667,6 +667,8 @@ nonTypeTemplateParmDecl; const internal::VariadicDynCastAllOfMatcher templateTypeParmDecl; +const internal::VariadicAllOfMatcher + cxxBaseSpecifier; const internal::VariadicAllOfMatcher qualType; const internal::VariadicAllOfMatcher type; const internal::VariadicAllOfMatcher typeLoc; Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -2942,5 +2942,289 @@ 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( + isPublicBase() + ) + ) + ) + ); +} + +TEST(IsPublicBase, DefaultAccessSpecifierPublic) { + EXPECT_TRUE( + matches( + "class Base {};" + "struct Derived : Base {};", + cxxRecordDecl( + hasAnyBase( + isPublicBase() + ) + ) + ) + ); +} + +TEST(IsPublicBase, Private) { + EXPECT_TRUE( + notMatches( + "class Base {};" + "class Derived : private Base {};", + cxxRecordDecl( + hasAnyBase( + isPublicBase() + ) + ) + ) + ); +} + +TEST(IsPublicBase, DefaultAccessSpecifierPrivate) { + EXPECT_TRUE( + notMatches( + "class Base {};" + "class Derived : Base {};", + cxxRecordDecl( + hasAnyBase( + isPublicBase() + ) + ) + ) + ); +} + +TEST(IsPublicBase, Protected) { + EXPECT_TRUE( + notMatches( + "class Base {};" + "class Derived : protected Base {};", + cxxRecordDecl( + hasAnyBase( + isPublicBase() + ) + ) + ) + ); +} + +TEST(IsPrivateBase, Private) { + EXPECT_TRUE( + matches( + "class Base {};" + "class Derived : private Base {};", + cxxRecordDecl( + hasAnyBase( + isPrivateBase() + ) + ) + ) + ); +} + +TEST(IsPrivateBase, DefaultAccessSpecifierPrivate) { + EXPECT_TRUE( + matches( + "struct Base {};" + "class Derived : Base {};", + cxxRecordDecl( + hasAnyBase( + isPrivateBase() + ) + ) + ) + ); +} + +TEST(IsPrivateBase, Public) { + EXPECT_TRUE( + notMatches( + "class Base {};" + "class Derived : public Base {};", + cxxRecordDecl( + hasAnyBase( + isPrivateBase() + ) + ) + ) + ); +} + +TEST(IsPrivateBase, DefaultAccessSpecifierPublic) { + EXPECT_TRUE( + notMatches( + "class Base {};" + "struct Derived : Base {};", + cxxRecordDecl( + hasAnyBase( + isPrivateBase() + ) + ) + ) + ); +} + + +TEST(IsPrivateBase, Protected) { + EXPECT_TRUE( + notMatches( + "class Base {};" + "class Derived : protected Base {};", + cxxRecordDecl( + hasAnyBase( + isPrivateBase() + ) + ) + ) + ); +} + + + +TEST(IsProtectedBase, Protected) { + EXPECT_TRUE( + matches( + "class Base {};" + "class Derived : protected Base {};", + cxxRecordDecl( + hasAnyBase( + isProtectedBase() + ) + ) + ) + ); +} + +TEST(IsProtectedBase, Public) { + EXPECT_TRUE( + notMatches( + "class Base {};" + "class Derived : public Base {};", + cxxRecordDecl( + hasAnyBase( + isProtectedBase() + ) + ) + ) + ); +} + +TEST(IsProtectedBase, Private) { + EXPECT_TRUE( + notMatches( + "class Base {};" + "class Derived : private Base {};", + cxxRecordDecl( + hasAnyBase( + isProtectedBase() + ) + ) + ) + ); +} + +TEST(IsVirtualBase, Directly) { + EXPECT_TRUE( + matches( + "class Base {};" + "class Derived : virtual Base {};", + cxxRecordDecl( + hasAnyBase( + isVirtualBase() + ) + ) + ) + ); +} + +TEST(IsVirtualBase, Indirectly) { + EXPECT_TRUE( + matches( + "class Base {};" + "class Intermediate : virtual Base {};" + "class Derived : Intermediate {};", + cxxRecordDecl( + hasName("Derived"), + hasAnyBase( + isVirtualBase() + ) + ) + ) + ); +} + +TEST(IsVirtualBase, NoVirtualBase) { + EXPECT_TRUE( + notMatches( + "class Base {};" + "class Derived : Base {};", + cxxRecordDecl( + hasAnyBase( + isVirtualBase() + ) + ) + ) + ); +} + } // namespace ast_matchers } // namespace clang