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 @@ -138,6 +138,7 @@ NKI_QualType, NKI_TypeLoc, NKI_LastKindWithoutPointerIdentity = NKI_TypeLoc, + NKI_CXXBaseSpecifier, NKI_CXXCtorInitializer, NKI_NestedNameSpecifier, NKI_Decl, @@ -189,6 +190,7 @@ template <> struct ASTNodeKind::KindToKindId { \ static const NodeKindId Id = NKI_##Class; \ }; +KIND_TO_KIND_ID(CXXBaseSpecifier) KIND_TO_KIND_ID(CXXCtorInitializer) KIND_TO_KIND_ID(TemplateArgument) KIND_TO_KIND_ID(TemplateName) @@ -487,6 +489,10 @@ struct DynTypedNode::BaseConverter< NestedNameSpecifier, void> : public PtrConverter {}; +template <> +struct DynTypedNode::BaseConverter< + CXXBaseSpecifier, void> : public PtrConverter {}; + template <> struct DynTypedNode::BaseConverter< CXXCtorInitializer, void> : public PtrConverter {}; 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 @@ -140,6 +140,7 @@ using TypeLocMatcher = internal::Matcher; using NestedNameSpecifierMatcher = internal::Matcher; using NestedNameSpecifierLocMatcher = internal::Matcher; +using CXXBaseSpecifierMatcher = internal::Matcher; using CXXCtorInitializerMatcher = internal::Matcher; /// @} @@ -468,6 +469,16 @@ extern const internal::VariadicDynCastAllOfMatcher accessSpecDecl; +/// Matches class bases. +/// +/// Examples matches \c public virtual B. +/// \code +/// class B {}; +/// class C : public virtual B {}; +/// \endcode +extern const internal::VariadicAllOfMatcher + cxxBaseSpecifier; + /// Matches constructor initializers. /// /// Examples matches \c i(42). @@ -2602,6 +2613,23 @@ AST_POLYMORPHIC_SUPPORTED_TYPES(CXXOperatorCallExpr, FunctionDecl)>(Name); } +/// Matches C++ classes that have a direct base class matching \c Base. +/// +/// Example matches X (Base == cxxBaseSpecifier()) +/// \code +/// class X; +/// class Y : X {}; +/// \endcode +AST_MATCHER_P_OVERLOAD(CXXRecordDecl, hasBase, + internal::Matcher, Base, 1) { + if (!Node.hasDefinition()) + return false; + for (const CXXBaseSpecifier &BaseSpec : Node.bases()) + if (Base.matches(BaseSpec, Finder, Builder)) + return true; + return false; +} + /// Matches C++ classes that are directly or indirectly derived from a class /// matching \c Base, or Objective-C classes that directly or indirectly /// subclass a class matching \c Base. @@ -3258,16 +3286,19 @@ /// and z (matcher = varDecl(hasType(cxxRecordDecl(hasName("X"))))) /// and U (matcher = typedefDecl(hasType(asString("int"))) /// and friend class X (matcher = friendDecl(hasType("X")) +/// and public virtual X (matcher = cxxBaseSpecifier(hasType( +/// asString("class X"))) /// \code /// class X {}; /// void y(X &x) { x; X z; } /// typedef int U; /// class Y { friend class X; }; +/// class Z : public virtual X {}; /// \endcode AST_POLYMORPHIC_MATCHER_P_OVERLOAD( hasType, AST_POLYMORPHIC_SUPPORTED_TYPES(Expr, FriendDecl, TypedefNameDecl, - ValueDecl), + ValueDecl, CXXBaseSpecifier), internal::Matcher, InnerMatcher, 0) { QualType QT = internal::getUnderlyingType(Node); if (!QT.isNull()) @@ -3287,15 +3318,20 @@ /// 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")) +/// and public virtual X (matcher = cxxBaseSpecifier(hasType( +/// cxxRecordDecl(hasName("X")))) /// \code /// class X {}; /// void y(X &x) { x; X z; } /// class Y { friend class X; }; +/// class Z : public virtual X {}; /// \endcode /// /// 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()) 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.. @@ -926,6 +929,7 @@ std::is_same::value || std::is_same::value || std::is_same::value || + std::is_same::value || std::is_same::value; }; template @@ -1094,7 +1098,7 @@ /// Useful for matchers like \c anything and \c unless. using AllNodeBaseTypes = TypeList; + Type, TypeLoc, CXXBaseSpecifier, CXXCtorInitializer>; /// Helper meta-function to extract the argument out of a function of /// type void(Arg). 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" }, @@ -163,6 +164,8 @@ } SourceRange DynTypedNode::getSourceRange() const { + if (const CXXBaseSpecifier *CBS = get()) + return CBS->getSourceRange(); if (const CXXCtorInitializer *CCI = get()) return CCI->getSourceRange(); if (const NestedNameSpecifierLoc *NNSL = get()) diff --git a/clang/unittests/AST/ASTTypeTraitsTest.cpp b/clang/unittests/AST/ASTTypeTraitsTest.cpp --- a/clang/unittests/AST/ASTTypeTraitsTest.cpp +++ b/clang/unittests/AST/ASTTypeTraitsTest.cpp @@ -112,6 +112,7 @@ VERIFY_NAME(NestedNameSpecifierLoc); VERIFY_NAME(QualType); VERIFY_NAME(TypeLoc); + VERIFY_NAME(CXXBaseSpecifier); VERIFY_NAME(CXXCtorInitializer); VERIFY_NAME(NestedNameSpecifier); VERIFY_NAME(Decl); @@ -141,6 +142,15 @@ EXPECT_TRUE(Verifier.match("void f() {}", typeLoc(loc(functionType())))); } +#if 0 // Cannot be used as a top level matcher at the time +TEST(DynTypedNode, CXXBaseSpecifierSourceRange) { + RangeVerifier Verifier; + Verifier.expectRange(1, 23, 1, 39); + EXPECT_TRUE(Verifier.match("class B {}; class C : public virtual B {};", + cxxBaseSpecifier())); +} +#endif + TEST(DynTypedNode, NNSLocSourceRange) { RangeVerifier Verifier; Verifier.expectRange(1, 33, 1, 34); 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 @@ -678,6 +678,17 @@ "@interface Z : A @end", ZIsDirectlyDerivedFromX)); } +TEST(DeclarationMatcher, ClassHasBase) { + DeclarationMatcher HasAnyBase = cxxRecordDecl(hasBase(cxxBaseSpecifier())); + EXPECT_TRUE(matches("class X {}; class Y : X {};", HasAnyBase)); + EXPECT_TRUE(notMatches("class X {};", HasAnyBase)); + + DeclarationMatcher HasBaseX = + cxxRecordDecl(hasBase(cxxBaseSpecifier(hasType(asString("class X"))))); + EXPECT_TRUE(matches("class X {}; class Y : X {};", HasBaseX)); + EXPECT_TRUE(notMatches("class Z {}; class Y : Z {};", HasBaseX)); +} + TEST(DeclarationMatcher, IsLambda) { const auto IsLambda = cxxMethodDecl(ofClass(cxxRecordDecl(isLambda()))); EXPECT_TRUE(matches("auto x = []{};", IsLambda));