diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -2576,13 +2576,13 @@ -Matcher<CXXRecordDecl>isDerivedFromstd::string BaseName -
Overloaded method as shortcut for isDerivedFrom(hasName(...)).
+Matcher<CXXRecordDecl>isDerivedFromstd::string BaseName
+
Overloaded method as shortcut for isDerivedFrom(hasName(...)).
 
-Matcher<CXXRecordDecl>isDirectlyDerivedFromstd::string BaseName -
Overloaded method as shortcut for isDirectlyDerivedFrom(hasName(...)).
+Matcher<CXXRecordDecl>isDirectlyDerivedFromstd::string BaseName
+
Overloaded method as shortcut for isDirectlyDerivedFrom(hasName(...)).
 
@@ -2628,8 +2628,8 @@
-Matcher<CXXRecordDecl>isSameOrDerivedFromstd::string BaseName -
Overloaded method as shortcut for
+Matcher<CXXRecordDecl>isSameOrDerivedFromstd::string BaseName
+
Overloaded method as shortcut for
 isSameOrDerivedFrom(hasName(...)).
 
@@ -3593,6 +3593,22 @@
+Matcher<ObjCInterfaceDecl>isDerivedFromstd::string BaseName +
Overloaded method as shortcut for isDerivedFrom(hasName(...)).
+
+ + +Matcher<ObjCInterfaceDecl>isDirectlyDerivedFromstd::string BaseName +
Overloaded method as shortcut for isDirectlyDerivedFrom(hasName(...)).
+
+ + +Matcher<ObjCInterfaceDecl>isSameOrDerivedFromstd::string BaseName +
Overloaded method as shortcut for
+isSameOrDerivedFrom(hasName(...)).
+
+ + Matcher<ObjCMessageExpr>argumentCountIsunsigned N
Checks that a call expression or a constructor call expression has
 a specific number of arguments (including absent default arguments).
@@ -5248,8 +5264,9 @@
 
 
 Matcher<CXXRecordDecl>isDerivedFromMatcher<NamedDecl> Base
-
Matches C++ classes that are directly or indirectly derived from
-a class matching 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.
 
@@ -5265,11 +5282,18 @@
   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<CXXRecordDecl>isDirectlyDerivedFromMatcher<NamedDecl> Base -
Matches C++ classes that are directly derived from a class matching Base.
+
Matches C++ or Objective-C classes that are directly derived from a class
+matching Base.
 
 Note that a class is not considered to be derived from itself.
 
@@ -6469,6 +6493,61 @@
 
+Matcher<ObjCInterfaceDecl>isDerivedFromMatcher<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>isDirectlyDerivedFromMatcher<NamedDecl> Base +
Matches C++ or Objective-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 isDerivedFrom(hasName("X")):
+  class Foo;
+  typedef Foo X;
+  class Bar : public Foo {};  // derived from a type that X is a typedef of
+
+ + +Matcher<ObjCInterfaceDecl>isSameOrDerivedFromMatcher<NamedDecl> Base +
Similar to isDerivedFrom(), but also matches classes that directly
+match Base.
+
+ + 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
@@ -2611,8 +2611,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.
 ///
@@ -2632,36 +2633,80 @@
 ///   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, /*Directly=*/false);
+///
+/// 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 *RD = dyn_cast(&Node))
+    return Finder->classIsDerivedFrom(RD, Base, Builder, /*Directly=*/false);
+
+  // The node must be an Objective-C class.
+  const auto *InterfaceDecl = cast(&Node);
+  return Finder->objcClassIsDerivedFrom(InterfaceDecl, Base, Builder,
+                                        /*Directly=*/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) {
   if (BaseName.empty())
     return false;
-  return isDerivedFrom(hasName(BaseName)).matches(Node, Finder, Builder);
+
+  const auto M = isDerivedFrom(hasName(BaseName));
+
+  if (const auto *RD = dyn_cast(&Node))
+    return Matcher(M).matches(*RD, Finder, Builder);
+
+  const auto *InterfaceDecl = cast(&Node);
+  return Matcher(M).matches(*InterfaceDecl, Finder, Builder);
 }
 
 /// 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 *RD = dyn_cast(&Node))
