Index: clang/docs/LibASTMatchersReference.html =================================================================== --- clang/docs/LibASTMatchersReference.html +++ clang/docs/LibASTMatchersReference.html @@ -2581,6 +2581,11 @@ +
Overloaded method as shortcut for isDirectlyDerivedFrom(hasName(...)). +
Matches explicit template specializations of function, class, or static member variable template instantiations. @@ -5269,6 +5274,27 @@
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 isDirectlyDerivedFrom(hasName("X")): + class Foo; + typedef Foo X; + class Bar : public Foo {}; // derived from a type that X is a typedef of +
Matches any argument of a call expression or a constructor call expression, or an ObjC-message-send expression. Index: clang/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchers.h +++ clang/include/clang/ASTMatchers/ASTMatchers.h @@ -2634,7 +2634,7 @@ /// \endcode AST_MATCHER_P(CXXRecordDecl, isDerivedFrom, internal::Matcher, Base) { - return Finder->classIsDerivedFrom(&Node, Base, Builder); + return Finder->classIsDerivedFrom(&Node, Base, Builder, false); } /// Overloaded method as shortcut for \c isDerivedFrom(hasName(...)). @@ -2659,6 +2659,20 @@ return isSameOrDerivedFrom(hasName(BaseName)).matches(Node, Finder, Builder); } +/// Similar to \c isDerivedFrom(), but matches classes that directly derive from +/// \c Base. +AST_MATCHER_P_OVERLOAD(CXXRecordDecl, isDirectlyDerivedFrom, + internal::Matcher , Base, 0) { + return Finder->classIsDerivedFrom(&Node, Base, Builder, 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. /// Index: clang/include/clang/ASTMatchers/ASTMatchersInternal.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -974,10 +974,11 @@ /// 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, Index: clang/lib/ASTMatchers/ASTMatchFinder.cpp =================================================================== --- clang/lib/ASTMatchers/ASTMatchFinder.cpp +++ clang/lib/ASTMatchers/ASTMatchFinder.cpp @@ -430,7 +430,8 @@ 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 @@ // 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 @@ *Builder = std::move(Result); return true; } - if (classIsDerivedFrom(ClassDecl, Base, Builder)) + if (!Directly && classIsDerivedFrom(ClassDecl, Base, Builder, Directly)) return true; } return false; Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -331,6 +331,16 @@ 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,125 +351,240 @@ 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(matches("class X {}; template class Z : public X {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template class X {}; " "template class Z : public X {};", ZIsDerivedFromX)); + EXPECT_TRUE(matches("template class X {}; " + "template class Z : public X {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template class X {}; " "template class Z : public X {};", ZIsDerivedFromX)); + EXPECT_TRUE(matches("template class X {}; " + "template class Z : public X {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( notMatches("template class A { class Z : public X {}; };", ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template class A { class Z : public X {}; };", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template class A { public: class Z : public X {}; }; " "class X{}; void y() { A ::Z z; }", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template class A { public: class Z : public X {}; }; " + "class X{}; void y() { A ::Z z; }", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template class X {}; " "template class A { class Z : public X {}; };", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template class X {}; " + "template class A { class Z : public X {}; };", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( notMatches("template class X> class A { " " class Z : public X {}; };", ZIsDerivedFromX)); + EXPECT_TRUE(notMatches("template class X> class A { " + " class Z : public X {}; };", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template class X> class A { " " public: class Z : public X {}; }; " "template class X {}; void y() { A ::Z z; }", ZIsDerivedFromX)); + EXPECT_TRUE(matches("template class X> class A { " + " public: class Z : public X {}; }; " + "template class X {}; void y() { A ::Z z; }", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( notMatches("template class A { class Z : public X::D {}; };", ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template class A { class Z : public X::D {}; };", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template class A { public: " " class Z : public X::D {}; }; " "class Y { public: class X {}; typedef X D; }; " "void y() { A ::Z z; }", ZIsDerivedFromX)); + EXPECT_TRUE(matches("template class A { public: " + " class Z : public X::D {}; }; " + "class Y { public: class X {}; typedef X D; }; " + "void y() { A ::Z z; }", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("class X {}; typedef X Y; class Z : public Y {};", ZIsDerivedFromX)); + EXPECT_TRUE(matches("class X {}; typedef X Y; class Z : public Y {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template class Y { typedef typename T::U X; " " class Z : public X {}; };", ZIsDerivedFromX)); + EXPECT_TRUE(matches("template class Y { typedef typename T::U X; " + " class Z : public X {}; };", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE(matches("class X {}; class Z : public ::X {};", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("class X {}; class Z : public ::X {};", ZIsDirectlyDerivedFromX)); EXPECT_TRUE( notMatches("template class X {}; " "template class A { class Z : public X ::D {}; };", ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template class X {}; " + "template class A { class Z : public X ::D {}; };", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template class X { public: typedef X D; }; " "template class A { public: " " class Z : public X ::D {}; }; void y() { A ::Z z; }", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template class X { public: typedef X D; }; " + "template class A { public: " + " class Z : public X ::D {}; }; void y() { A ::Z z; }", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( notMatches("template class A { class Z : public X::D::E {}; };", ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template class A { class Z : public X::D::E {}; };", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("class X {}; typedef X V; typedef V W; class Z : public W {};", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("class X {}; typedef X V; typedef V W; class Z : public W {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( 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 {}; };", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template class X {}; " + "template class A { class Z : public X {}; };", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( notMatches("template class D { typedef X A; typedef A B; " " typedef B C; class Z : public C {}; };", ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template class D { typedef X A; typedef A B; " + " typedef B C; class Z : public C {}; };", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("class X {}; typedef X A; typedef A B; " "class Z : public B {};", ZIsDerivedFromX)); + EXPECT_TRUE(matches("class X {}; typedef X A; typedef A B; " + "class Z : public B {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("class X {}; typedef X A; typedef A B; typedef B C; " "class Z : public C {};", ZIsDerivedFromX)); + EXPECT_TRUE(matches("class X {}; typedef X A; typedef A B; typedef B C; " + "class Z : public C {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("class U {}; typedef U X; typedef X V; " "class Z : public V {};", ZIsDerivedFromX)); + EXPECT_TRUE(matches("class U {}; typedef U X; typedef X V; " + "class Z : public V {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("class Base {}; typedef Base X; " "class Z : public Base {};", ZIsDerivedFromX)); + EXPECT_TRUE(matches("class Base {}; typedef Base X; " + "class Z : public Base {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("class Base {}; typedef Base Base2; typedef Base2 X; " "class Z : public Base {};", ZIsDerivedFromX)); + EXPECT_TRUE(matches("class Base {}; typedef Base Base2; typedef Base2 X; " + "class Z : public Base {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( notMatches("class Base {}; class Base2 {}; typedef Base2 X; " "class Z : public Base {};", ZIsDerivedFromX)); + EXPECT_TRUE(notMatches("class Base {}; class Base2 {}; typedef Base2 X; " + "class Z : public Base {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("class A {}; typedef A X; typedef A Y; " "class Z : public Y {};", ZIsDerivedFromX)); + EXPECT_TRUE(matches("class A {}; typedef A X; typedef A Y; " + "class Z : public Y {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( notMatches("template class Z;" "template <> class Z {};" "template class Z : public Z {};", IsDerivedFromX)); + EXPECT_TRUE(notMatches("template class Z;" + "template <> class Z {};" + "template class Z : public Z {};", + IsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template class X;" "template <> class X {};" "template class X : public X {};", IsDerivedFromX)); + EXPECT_TRUE(matches("template class X;" + "template <> class X {};" + "template class X : public X {};", + IsDirectlyDerivedFromX)); EXPECT_TRUE(matches( "class X {};" "template class Z;" "template <> class Z {};" "template class Z : public Z , public X {};", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("class X {};" + "template class Z;" + "template <> class Z {};" + "template class Z : public Z , public X {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( notMatches("template struct X;" "template struct X : public X {};", cxxRecordDecl(isDerivedFrom(recordDecl(hasName("Some")))))); + EXPECT_TRUE(notMatches( + "template struct X;" + "template struct X : public X {};", + cxxRecordDecl(isDirectlyDerivedFrom(recordDecl(hasName("Some")))))); EXPECT_TRUE(matches( "struct A {};" "template struct X;" @@ -467,6 +592,14 @@ "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 @@ -484,15 +617,28 @@ RecursiveTemplateOneParameter, varDecl(hasName("z_float"), hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base1"))))))); + EXPECT_TRUE(notMatches( + RecursiveTemplateOneParameter, + varDecl(hasName("z_float"), hasInitializer(hasType(cxxRecordDecl( + isDirectlyDerivedFrom("Base1"))))))); EXPECT_TRUE(notMatches( RecursiveTemplateOneParameter, varDecl(hasName("z_float"), hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base2"))))))); + EXPECT_TRUE(notMatches( + RecursiveTemplateOneParameter, + varDecl(hasName("z_float"), hasInitializer(hasType(cxxRecordDecl( + isDirectlyDerivedFrom("Base2"))))))); EXPECT_TRUE(matches( RecursiveTemplateOneParameter, varDecl(hasName("z_char"), hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base1"), isDerivedFrom("Base2"))))))); + EXPECT_TRUE(notMatches( + RecursiveTemplateOneParameter, + varDecl(hasName("z_char"), hasInitializer(hasType(cxxRecordDecl( + isDirectlyDerivedFrom("Base1"), + isDirectlyDerivedFrom("Base2"))))))); const char *RecursiveTemplateTwoParameters = "class Base1 {}; class Base2 {};" @@ -509,31 +655,56 @@ RecursiveTemplateTwoParameters, varDecl(hasName("z_float"), hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base1"))))))); + EXPECT_TRUE(notMatches( + RecursiveTemplateTwoParameters, + varDecl(hasName("z_float"), hasInitializer(hasType(cxxRecordDecl( + isDirectlyDerivedFrom("Base1"))))))); EXPECT_TRUE(notMatches( RecursiveTemplateTwoParameters, varDecl(hasName("z_float"), hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base2"))))))); + EXPECT_TRUE(notMatches( + RecursiveTemplateTwoParameters, + varDecl(hasName("z_float"), hasInitializer(hasType(cxxRecordDecl( + isDirectlyDerivedFrom("Base2"))))))); EXPECT_TRUE(matches( RecursiveTemplateTwoParameters, varDecl(hasName("z_char"), hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base1"), isDerivedFrom("Base2"))))))); + EXPECT_TRUE(notMatches( + RecursiveTemplateTwoParameters, + varDecl(hasName("z_char"), hasInitializer(hasType(cxxRecordDecl( + isDirectlyDerivedFrom("Base1"), + isDirectlyDerivedFrom("Base2"))))))); EXPECT_TRUE(matches( "namespace ns { class X {}; class Y : public X {}; }", cxxRecordDecl(isDerivedFrom("::ns::X")))); + EXPECT_TRUE(matches("namespace ns { class X {}; class Y : public X {}; }", + cxxRecordDecl(isDirectlyDerivedFrom("::ns::X")))); EXPECT_TRUE(notMatches( "class X {}; class Y : public X {};", cxxRecordDecl(isDerivedFrom("::ns::X")))); + EXPECT_TRUE(notMatches("class X {}; class Y : public X {};", + cxxRecordDecl(isDirectlyDerivedFrom("::ns::X")))); EXPECT_TRUE(matches( "class X {}; class Y : public X {};", cxxRecordDecl(isDerivedFrom(recordDecl(hasName("X")).bind("test"))))); + EXPECT_TRUE(matches("class X {}; class Y : public X {};", + cxxRecordDecl(isDirectlyDerivedFrom( + recordDecl(hasName("X")).bind("test"))))); EXPECT_TRUE(matches( "template class X {};" "template using Z = X ;" "template class Y : Z {};", cxxRecordDecl(isDerivedFrom(namedDecl(hasName("X")))))); + EXPECT_TRUE( + matches("template class X {};" + "template using Z = X ;" + "template class Y : Z {};", + cxxRecordDecl(isDirectlyDerivedFrom(namedDecl(hasName("X")))))); } TEST(DeclarationMatcher, IsLambda) {