diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -6285,6 +6285,23 @@ +
Matches Objective-C classes that directly or indirectly subclass +a matching superclass. + +Note that a class is not considered to be a subclass of itself. + +Example matches implementation declarations for Y and Z. + (matcher = objcInterfaceDecl(isSubclassOfInterface(hasName("X")))) + @interface X + @end + @interface Y : X // directly derived + @end + @interface Z : Y // indirectly derived + @end +
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 @@ -1461,6 +1461,35 @@ extern const internal::VariadicDynCastAllOfMatcherobjcFinallyStmt; +/// Matches Objective-C classes that directly or indirectly subclass +/// a matching superclass. +/// +/// Note that a class is not considered to be a subclass of itself. +/// +/// Example matches implementation declarations for Y and Z. +/// (matcher = objcInterfaceDecl(isSubclassOfInterface(hasName("X")))) +/// \code +/// @interface X +/// @end +/// @interface Y : X // directly derived +/// @end +/// @interface Z : Y // indirectly derived +/// @end +/// \endcode +AST_MATCHER_P(ObjCInterfaceDecl, isSubclassOfInterface, + internal::Matcher , + InnerMatcher) { + // Check if any of the superclasses of the class match. + for (const ObjCInterfaceDecl *SuperClass = Node.getSuperClass(); + SuperClass != nullptr; SuperClass = SuperClass->getSuperClass()) { + if (InnerMatcher.matches(*SuperClass, Finder, Builder)) + return true; + } + + // No matches found. + return false; +} + /// Matches expressions that introduce cleanups to be run at the end /// of the sub-expression's evaluation. /// diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -393,6 +393,7 @@ REGISTER_MATCHER(isStaticLocal); REGISTER_MATCHER(isStaticStorageClass); REGISTER_MATCHER(isStruct); + REGISTER_MATCHER(isSubclassOfInterface); REGISTER_MATCHER(isTemplateInstantiation); REGISTER_MATCHER(isTypeDependent); REGISTER_MATCHER(isUnion); 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 @@ -1389,6 +1389,23 @@ EXPECT_TRUE(matchesObjC("void f() { ^{}(); }", blockExpr())); } +TEST(IsSubclassOfInterfaceMatcher, SubclassMatching) { + std::string ObjCString = "@interface A @end" + "@interface B : A @end" + "@interface C : B @end"; + EXPECT_TRUE(matchesObjC( + ObjCString, objcInterfaceDecl(isSubclassOfInterface(hasName("A"))))); + EXPECT_TRUE(matchesObjC(ObjCString, + objcInterfaceDecl(isSubclassOfInterface(hasName("A")), + unless(hasName("B"))))); + EXPECT_TRUE(matchesObjC( + ObjCString, objcInterfaceDecl(isSubclassOfInterface(hasName("B"))))); + EXPECT_FALSE(matchesObjC( + ObjCString, objcInterfaceDecl(isSubclassOfInterface(hasName("C"))))); + EXPECT_FALSE(matchesObjC( + ObjCString, objcInterfaceDecl(isSubclassOfInterface(hasName("X"))))); +} + TEST(StatementCountIs, FindsNoStatementsInAnEmptyCompoundStatement) { EXPECT_TRUE(matches("void f() { }", compoundStmt(statementCountIs(0))));