AntonBikineev created this revision. AntonBikineev added a reviewer: rsmith. AntonBikineev added a project: clang.
This patch adds isDirectlyDerivedFrom AST-matcher which is similar to isDerivedFrom but only matches against direct base classes. Repository: rC Clang https://reviews.llvm.org/D65092 Files: clang/docs/LibASTMatchersReference.html clang/include/clang/ASTMatchers/ASTMatchers.h clang/include/clang/ASTMatchers/ASTMatchersInternal.h clang/lib/ASTMatchers/ASTMatchFinder.cpp clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
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 T> class Y : public X {};" "class Z : public Y<int> {};", ZIsDerivedFromX)); + EXPECT_TRUE(notMatches("class X {};" + "template<class T> class Y : public X {};" + "class Z : public Y<int> {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE(matches("class X {}; template<class T> class Z : public X {};", ZIsDerivedFromX)); + EXPECT_TRUE(matches("class X {}; template<class T> class Z : public X {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template<class T> class X {}; " "template<class T> class Z : public X<T> {};", ZIsDerivedFromX)); + EXPECT_TRUE(matches("template<class T> class X {}; " + "template<class T> class Z : public X<T> {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template<class T, class U=T> class X {}; " "template<class T> class Z : public X<T> {};", ZIsDerivedFromX)); + EXPECT_TRUE(matches("template<class T, class U=T> class X {}; " + "template<class T> class Z : public X<T> {};", + 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 {}; }; " "class X{}; void y() { A<X>::Z z; }", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template<class X> class A { public: class Z : public X {}; }; " + "class X{}; void y() { A<X>::Z z; }", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template <class T> class X {}; " "template<class Y> class A { class Z : public X<Y> {}; };", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template <class T> class X {}; " + "template<class Y> class A { class Z : public X<Y> {}; };", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( notMatches("template<template<class T> class X> class A { " " class Z : public X<int> {}; };", ZIsDerivedFromX)); + EXPECT_TRUE(notMatches("template<template<class T> class X> class A { " + " class Z : public X<int> {}; };", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template<template<class T> class X> class A { " " public: class Z : public X<int> {}; }; " "template<class T> class X {}; void y() { A<X>::Z z; }", ZIsDerivedFromX)); + EXPECT_TRUE(matches("template<template<class T> class X> class A { " + " public: class Z : public X<int> {}; }; " + "template<class T> class X {}; void y() { A<X>::Z z; }", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( notMatches("template<class X> class A { class Z : public X::D {}; };", ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template<class X> class A { class Z : public X::D {}; };", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template<class X> class A { public: " " class Z : public X::D {}; }; " "class Y { public: class X {}; typedef X D; }; " "void y() { A<Y>::Z z; }", ZIsDerivedFromX)); + EXPECT_TRUE(matches("template<class X> class A { public: " + " class Z : public X::D {}; }; " + "class Y { public: class X {}; typedef X D; }; " + "void y() { A<Y>::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 T> class Y { typedef typename T::U X; " " class Z : public X {}; };", ZIsDerivedFromX)); + EXPECT_TRUE(matches("template<class T> 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 T> class X {}; " "template<class T> class A { class Z : public X<T>::D {}; };", ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template<class T> class X {}; " + "template<class T> class A { class Z : public X<T>::D {}; };", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template<class T> class X { public: typedef X<T> D; }; " "template<class T> class A { public: " " class Z : public X<T>::D {}; }; void y() { A<int>::Z z; }", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template<class T> class X { public: typedef X<T> D; }; " + "template<class T> class A { public: " + " class Z : public X<T>::D {}; }; void y() { A<int>::Z z; }", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( notMatches("template<class X> class A { class Z : public X::D::E {}; };", ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template<class X> 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 T, class U> class X {}; " "template<class T> class A { class Z : public X<T, int> {}; };", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template<class T, class U> class X {}; " + "template<class T> class A { class Z : public X<T, int> {}; };", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( notMatches("template<class X> class D { typedef X A; typedef A B; " " typedef B C; class Z : public C {}; };", ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template<class X> 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 <typename T> class Z;" "template <> class Z<void> {};" "template <typename T> class Z : public Z<void> {};", IsDerivedFromX)); + EXPECT_TRUE(notMatches("template <typename T> class Z;" + "template <> class Z<void> {};" + "template <typename T> class Z : public Z<void> {};", + IsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template <typename T> class X;" "template <> class X<void> {};" "template <typename T> class X : public X<void> {};", IsDerivedFromX)); + EXPECT_TRUE(matches("template <typename T> class X;" + "template <> class X<void> {};" + "template <typename T> class X : public X<void> {};", + IsDirectlyDerivedFromX)); EXPECT_TRUE(matches( "class X {};" "template <typename T> class Z;" "template <> class Z<void> {};" "template <typename T> class Z : public Z<void>, public X {};", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("class X {};" + "template <typename T> class Z;" + "template <> class Z<void> {};" + "template <typename T> class Z : public Z<void>, public X {};", + ZIsDirectlyDerivedFromX)); EXPECT_TRUE( notMatches("template<int> struct X;" "template<int i> struct X : public X<i-1> {};", cxxRecordDecl(isDerivedFrom(recordDecl(hasName("Some")))))); + EXPECT_TRUE(notMatches( + "template<int> struct X;" + "template<int i> struct X : public X<i-1> {};", + cxxRecordDecl(isDirectlyDerivedFrom(recordDecl(hasName("Some")))))); EXPECT_TRUE(matches( "struct A {};" "template<int> 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<int> struct X;" + "template<int i> struct X : public X<i-1> {};" + "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<typename T> class X {};" "template<typename T> using Z = X<T>;" "template <typename T> class Y : Z<T> {};", cxxRecordDecl(isDerivedFrom(namedDecl(hasName("X")))))); + EXPECT_TRUE( + matches("template<typename T> class X {};" + "template<typename T> using Z = X<T>;" + "template <typename T> class Y : Z<T> {};", + cxxRecordDecl(isDirectlyDerivedFrom(namedDecl(hasName("X")))))); } TEST(DeclarationMatcher, IsLambda) { 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<NamedDecl> &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<NamedDecl> &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/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<NamedDecl> &Base, - BoundNodesTreeBuilder *Builder) = 0; + BoundNodesTreeBuilder *Builder, + bool Directly) = 0; template <typename T> bool matchesChildOf(const T &Node, const DynTypedMatcher &Matcher, 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<NamedDecl>, 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<NamedDecl>, 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/docs/LibASTMatchersReference.html =================================================================== --- clang/docs/LibASTMatchersReference.html +++ clang/docs/LibASTMatchersReference.html @@ -2581,6 +2581,11 @@ </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('isDirectlyDerivedFrom1')"><a name="isDirectlyDerivedFrom1Anchor">isDirectlyDerivedFrom</a></td><td>std::string BaseName</td></tr> +<tr><td colspan="4" class="doc" id="isDirectlyDerivedFrom1"><pre>Overloaded method as shortcut for isDirectlyDerivedFrom(hasName(...)). +</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('isExplicitTemplateSpecialization2')"><a name="isExplicitTemplateSpecialization2Anchor">isExplicitTemplateSpecialization</a></td><td></td></tr> <tr><td colspan="4" class="doc" id="isExplicitTemplateSpecialization2"><pre>Matches explicit template specializations of function, class, or static member variable template instantiations. @@ -5269,6 +5274,27 @@ </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('isDirectlyDerivedFrom0')"><a name="isDirectlyDerivedFrom0Anchor">isDirectlyDerivedFrom</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1NamedDecl.html">NamedDecl</a>> Base</td></tr> +<tr><td colspan="4" class="doc" id="isDirectlyDerivedFrom0"><pre>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 +</pre></td></tr> + + <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXUnresolvedConstructExpr.html">CXXUnresolvedConstructExpr</a>></td><td class="name" onclick="toggle('hasAnyArgument2')"><a name="hasAnyArgument2Anchor">hasAnyArgument</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr> <tr><td colspan="4" class="doc" id="hasAnyArgument2"><pre>Matches any argument of a call expression or a constructor call expression, or an ObjC-message-send expression.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits