njames93 created this revision. njames93 added reviewers: klimek, aaron.ballman, jkorous. Herald added subscribers: cfe-commits, dexonsmith. Herald added a project: clang.
Adds a matcher called `hasDirectBase` for matching the `CXXBaseSpecifier` of a class that directly derives from another class. Adds a matcher called `hasClass` that matches on the class that a `CXXBaseSpecifier` refers to. Also removed the `CXXBaseSpecifier` overload for the `hasType` Matcher in favour of this `hasClass` matcher. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D81552 Files: clang/docs/LibASTMatchersReference.html clang/include/clang/ASTMatchers/ASTMatchers.h clang/include/clang/ASTMatchers/ASTMatchersInternal.h clang/lib/ASTMatchers/Dynamic/Registry.cpp clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -2997,26 +2997,24 @@ } TEST(HasAnyBase, DirectBase) { - EXPECT_TRUE(matches( - "struct Base {};" - "struct ExpectedMatch : Base {};", - cxxRecordDecl(hasName("ExpectedMatch"), - hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))); + EXPECT_TRUE(matches("struct Base {};" + "struct ExpectedMatch : Base {};", + cxxRecordDecl(hasName("ExpectedMatch"), + hasAnyBase(hasClass(hasName("Base")))))); } TEST(HasAnyBase, IndirectBase) { - EXPECT_TRUE(matches( - "struct Base {};" - "struct Intermediate : Base {};" - "struct ExpectedMatch : Intermediate {};", - cxxRecordDecl(hasName("ExpectedMatch"), - hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))); + EXPECT_TRUE(matches("struct Base {};" + "struct Intermediate : Base {};" + "struct ExpectedMatch : Intermediate {};", + cxxRecordDecl(hasName("ExpectedMatch"), + hasAnyBase(hasClass(hasName("Base")))))); } TEST(HasAnyBase, NoBase) { EXPECT_TRUE(notMatches("struct Foo {};" "struct Bar {};", - cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl()))))); + cxxRecordDecl(hasAnyBase(hasClass(cxxRecordDecl()))))); } TEST(IsPublicBase, Public) { @@ -3117,5 +3115,57 @@ cxxRecordDecl(hasAnyBase(isVirtual())))); } +TEST(BaseSpecifier, hasDirectBase) { + EXPECT_TRUE(matches( + R"cc( + class Base {}; + class Derived : Base{}; + )cc", + cxxRecordDecl(hasName("Derived"), + hasDirectBase(hasClass(hasName("Base")))))); + + StringRef MultiDerived = R"cc( + class Base {}; + class Base2 {}; + class Derived : Base, Base2{}; + )cc"; + + EXPECT_TRUE(matches(MultiDerived, + cxxRecordDecl(hasName("Derived"), + hasDirectBase(hasClass(hasName("Base")))))); + EXPECT_TRUE(matches( + MultiDerived, cxxRecordDecl(hasName("Derived"), + hasDirectBase(hasClass(hasName("Base2")))))); + + StringRef Indirect = R"cc( + class Base {}; + class Intermediate : Base {}; + class Derived : Intermediate{}; + )cc"; + + EXPECT_TRUE( + matches(Indirect, + cxxRecordDecl(hasName("Derived"), + hasDirectBase(hasClass(hasName("Intermediate")))))); + EXPECT_TRUE(notMatches( + Indirect, cxxRecordDecl(hasName("Derived"), + hasDirectBase(hasClass(hasName("Base")))))); + + // Only really here to make sure the asserts don't fire + EXPECT_FALSE( + matches(R"cc( + class Base {}; + class Derived : BAse {}; + )cc", + cxxRecordDecl(hasDirectBase(hasClass(hasName("Base")))))); + + EXPECT_FALSE( + matches(R"cc( + using Base = int; + class Derived : Base {}; + )cc", + cxxRecordDecl(hasDirectBase(hasClass(hasName("Base")))))); +} + } // namespace ast_matchers } // namespace clang Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp =================================================================== --- clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -261,6 +261,7 @@ REGISTER_MATCHER(hasCanonicalType); REGISTER_MATCHER(hasCaseConstant); REGISTER_MATCHER(hasCastKind); + REGISTER_MATCHER(hasClass); REGISTER_MATCHER(hasCondition); REGISTER_MATCHER(hasConditionVariableStatement); REGISTER_MATCHER(hasDecayedType); @@ -271,6 +272,7 @@ REGISTER_MATCHER(hasDefinition); REGISTER_MATCHER(hasDescendant); REGISTER_MATCHER(hasDestinationType); + REGISTER_MATCHER(hasDirectBase); REGISTER_MATCHER(hasDynamicExceptionSpec); REGISTER_MATCHER(hasEitherOperand); REGISTER_MATCHER(hasElementType); Index: clang/include/clang/ASTMatchers/ASTMatchersInternal.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -130,9 +130,6 @@ 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.. Index: clang/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchers.h +++ clang/include/clang/ASTMatchers/ASTMatchers.h @@ -2864,7 +2864,7 @@ /// BaseSpecMatcher. /// /// Example: -/// matcher hasAnyBase(hasType(cxxRecordDecl(hasName("SpecialBase"))))) +/// matcher hasAnyBase(hasClass(hasName("SpecialBase"))) /// \code /// class Foo; /// class Bar : Foo {}; @@ -2880,6 +2880,27 @@ return internal::matchesAnyBase(Node, BaseSpecMatcher, Finder, Builder); } +/// Matches C++ classes that have a direct base matching \p BaseSpecMatcher. +/// +/// Example: +/// matcher hasDirectBase(hasClass(hasName("SpecialBase"))) +/// \code +/// class Foo; +/// class Bar : Foo {}; +/// class Baz : Bar {}; +/// class SpecialBase; +/// class Proxy : SpecialBase {}; // matches Proxy +/// class IndirectlyDerived : Proxy {}; // doesn't match +/// \endcode +AST_MATCHER_P(CXXRecordDecl, hasDirectBase, internal::Matcher<CXXBaseSpecifier>, + BaseSpecMatcher) { + + return Node.hasDefinition() && + llvm::any_of(Node.bases(), [&](const CXXBaseSpecifier &Base) { + return BaseSpecMatcher.matches(Base, Finder, Builder); + }); +} + /// Similar to \c isDerivedFrom(), but also matches classes that directly /// match \c Base. AST_POLYMORPHIC_MATCHER_P_OVERLOAD( @@ -3510,19 +3531,10 @@ /// class Y { friend class X; }; /// \endcode /// -/// 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> +/// Usable as: Matcher<Expr>, Matcher<FriendDecl>, Matcher<ValueDecl> AST_POLYMORPHIC_MATCHER_P_OVERLOAD( - hasType, - AST_POLYMORPHIC_SUPPORTED_TYPES(Expr, FriendDecl, ValueDecl, - CXXBaseSpecifier), + hasType, AST_POLYMORPHIC_SUPPORTED_TYPES(Expr, FriendDecl, ValueDecl), internal::Matcher<Decl>, InnerMatcher, 1) { QualType QT = internal::getUnderlyingType(Node); if (!QT.isNull()) @@ -3530,6 +3542,23 @@ return false; } +/// Matches if the Base specifier refers to the given class matcher. +/// +/// Example matches class Derived +/// (matcher = cxxRecordDecl(hasAnyBase(hasClass(hasName("Base"))))) +/// \code +/// class Base {}; +/// class Derived : Base {}; +/// \endcode +AST_MATCHER_P(CXXBaseSpecifier, hasClass, internal::Matcher<CXXRecordDecl>, + InnerMatcher) { + QualType QT = Node.getType(); + assert(!QT.isNull() && "CXXBaseSpecifier shouldn't have a null type"); + const CXXRecordDecl *RD = QT->getAsCXXRecordDecl(); + assert(RD && "CXXBaseSpecifier should refer to a CXXRecordDecl"); + return InnerMatcher.matches(*RD, Finder, Builder); +} + /// Matches if the type location of the declarator decl's type matches /// the inner matcher. /// Index: clang/docs/LibASTMatchersReference.html =================================================================== --- clang/docs/LibASTMatchersReference.html +++ clang/docs/LibASTMatchersReference.html @@ -1226,6 +1226,11 @@ </pre></td></tr> +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('fixedPointLiteral0')"><a name="fixedPointLiteral0Anchor">fixedPointLiteral</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1FixedPointLiteral.html">FixedPointLiteral</a>>...</td></tr> +<tr><td colspan="4" class="doc" id="fixedPointLiteral0"><pre>Matches fixed point literals +</pre></td></tr> + + <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('floatLiteral0')"><a name="floatLiteral0Anchor">floatLiteral</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1FloatingLiteral.html">FloatingLiteral</a>>...</td></tr> <tr><td colspan="4" class="doc" id="floatLiteral0"><pre>Matches float literals of all sizes / encodings, e.g. 1.0, 1.0f, 1.0L and 1e10. @@ -5219,30 +5224,13 @@ </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; }; +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>></td><td class="name" onclick="toggle('hasClass0')"><a name="hasClass0Anchor">hasClass</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>> InnerMatcher</td></tr> +<tr><td colspan="4" class="doc" id="hasClass0"><pre>Matches if the Base specifier refers to the given class matcher. Example matches class Derived -(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base")))))) +(matcher = cxxRecordDecl(hasAnyBase(hasClass(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> @@ -5632,18 +5620,33 @@ <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; +Example: +matcher hasAnyBase(hasClass(hasName("SpecialBase"))) + class Foo; class Bar : Foo {}; class Baz : Bar {}; class SpecialBase; - class DirectlyDerived : SpecialBase {}; // directly derived - class IndirectlyDerived : DirectlyDerived {}; // indirectly derived + class Proxy : SpecialBase {}; // matches Proxy + class IndirectlyDerived : Proxy {}; //matches IndirectlyDerived 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('hasDirectBase0')"><a name="hasDirectBase0Anchor">hasDirectBase</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="hasDirectBase0"><pre>Matches C++ classes that have a direct base matching BaseSpecMatcher. + +Example: +matcher hasDirectBase(hasClass(hasName("SpecialBase"))) + class Foo; + class Bar : Foo {}; + class Baz : Bar {}; + class SpecialBase; + class Proxy : SpecialBase {}; // matches Proxy + class IndirectlyDerived : Proxy {}; // doesn't match +</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. @@ -6201,13 +6204,8 @@ 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>> +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>> </pre></td></tr> @@ -6421,13 +6419,8 @@ 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>> +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>> </pre></td></tr> @@ -7815,13 +7808,8 @@ 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>> +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>> </pre></td></tr>
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits