diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html index c93394096f2f..9f13331aa1bc 100644 --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -2581,6 +2581,11 @@ class y; +
Overloaded method as shortcut for isDirectlyDerivedFrom(hasName(...)). +
Matches explicit template specializations of function, class, or static member variable template instantiations. @@ -5263,6 +5268,26 @@ In the following example, Bar matches isDerivedFrom(hasName("X")):
Matches C++ classes that are directly derived from a class matching Base. + +Note that a class is not considered to be derived from itself. + +Example matches Y, C (Base == hasName("X")) + class X; + class Y : public X {}; // directly derived + class Z : public Y {}; // indirectly derived + typedef X A; + typedef A B; + class C : public B {}; // derived from a typedef of X + +In the following example, Bar matches isDerivedFrom(hasName("X")): + class Foo; + typedef Foo X; + class Bar : public Foo {}; // derived from a type that X is a typedef of +
Similar to isDerivedFrom(), but also matches classes that directly match Base. diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index 063d8217d9aa..a61358ea0e96 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -2634,7 +2634,7 @@ hasOverloadedOperatorName(StringRef Name) { /// \endcode AST_MATCHER_P(CXXRecordDecl, isDerivedFrom, internal::Matcher, Base) { - return Finder->classIsDerivedFrom(&Node, Base, Builder); + return Finder->classIsDerivedFrom(&Node, Base, Builder, /*Directly=*/false); } /// Overloaded method as shortcut for \c isDerivedFrom(hasName(...)). @@ -2659,6 +2659,38 @@ AST_MATCHER_P_OVERLOAD(CXXRecordDecl, isSameOrDerivedFrom, std::string, return isSameOrDerivedFrom(hasName(BaseName)).matches(Node, Finder, Builder); } +/// Matches C++ classes that are directly derived from a class matching \c Base. +/// +/// Note that a class is not considered to be derived from itself. +/// +/// Example matches Y, C (Base == hasName("X")) +/// \code +/// class X; +/// class Y : public X {}; // directly derived +/// class Z : public Y {}; // indirectly derived +/// typedef X A; +/// typedef A B; +/// class C : public B {}; // derived from a typedef of X +/// \endcode +/// +/// In the following example, Bar matches isDerivedFrom(hasName("X")): +/// \code +/// class Foo; +/// typedef Foo X; +/// class Bar : public Foo {}; // derived from a type that X is a typedef of +/// \endcode +AST_MATCHER_P_OVERLOAD(CXXRecordDecl, isDirectlyDerivedFrom, + internal::Matcher , Base, 0) { + return Finder->classIsDerivedFrom(&Node, Base, Builder, /*Directly=*/true); +} + +/// Overloaded method as shortcut for \c isDirectlyDerivedFrom(hasName(...)). +AST_MATCHER_P_OVERLOAD(CXXRecordDecl, isDirectlyDerivedFrom, std::string, + BaseName, 1) { + assert(!BaseName.empty()); + return isDirectlyDerivedFrom(hasName(BaseName)) + .matches(Node, Finder, Builder); +} /// Matches the first method of a class or struct that satisfies \c /// InnerMatcher. /// diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h index 32e2973c9c06..7b68d598c8b0 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -974,10 +974,11 @@ public: /// Returns true if the given class is directly or indirectly derived /// from a base type matching \c base. /// - /// A class is considered to be also derived from itself. + /// A class is not considered to be derived from itself. virtual bool classIsDerivedFrom(const CXXRecordDecl *Declaration, const Matcher &Base, - BoundNodesTreeBuilder *Builder) = 0; + BoundNodesTreeBuilder *Builder, + bool Directly) = 0; template bool matchesChildOf(const T &Node, const DynTypedMatcher &Matcher, diff --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp b/clang/lib/ASTMatchers/ASTMatchFinder.cpp index f407e0875ac4..6a4715ebe1b9 100644 --- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp @@ -430,7 +430,8 @@ public: bool classIsDerivedFrom(const CXXRecordDecl *Declaration, const Matcher &Base, - BoundNodesTreeBuilder *Builder) override; + BoundNodesTreeBuilder *Builder, + bool Directly) override; // Implements ASTMatchFinder::matchesChildOf. bool matchesChildOf(const ast_type_traits::DynTypedNode &Node, @@ -817,7 +818,8 @@ getAsCXXRecordDeclOrPrimaryTemplate(const Type *TypeNode) { // derived from itself. bool MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *Declaration, const Matcher &Base, - BoundNodesTreeBuilder *Builder) { + BoundNodesTreeBuilder *Builder, + bool Directly) { if (!Declaration->hasDefinition()) return false; for (const auto &It : Declaration->bases()) { @@ -842,7 +844,7 @@ bool MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *Declaration, *Builder = std::move(Result); return true; } - if (classIsDerivedFrom(ClassDecl, Base, Builder)) + if (!Directly && classIsDerivedFrom(ClassDecl, Base, Builder, Directly)) return true; } return false; diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp index 33058053571a..d6035adc7897 100644 --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -108,6 +108,7 @@ RegistryMaps::RegistryMaps() { REGISTER_OVERLOADED_2(hasType); REGISTER_OVERLOADED_2(ignoringParens); REGISTER_OVERLOADED_2(isDerivedFrom); + REGISTER_OVERLOADED_2(isDirectlyDerivedFrom); REGISTER_OVERLOADED_2(isSameOrDerivedFrom); REGISTER_OVERLOADED_2(loc); REGISTER_OVERLOADED_2(pointsTo); diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp index 8ab472e9dc8f..0fedb0c8d608 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -331,6 +331,16 @@ TEST(DeclarationMatcher, ClassIsDerived) { EXPECT_TRUE(notMatches("class Y;", IsDerivedFromX)); EXPECT_TRUE(notMatches("", IsDerivedFromX)); + DeclarationMatcher IsDirectlyDerivedFromX = + cxxRecordDecl(isDirectlyDerivedFrom("X")); + + EXPECT_TRUE( + matches("class X {}; class Y : public X {};", IsDirectlyDerivedFromX)); + EXPECT_TRUE(notMatches("class X {};", IsDirectlyDerivedFromX)); + EXPECT_TRUE(notMatches("class X;", IsDirectlyDerivedFromX)); + EXPECT_TRUE(notMatches("class Y;", IsDirectlyDerivedFromX)); + EXPECT_TRUE(notMatches("", IsDirectlyDerivedFromX)); + DeclarationMatcher IsAX = cxxRecordDecl(isSameOrDerivedFrom("X")); EXPECT_TRUE(matches("class X {}; class Y : public X {};", IsAX)); @@ -341,13 +351,22 @@ TEST(DeclarationMatcher, ClassIsDerived) { DeclarationMatcher ZIsDerivedFromX = cxxRecordDecl(hasName("Z"), isDerivedFrom("X")); + DeclarationMatcher ZIsDirectlyDerivedFromX = + cxxRecordDecl(hasName("Z"), isDirectlyDerivedFrom("X")); EXPECT_TRUE( matches("class X {}; class Y : public X {}; class Z : public Y {};", ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("class X {}; class Y : public X {}; class Z : public Y {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("class X {};" "template class Y : public X {};" "class Z : public Y {};", ZIsDerivedFromX)); + EXPECT_TRUE(notMatches("class X {};" + "template class Y : public X {};" + "class Z : public Y {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE(matches("class X {}; template class Z : public X {};", ZIsDerivedFromX)); EXPECT_TRUE( @@ -411,6 +430,9 @@ TEST(DeclarationMatcher, ClassIsDerived) { matches("class X {}; class Y : public X {}; " "typedef Y V; typedef V W; class Z : public W {};", ZIsDerivedFromX)); + EXPECT_TRUE(notMatches("class X {}; class Y : public X {}; " + "typedef Y V; typedef V W; class Z : public W {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template class X {}; " "template class A { class Z : public X {}; };", @@ -467,6 +489,14 @@ TEST(DeclarationMatcher, ClassIsDerived) { "template<> struct X<0> : public A {};" "struct B : public X<42> {};", cxxRecordDecl(hasName("B"), isDerivedFrom(recordDecl(hasName("A")))))); + EXPECT_TRUE(notMatches( + "struct A {};" + "template struct X;" + "template struct X : public X {};" + "template<> struct X<0> : public A {};" + "struct B : public X<42> {};", + cxxRecordDecl(hasName("B"), + isDirectlyDerivedFrom(recordDecl(hasName("A")))))); // FIXME: Once we have better matchers for template type matching, // get rid of the Variable(...) matching and match the right template