+    return Matcher(M).matches(*RD, Finder, Builder);
+
+  const auto *InterfaceDecl = cast(&Node);
+  return Matcher(M).matches(*InterfaceDecl, Finder, Builder);
 }
 
 /// 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) {
   if (BaseName.empty())
     return false;
-  return isSameOrDerivedFrom(hasName(BaseName)).matches(Node, Finder, Builder);
+
+  const auto M = isSameOrDerivedFrom(hasName(BaseName));
+
+  if (const auto *RD = dyn_cast(&Node))
+    return Matcher(M).matches(*RD, Finder, Builder);
+
+  const auto *InterfaceDecl = cast(&Node);
+  return Matcher(M).matches(*InterfaceDecl, Finder, Builder);
 }
 
-/// Matches C++ classes that are directly derived from a class matching \c Base.
+/// Matches C++ or Objective-C classes that are directly derived from a class
+/// matching \c Base.
 ///
 /// Note that a class is not considered to be derived from itself.
 ///
@@ -2681,18 +2726,34 @@
 ///   typedef Foo X;
 ///   class Bar : public Foo {};  // derived from a type that X is a typedef of
 /// \endcode
-AST_MATCHER_P_OVERLOAD(CXXRecordDecl, isDirectlyDerivedFrom,
-                       internal::Matcher, Base, 0) {
-  return Finder->classIsDerivedFrom(&Node, Base, Builder, /*Directly=*/true);
+AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
+    isDirectlyDerivedFrom,
+    AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl),
+    internal::Matcher, Base, 0) {
+  // Check if the node is a C++ struct/union/class.
+  if (const auto *RD = dyn_cast(&Node))
+    return Finder->classIsDerivedFrom(RD, Base, Builder, /*Directly=*/true);
+
+  // The node must be an Objective-C class.
+  const auto *InterfaceDecl = cast(&Node);
+  return Finder->objcClassIsDerivedFrom(InterfaceDecl, Base, Builder,
+                                        /*Directly=*/true);
 }
 
 /// Overloaded method as shortcut for \c isDirectlyDerivedFrom(hasName(...)).
-AST_MATCHER_P_OVERLOAD(CXXRecordDecl, isDirectlyDerivedFrom, std::string,
-                       BaseName, 1) {
+AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
+    isDirectlyDerivedFrom,
+    AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl),
+    std::string, BaseName, 1) {
   if (BaseName.empty())
     return false;
-  return isDirectlyDerivedFrom(hasName(BaseName))
-      .matches(Node, Finder, Builder);
+  const auto M = isDirectlyDerivedFrom(hasName(BaseName));
+
+  if (const auto *RD = dyn_cast(&Node))
+    return Matcher(M).matches(*RD, Finder, Builder);
+
+  const auto *InterfaceDecl = cast(&Node);
+  return Matcher(M).matches(*InterfaceDecl, Finder, Builder);
 }
 /// Matches the first method of a class or struct that satisfies \c
 /// InnerMatcher.
diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -971,7 +971,7 @@
 
   virtual ~ASTMatchFinder() = default;
 
-  /// Returns true if the given class is directly or indirectly derived
+  /// Returns true if the given C++ class is directly or indirectly derived
   /// from a base type matching \c base.
   ///
   /// A class is not considered to be derived from itself.
@@ -980,6 +980,15 @@
                                   BoundNodesTreeBuilder *Builder,
                                   bool Directly) = 0;
 
+  /// Returns true if the given Objective-C class is directly or indirectly
+  /// derived from a base class matching \c base.
+  ///
+  /// A class is not considered to be derived from itself.
+  virtual bool objcClassIsDerivedFrom(const ObjCInterfaceDecl *Declaration,
+                                      const Matcher &Base,
+                                      BoundNodesTreeBuilder *Builder,
+                                      bool Directly) = 0;
+
   template 
   bool matchesChildOf(const T &Node, const DynTypedMatcher &Matcher,
                       BoundNodesTreeBuilder *Builder,
diff --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
--- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
@@ -374,6 +374,12 @@
     return true;
   }
 
