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 @@ +Matcher<ObjCInterfaceDecl>isSubclassOfInterfaceMatcher<ObjCInterfaceDecl> InnerMatcher +
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
+
+ + Matcher<ObjCMessageExpr>hasAnyArgumentMatcher<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
@@ -1461,6 +1461,35 @@
 extern const internal::VariadicDynCastAllOfMatcher
     objcFinallyStmt;
 
+/// 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))));