diff --git a/clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp b/clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp --- a/clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp @@ -59,8 +59,9 @@ // 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 --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -2175,6 +2175,75 @@ +Matcher<CXXBaseSpecifier>isPrivate +
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'
+
+ + +Matcher<CXXBaseSpecifier>isProtected +
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'
+
+ + +Matcher<CXXBaseSpecifier>isPublic +
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'
+
+ + +Matcher<CXXBaseSpecifier>isVirtual +
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<CXXMethodDecl>, Matcher<CXXBaseSpecifier>
+
+ + Matcher<CXXBoolLiteralExpr>equalsbool Value

 
@@ -2562,14 +2631,21 @@
 
 
 Matcher<CXXMethodDecl>isVirtual
-
Matches if the given method declaration is virtual.
+
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<CXXMethodDecl>, Matcher<CXXBaseSpecifier>
 
@@ -3012,44 +3088,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.
+
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'
 
@@ -5135,6 +5219,33 @@
+Matcher<CXXBaseSpecifier>hasTypeMatcher<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; };
+
+Example matches class Derived
+(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))
+class Base {};
+class Derived : Base {};
+
+Usable as: Matcher<Expr>, Matcher<FriendDecl>, Matcher<ValueDecl>,
+Matcher<CXXBaseSpecifier>
+
+ + Matcher<CXXConstructExpr>forEachArgumentWithParamMatcher<Expr> ArgMatcher, Matcher<ParmVarDecl> ParamMatcher
Matches all arguments and their respective ParmVarDecl.
 
@@ -5518,6 +5629,21 @@
 
+Matcher<CXXRecordDecl>hasAnyBaseMatcher<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>hasMethodMatcher<CXXMethodDecl> InnerMatcher
Matches the first method of a class or struct that satisfies InnerMatcher.
 
@@ -6075,7 +6201,13 @@
  void y(X &x) { x; X z; }
  class Y { friend class X; };
 
-Usable as: Matcher<Expr>, Matcher<ValueDecl>
+Example matches class Derived
+(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))
+class Base {};
+class Derived : Base {};
+
+Usable as: Matcher<Expr>, Matcher<FriendDecl>, Matcher<ValueDecl>,
+Matcher<CXXBaseSpecifier>
 
@@ -6289,7 +6421,13 @@ void y(X &x) { x; X z; } class Y { friend class X; }; -Usable as: Matcher<Expr>, Matcher<ValueDecl> +Example matches class Derived +(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base")))))) +class Base {}; +class Derived : Base {}; + +Usable as: Matcher<Expr>, Matcher<FriendDecl>, Matcher<ValueDecl>, +Matcher<CXXBaseSpecifier>
@@ -7677,7 +7815,13 @@ void y(X &x) { x; X z; } class Y { friend class X; }; -Usable as: Matcher<Expr>, Matcher<ValueDecl> +Example matches class Derived +(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base")))))) +class Base {}; +class Derived : Base {}; + +Usable as: Matcher<Expr>, Matcher<FriendDecl>, Matcher<ValueDecl>, +Matcher<CXXBaseSpecifier>
diff --git a/clang/include/clang/AST/ASTTypeTraits.h b/clang/include/clang/AST/ASTTypeTraits.h --- 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 @@ 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 diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h --- 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 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 @@ return Matcher(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, + 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 @@ /// class Y { friend class X; }; /// \endcode /// -/// Usable as: Matcher, Matcher +/// Example matches class Derived +/// (matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base")))))) +/// \code +/// class Base {}; +/// class Derived : Base {}; +/// \endcode +/// +/// Usable as: Matcher, Matcher, 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()) @@ -5177,17 +5228,28 @@ 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, Matcher +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 --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/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: @@ -1929,6 +1941,13 @@ HasOverloadOpNameMatcher hasAnyOverloadedOperatorNameFunc(ArrayRef 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 &BaseSpecMatcher, + ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder); + } // namespace internal } // namespace ast_matchers diff --git a/clang/lib/AST/ASTTypeTraits.cpp b/clang/lib/AST/ASTTypeTraits.cpp --- a/clang/lib/AST/ASTTypeTraits.cpp +++ b/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" }, diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp --- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -68,6 +68,30 @@ BoundNodesTreeBuilder *Builder, ArrayRef InnerMatchers); +bool matchesAnyBase(const CXXRecordDecl &Node, + const Matcher &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 --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -237,6 +237,7 @@ 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 --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -3006,5 +3006,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