+  bool VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *CAD) {
+    const ObjCInterfaceDecl *InterfaceDecl = CAD->getClassInterface();
+    CompatibleAliases[InterfaceDecl].insert(CAD);
+    return true;
+  }
+
   bool TraverseDecl(Decl *DeclNode);
   bool TraverseStmt(Stmt *StmtNode, DataRecursionQueue *Queue = nullptr);
   bool TraverseType(QualType TypeNode);
@@ -433,6 +439,11 @@
                           BoundNodesTreeBuilder *Builder,
                           bool Directly) override;
 
+  bool objcClassIsDerivedFrom(const ObjCInterfaceDecl *Declaration,
+                              const Matcher &Base,
+                              BoundNodesTreeBuilder *Builder,
+                              bool Directly) override;
+
   // Implements ASTMatchFinder::matchesChildOf.
   bool matchesChildOf(const ast_type_traits::DynTypedNode &Node,
                       const DynTypedMatcher &Matcher,
@@ -763,6 +774,23 @@
     return false;
   }
 
+  bool
+  objcClassHasMatchingCompatibilityAlias(const ObjCInterfaceDecl *InterfaceDecl,
+                                         const Matcher &Matcher,
+                                         BoundNodesTreeBuilder *Builder) {
+    auto Aliases = CompatibleAliases.find(InterfaceDecl);
+    if (Aliases == CompatibleAliases.end())
+      return false;
+    for (const ObjCCompatibleAliasDecl *Alias : Aliases->second) {
+      BoundNodesTreeBuilder Result(*Builder);
+      if (Matcher.matches(*Alias, this, &Result)) {
+        *Builder = std::move(Result);
+        return true;
+      }
+    }
+    return false;
+  }
+
   /// Bucket to record map.
   ///
   /// Used to get the appropriate bucket for each matcher.
@@ -787,6 +815,11 @@
   // Maps a canonical type to its TypedefDecls.
   llvm::DenseMap > TypeAliases;
 
+  // Maps an Objective-C interface to its ObjCCompatibleAliasDecls.
+  llvm::DenseMap>
+      CompatibleAliases;
+
   // Maps (matcher, node) -> the match result for memoization.
   typedef std::map MemoizationMap;
   MemoizationMap ResultCache;
@@ -813,7 +846,7 @@
   return nullptr;
 }
 
-// Returns true if the given class is directly or indirectly derived
+// Returns true if the given C++ class is directly or indirectly derived
 // from a base type with the given name.  A class is not considered to be
 // derived from itself.
 bool MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *Declaration,
@@ -850,6 +883,34 @@
   return false;
 }
 
