diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -2526,8 +2526,8 @@ -
Overloaded method as shortcut for isDerivedFrom(hasName(...)). ++ Matcher<CXXRecordDecl> isDerivedFrom std::string BaseName @@ -2573,8 +2573,8 @@ Overloaded method as shortcut for isDerivedFrom(hasName(...)).
Overloaded method as shortcut for ++ Matcher<CXXRecordDecl> isSameOrDerivedFrom std::string BaseName @@ -3538,6 +3538,17 @@ Overloaded method as shortcut for isSameOrDerivedFrom(hasName(...)).
Overloaded method as shortcut for isDerivedFrom(hasName(...)). +
Overloaded method as shortcut for +isSameOrDerivedFrom(hasName(...)). +
Checks that a call expression or a constructor call expression has a specific number of arguments (including absent default arguments). @@ -5193,8 +5204,9 @@- Matcher<CXXRecordDecl> isDerivedFrom Matcher<NamedDecl> Base + Matches C++ classes that are directly or indirectly derived from -a class matching Base. +@@ -6345,6 +6363,40 @@ Matches C++ classes that are directly or indirectly derived from a class +matching Base, or Objective-C classes that directly or indirectly +subclass a class matching Base. Note that a class is not considered to be derived from itself. @@ -5210,6 +5222,12 @@ class Foo; typedef Foo X; class Bar : public Foo {}; // derived from a type that X is a typedef of + +In the following example, Bar matches isDerivedFrom(hasName("NSObject")) + @interface NSObject @end + @interface Bar : NSObject @end + +Usable as: Matcher<CXXRecordDecl>, Matcher<ObjCInterfaceDecl>+ Matcher<ObjCInterfaceDecl> isDerivedFrom Matcher<NamedDecl> Base + + + Matches C++ classes that are directly or indirectly derived from a class +matching Base, or Objective-C classes that directly or indirectly +subclass a class matching Base. + +Note that a class is not considered to be derived from itself. + +Example matches Y, Z, 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 + +In the following example, Bar matches isDerivedFrom(hasName("NSObject")) + @interface NSObject @end + @interface Bar : NSObject @end + +Usable as: Matcher<CXXRecordDecl>, Matcher<ObjCInterfaceDecl> ++ Matcher<ObjCInterfaceDecl> isSameOrDerivedFrom Matcher<NamedDecl> Base + + Similar to isDerivedFrom(), but also matches classes that directly +match Base. +Matcher<ObjCMessageExpr> hasAnyArgument Matcher<Expr> InnerMatcher Matches any argument of a call expression or a constructor call expression, or an ObjC-message-send expression. 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 @@ -2600,8 +2600,9 @@ AST_POLYMORPHIC_SUPPORTED_TYPES(CXXOperatorCallExpr, FunctionDecl)>(Name); } -/// Matches C++ classes that are directly or indirectly derived from -/// a class matching \c Base. +/// 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. /// /// Note that a class is not considered to be derived from itself. /// @@ -2621,31 +2622,94 @@ /// typedef Foo X; /// class Bar : public Foo {}; // derived from a type that X is a typedef of /// \endcode -AST_MATCHER_P(CXXRecordDecl, isDerivedFrom, - internal::Matcher, Base) { - return Finder->classIsDerivedFrom(&Node, Base, Builder); +/// +/// In the following example, Bar matches isDerivedFrom(hasName("NSObject")) +/// \code +/// @interface NSObject @end +/// @interface Bar : NSObject @end +/// \endcode +/// +/// Usable as: Matcher , Matcher +AST_POLYMORPHIC_MATCHER_P( + isDerivedFrom, + AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl), + internal::Matcher , Base) { + // Check if the node is a C++ struct/union/class. + if (const auto *RecordDecl = dyn_cast (&Node)) + return Finder->classIsDerivedFrom(RecordDecl, Base, Builder); + + // Check if the node is an Objective-C class. + if (const auto *InterfaceDecl = dyn_cast (&Node)) { + // Check if any of the superclasses of the class match. + for (const ObjCInterfaceDecl *SuperClass = InterfaceDecl->getSuperClass(); + SuperClass != nullptr; SuperClass = SuperClass->getSuperClass()) { + if (Base.matches(*SuperClass, Finder, Builder)) + return true; + } + } + + // No matches found. + return false; } /// Overloaded method as shortcut for \c isDerivedFrom(hasName(...)). -AST_MATCHER_P_OVERLOAD(CXXRecordDecl, isDerivedFrom, std::string, BaseName, 1) { +AST_POLYMORPHIC_MATCHER_P_OVERLOAD( + isDerivedFrom, + AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl), + std::string, BaseName, 1) { assert(!BaseName.empty()); - return isDerivedFrom(hasName(BaseName)).matches(Node, Finder, Builder); + + const auto M = isDerivedFrom(hasName(BaseName)); + + if (const auto *RecordDecl = dyn_cast (&Node)) + return Matcher (M).matches(*RecordDecl, Finder, Builder); + + if (const auto *InterfaceDecl = dyn_cast (&Node)) { + return Matcher (M).matches(*InterfaceDecl, Finder, + Builder); + } + + llvm_unreachable("Not a valid polymorphic type"); } /// Similar to \c isDerivedFrom(), but also matches classes that directly /// match \c Base. -AST_MATCHER_P_OVERLOAD(CXXRecordDecl, isSameOrDerivedFrom, - internal::Matcher , Base, 0) { - return Matcher (anyOf(Base, isDerivedFrom(Base))) - .matches(Node, Finder, Builder); +AST_POLYMORPHIC_MATCHER_P_OVERLOAD( + isSameOrDerivedFrom, + AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl), + internal::Matcher , Base, 0) { + const auto M = anyOf(Base, isDerivedFrom(Base)); + + if (const auto *RecordDecl = dyn_cast (&Node)) + return Matcher (M).matches(*RecordDecl, Finder, Builder); + + if (const auto *InterfaceDecl = dyn_cast (&Node)) { + return Matcher (M).matches(*InterfaceDecl, Finder, + Builder); + } + + llvm_unreachable("Not a valid polymorphic type"); } /// Overloaded method as shortcut for /// \c isSameOrDerivedFrom(hasName(...)). -AST_MATCHER_P_OVERLOAD(CXXRecordDecl, isSameOrDerivedFrom, std::string, - BaseName, 1) { +AST_POLYMORPHIC_MATCHER_P_OVERLOAD( + isSameOrDerivedFrom, + AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl), + std::string, BaseName, 1) { assert(!BaseName.empty()); - return isSameOrDerivedFrom(hasName(BaseName)).matches(Node, Finder, Builder); + + const auto M = isSameOrDerivedFrom(hasName(BaseName)); + + if (const auto *RecordDecl = dyn_cast (&Node)) + return Matcher (M).matches(*RecordDecl, Finder, Builder); + + if (const auto *InterfaceDecl = dyn_cast (&Node)) { + return Matcher (M).matches(*InterfaceDecl, Finder, + Builder); + } + + llvm_unreachable("Not a valid polymorphic type"); } /// Matches the first method of a class or struct that satisfies \c 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 @@ -536,6 +536,38 @@ cxxRecordDecl(isDerivedFrom(namedDecl(hasName("X")))))); } +TEST(DeclarationMatcher, ObjCClassIsDerived) { + DeclarationMatcher IsDerivedFromX = objcInterfaceDecl(isDerivedFrom("X")); + EXPECT_TRUE( + matchesObjC("@interface X @end @interface Y : X @end", IsDerivedFromX)); + EXPECT_TRUE(matchesObjC( + "@interface X @end @interface Y<__covariant ObjectType> : X @end", + IsDerivedFromX)); + EXPECT_TRUE(notMatchesObjC("@interface X @end", IsDerivedFromX)); + EXPECT_TRUE(notMatchesObjC("@class X;", IsDerivedFromX)); + EXPECT_TRUE(notMatchesObjC("@class Y;", IsDerivedFromX)); + + DeclarationMatcher IsAX = objcInterfaceDecl(isSameOrDerivedFrom("X")); + EXPECT_TRUE(matchesObjC("@interface X @end @interface Y : X @end", IsAX)); + EXPECT_TRUE(matchesObjC("@interface X @end", IsAX)); + EXPECT_TRUE(matchesObjC("@class X;", IsAX)); + EXPECT_TRUE(notMatchesObjC("@interface Y @end", IsAX)); + EXPECT_TRUE(notMatchesObjC("@class Y;", IsAX)); + + DeclarationMatcher ZIsDerivedFromX = + objcInterfaceDecl(hasName("Z"), isDerivedFrom("X")); + EXPECT_TRUE(matchesObjC( + "@interface X @end @interface Y : X @end @interface Z : Y @end", + ZIsDerivedFromX)); + EXPECT_TRUE(matchesObjC( + "@interface X @end typedef X Y; @interface Z : Y @end", ZIsDerivedFromX)); + EXPECT_TRUE(matchesObjC("@interface X @end @interface Y : X @end typedef Y " + "V; typedef V W; @interface Z : W @end", + ZIsDerivedFromX)); + EXPECT_TRUE(notMatchesObjC( + "@interface Y @end typedef Y X; @interface Z : X @end", ZIsDerivedFromX)); +} + TEST(DeclarationMatcher, IsLambda) { const auto IsLambda = cxxMethodDecl(ofClass(cxxRecordDecl(isLambda()))); EXPECT_TRUE(matches("auto x = []{};", IsLambda));