Index: clang/docs/LibASTMatchersReference.html =================================================================== --- clang/docs/LibASTMatchersReference.html +++ clang/docs/LibASTMatchersReference.html @@ -100,6 +100,16 @@
Matches C++ base specifier. + +Given + struct Base {}; + struct Derived : protected Base {}; + matches 'protected Base'. +
Matches constructor initializers. @@ -2175,6 +2185,80 @@
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' +
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' +
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' +
Matches declarations of virtual methods and C++ base specifers that specify +virtual inheritance. + +Given + class A { + public: + virtual void x(); + }; + matches A::x +
Matches C++ base specifers that specify virtual inheritance. + +Example: + class Base {}; + class DirectlyDerived : virtual Base {}; // matches Base + class IndirectlyDerived : DirectlyDerived, Base {}; // matches Base + +
Matches if the given method declaration is virtual. + Matches declarations of virtual methods and C++ base specifers that specify +virtual inheritance. Given class A { @@ -3012,44 +3097,52 @@- Matcher<Decl> isPrivate Matches private C++ declarations. + 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'- Matcher<Decl> isProtected Matches protected C++ declarations. + 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'- Matcher<Decl> isPublic + Matches public C++ declarations. +@@ -5106,6 +5199,27 @@ 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'+ Matcher<CXXBaseSpecifier> hasType Matcher<Decl> InnerMatcher + + 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; }; + +Usable as: Matcher<Expr>, Matcher<ValueDecl> +Matcher<CXXConstructExpr> forEachArgumentWithParam Matcher<Expr> ArgMatcher, Matcher<ParmVarDecl> ParamMatcher + Matches all arguments and their respective ParmVarDecl. @@ -5489,6 +5603,21 @@+ Matcher<CXXRecordDecl> hasAnyBase Matcher<CXXBaseSpecifier> BaseSpecMatcher + + 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. +Matcher<CXXRecordDecl> hasMethod Matcher<CXXMethodDecl> InnerMatcher Matches the first method of a class or struct that satisfies InnerMatcher. 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 + : 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,52 +549,82 @@ extern const internal::VariadicDynCastAllOfMatcher templateTypeParmDecl; -/// Matches public C++ declarations. +/// 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 and C++ base specifers that specify public +/// inheritance. +/// +/// 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. @@ -2835,6 +2866,36 @@ 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 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 +3528,9 @@ /// /// 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()) @@ -5128,7 +5191,8 @@ return Matched; } -/// Matches if the given method declaration is virtual. +/// Matches declarations of virtual methods and C++ base specifers that specify +/// virtual inheritance. /// /// Given /// \code @@ -5138,7 +5202,9 @@ /// }; /// \endcode /// matches A::x -AST_MATCHER(CXXMethodDecl, isVirtual) { +AST_POLYMORPHIC_MATCHER(isVirtual, + AST_POLYMORPHIC_SUPPORTED_TYPES(CXXMethodDecl, + CXXBaseSpecifier)) { return Node.isVirtual(); } 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.. @@ -142,6 +145,15 @@ return Node.getType()->getAs (); } +/// 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: @@ -982,6 +994,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,31 @@ return false; } +bool MatchASTVisitor::hasABaseSpec( + const CXXRecordDecl *ClassDecl, + const Matcher &BaseSpecMatcher, + BoundNodesTreeBuilder *Builder) { + if (!ClassDecl->hasDefinition()) + return false; + + CXXBasePaths Paths; + Paths.setOrigin(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,7 @@ nonTypeTemplateParmDecl; const internal::VariadicDynCastAllOfMatcher templateTypeParmDecl; +const internal::VariadicAllOfMatcher cxxBaseSpecifier; const internal::VariadicAllOfMatcher qualType; const internal::VariadicAllOfMatcher type; const internal::VariadicAllOfMatcher typeLoc; Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp =================================================================== --- clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -239,6 +239,7 @@ REGISTER_MATCHER(has); REGISTER_MATCHER(hasAncestor); REGISTER_MATCHER(hasAnyArgument); + REGISTER_MATCHER(hasAnyBase); REGISTER_MATCHER(hasAnyClause); REGISTER_MATCHER(hasAnyConstructorInitializer); REGISTER_MATCHER(hasAnyDeclaration); Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -2942,5 +2942,126 @@ 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