+// Returns true if the given Objective-C class is directly or indirectly
+// derived from a matching base class. A class is not considered to be derived
+// from itself.
+bool MatchASTVisitor::objcClassIsDerivedFrom(
+    const ObjCInterfaceDecl *Declaration, const Matcher &Base,
+    BoundNodesTreeBuilder *Builder, bool Directly) {
+  // Check if any of the superclasses of the class match.
+  for (const ObjCInterfaceDecl *ClassDecl = Declaration->getSuperClass();
+       ClassDecl != nullptr; ClassDecl = ClassDecl->getSuperClass()) {
+    // Check if there are any matching compatibility aliases.
+    if (objcClassHasMatchingCompatibilityAlias(ClassDecl, Base, Builder))
+      return true;
+
+    // Check if there are any matching type aliases.
+    const Type *TypeNode = ClassDecl->getTypeForDecl();
+    if (typeHasMatchingAlias(TypeNode, Base, Builder))
+      return true;
+
+    if (Base.matches(*ClassDecl, this, Builder))
+      return true;
+
+    if (Directly)
+      return false;
+  }
+
+  return false;
+}
+
 bool MatchASTVisitor::TraverseDecl(Decl *DeclNode) {
   if (!DeclNode) {
     return true;
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
@@ -573,6 +573,111 @@
   EXPECT_TRUE(notMatches(Code, cxxRecordDecl(isSameOrDerivedFrom(""))));
 }
 
+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(matchesObjC(
+      "@interface X @end @compatibility_alias Y X; @interface Z : Y @end",
+      IsDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface X @end typedef X Y; @interface Z : Y @end", IsDerivedFromX));
+  EXPECT_TRUE(notMatchesObjC("@interface X @end", IsDerivedFromX));
+  EXPECT_TRUE(notMatchesObjC("@class X;", IsDerivedFromX));
+  EXPECT_TRUE(notMatchesObjC("@class Y;", IsDerivedFromX));
+  EXPECT_TRUE(notMatchesObjC("@interface X @end @compatibility_alias Y X;",
+                             IsDerivedFromX));
+  EXPECT_TRUE(notMatchesObjC("@interface X @end typedef X Y;", IsDerivedFromX));
+
+  DeclarationMatcher IsDirectlyDerivedFromX =
+      objcInterfaceDecl(isDirectlyDerivedFrom("X"));
+  EXPECT_TRUE(
+      matchesObjC("@interface X @end @interface Y : X @end", IsDirectlyDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface X @end @interface Y<__covariant ObjectType> : X @end",
+      IsDirectlyDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface X @end @compatibility_alias Y X; @interface Z : Y @end",
+      IsDirectlyDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface X @end typedef X Y; @interface Z : Y @end",
+      IsDirectlyDerivedFromX));
+  EXPECT_TRUE(notMatchesObjC("@interface X @end", IsDirectlyDerivedFromX));
+  EXPECT_TRUE(notMatchesObjC("@class X;", IsDirectlyDerivedFromX));
+  EXPECT_TRUE(notMatchesObjC("@class Y;", IsDirectlyDerivedFromX));
+  EXPECT_TRUE(notMatchesObjC("@interface X @end @compatibility_alias Y X;",
+                             IsDirectlyDerivedFromX));
+  EXPECT_TRUE(notMatchesObjC("@interface X @end typedef X Y;",
+                             IsDirectlyDerivedFromX));
+
+  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"));
+  DeclarationMatcher ZIsDirectlyDerivedFromX =
+      objcInterfaceDecl(hasName("Z"), isDirectlyDerivedFrom("X"));
+  EXPECT_TRUE(matchesObjC(
+      "@interface X @end @interface Y : X @end @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(matchesObjC(
+      "@interface X @end typedef X Y; @interface Z : Y @end", ZIsDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface X @end typedef X Y; @interface Z : Y @end",
+      ZIsDirectlyDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface A @end typedef A X; typedef A Y; @interface Z : Y @end",
+      ZIsDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface A @end typedef A X; typedef A Y; @interface Z : Y @end",
+      ZIsDirectlyDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface X @end @compatibility_alias Y X; @interface Z : Y @end",
+      ZIsDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface X @end @compatibility_alias Y X; @interface Z : Y @end",
+      ZIsDirectlyDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface Y @end @compatibility_alias X Y; @interface Z : Y @end",
+      ZIsDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface Y @end @compatibility_alias X Y; @interface Z : Y @end",
+      ZIsDirectlyDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface A @end @compatibility_alias X A; @compatibility_alias Y A;"
+      "@interface Z : Y @end", ZIsDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface A @end @compatibility_alias X A; @compatibility_alias Y A;"
+      "@interface Z : Y @end", ZIsDirectlyDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface Y @end typedef Y X; @interface Z : X @end", ZIsDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface Y @end typedef Y X; @interface Z : X @end",
+      ZIsDirectlyDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface A @end @compatibility_alias Y A; typedef Y X;"
+      "@interface Z : A @end", ZIsDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface A @end @compatibility_alias Y A; typedef Y X;"
+      "@interface Z : A @end", ZIsDirectlyDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface A @end typedef A Y; @compatibility_alias X Y;"
+      "@interface Z : A @end", ZIsDerivedFromX));
+  EXPECT_TRUE(matchesObjC(
+      "@interface A @end typedef A Y; @compatibility_alias X Y;"
+      "@interface Z : A @end", ZIsDirectlyDerivedFromX));
+}
+
 TEST(DeclarationMatcher, IsLambda) {
   const auto IsLambda = cxxMethodDecl(ofClass(cxxRecordDecl(isLambda())));
   EXPECT_TRUE(matches("auto x = []{};", IsLambda));