summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephane Moore <mog@google.com>2019-08-12 23:23:35 +0000
committerStephane Moore <mog@google.com>2019-08-12 23:23:35 +0000
commita0a47d8ac113b1e959288698deef847f562921d3 (patch)
tree7389a9dabcd21a82c8143ec15db6fd955a0973a9
parente9865b9b31bb2e6bc742dc6fca8f9f9517c3c43e (diff)
downloadbcm5719-llvm-a0a47d8ac113b1e959288698deef847f562921d3.tar.gz
bcm5719-llvm-a0a47d8ac113b1e959288698deef847f562921d3.zip
[clang] Update isDerivedFrom to support Objective-C classes 🔍
Summary: This change updates `isDerivedFrom` to support Objective-C classes by converting it to a polymorphic matcher. Notes: The matching behavior for Objective-C classes is modeled to match the behavior of `isDerivedFrom` with C++ classes. To that effect, `isDerivedFrom` matches aliased types of derived Objective-C classes, including compatibility aliases. To achieve this, the AST visitor has been updated to map compatibility aliases to their underlying Objective-C class. `isSameOrDerivedFrom` also provides similar behaviors for C++ and Objective-C classes. The behavior that `cxxRecordDecl(isSameOrDerivedFrom("X"))` does not match `class Y {}; typedef Y X;` is mirrored for Objective-C in that `objcInterfaceDecl(isSameOrDerivedFrom("X"))` does not match either `@interface Y @end typedef Y X;` or `@interface Y @end @compatibility_alias X Y;`. Test Notes: Ran clang unit tests. Reviewers: aaron.ballman, jordan_rose, rjmccall, klimek, alexfh, gribozavr Reviewed By: aaron.ballman, gribozavr Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D60543 llvm-svn: 368632
-rw-r--r--clang/docs/LibASTMatchersReference.html97
-rw-r--r--clang/include/clang/ASTMatchers/ASTMatchers.h105
-rw-r--r--clang/include/clang/ASTMatchers/ASTMatchersInternal.h11
-rw-r--r--clang/lib/ASTMatchers/ASTMatchFinder.cpp63
-rw-r--r--clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp105
-rw-r--r--clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp2
6 files changed, 349 insertions, 34 deletions
diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html
index 68d536a0704..d9e867c9519 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -2576,13 +2576,13 @@ class y;
</pre></td></tr>
-<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>&gt;</td><td class="name" onclick="toggle('isDerivedFrom1')"><a name="isDerivedFrom1Anchor">isDerivedFrom</a></td><td>std::string BaseName</td></tr>
-<tr><td colspan="4" class="doc" id="isDerivedFrom1"><pre>Overloaded method as shortcut for isDerivedFrom(hasName(...)).
+<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>&gt;</td><td class="name" onclick="toggle('isDerivedFrom2')"><a name="isDerivedFrom2Anchor">isDerivedFrom</a></td><td>std::string BaseName</td></tr>
+<tr><td colspan="4" class="doc" id="isDerivedFrom2"><pre>Overloaded method as shortcut for isDerivedFrom(hasName(...)).
</pre></td></tr>
-<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>&gt;</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(...)).
+<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>&gt;</td><td class="name" onclick="toggle('isDirectlyDerivedFrom2')"><a name="isDirectlyDerivedFrom2Anchor">isDirectlyDerivedFrom</a></td><td>std::string BaseName</td></tr>
+<tr><td colspan="4" class="doc" id="isDirectlyDerivedFrom2"><pre>Overloaded method as shortcut for isDirectlyDerivedFrom(hasName(...)).
</pre></td></tr>
@@ -2628,8 +2628,8 @@ decltype(x)
</pre></td></tr>
-<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>&gt;</td><td class="name" onclick="toggle('isSameOrDerivedFrom1')"><a name="isSameOrDerivedFrom1Anchor">isSameOrDerivedFrom</a></td><td>std::string BaseName</td></tr>
-<tr><td colspan="4" class="doc" id="isSameOrDerivedFrom1"><pre>Overloaded method as shortcut for
+<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>&gt;</td><td class="name" onclick="toggle('isSameOrDerivedFrom2')"><a name="isSameOrDerivedFrom2Anchor">isSameOrDerivedFrom</a></td><td>std::string BaseName</td></tr>
+<tr><td colspan="4" class="doc" id="isSameOrDerivedFrom2"><pre>Overloaded method as shortcut for
isSameOrDerivedFrom(hasName(...)).
</pre></td></tr>
@@ -3593,6 +3593,22 @@ Given
</pre></td></tr>
+<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCInterfaceDecl.html">ObjCInterfaceDecl</a>&gt;</td><td class="name" onclick="toggle('isDerivedFrom3')"><a name="isDerivedFrom3Anchor">isDerivedFrom</a></td><td>std::string BaseName</td></tr>
+<tr><td colspan="4" class="doc" id="isDerivedFrom3"><pre>Overloaded method as shortcut for isDerivedFrom(hasName(...)).
+</pre></td></tr>
+
+
+<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCInterfaceDecl.html">ObjCInterfaceDecl</a>&gt;</td><td class="name" onclick="toggle('isDirectlyDerivedFrom3')"><a name="isDirectlyDerivedFrom3Anchor">isDirectlyDerivedFrom</a></td><td>std::string BaseName</td></tr>
+<tr><td colspan="4" class="doc" id="isDirectlyDerivedFrom3"><pre>Overloaded method as shortcut for isDirectlyDerivedFrom(hasName(...)).
+</pre></td></tr>
+
+
+<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCInterfaceDecl.html">ObjCInterfaceDecl</a>&gt;</td><td class="name" onclick="toggle('isSameOrDerivedFrom3')"><a name="isSameOrDerivedFrom3Anchor">isSameOrDerivedFrom</a></td><td>std::string BaseName</td></tr>
+<tr><td colspan="4" class="doc" id="isSameOrDerivedFrom3"><pre>Overloaded method as shortcut for
+isSameOrDerivedFrom(hasName(...)).
+</pre></td></tr>
+
+
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCMessageExpr.html">ObjCMessageExpr</a>&gt;</td><td class="name" onclick="toggle('argumentCountIs2')"><a name="argumentCountIs2Anchor">argumentCountIs</a></td><td>unsigned N</td></tr>
<tr><td colspan="4" class="doc" id="argumentCountIs2"><pre>Checks that a call expression or a constructor call expression has
a specific number of arguments (including absent default arguments).
@@ -5248,8 +5264,9 @@ A but not B.
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>&gt;</td><td class="name" onclick="toggle('isDerivedFrom0')"><a name="isDerivedFrom0Anchor">isDerivedFrom</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1NamedDecl.html">NamedDecl</a>&gt; Base</td></tr>
-<tr><td colspan="4" class="doc" id="isDerivedFrom0"><pre>Matches C++ classes that are directly or indirectly derived from
-a class matching Base.
+<tr><td colspan="4" class="doc" id="isDerivedFrom0"><pre>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 @@ 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&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCInterfaceDecl.html">ObjCInterfaceDecl</a>&gt;
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>&gt;</td><td class="name" onclick="toggle('isDirectlyDerivedFrom0')"><a name="isDirectlyDerivedFrom0Anchor">isDirectlyDerivedFrom</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1NamedDecl.html">NamedDecl</a>&gt; Base</td></tr>
-<tr><td colspan="4" class="doc" id="isDirectlyDerivedFrom0"><pre>Matches C++ classes that are directly derived from a class matching Base.
+<tr><td colspan="4" class="doc" id="isDirectlyDerivedFrom0"><pre>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.
@@ -6468,6 +6492,61 @@ Given
</pre></td></tr>
+<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCInterfaceDecl.html">ObjCInterfaceDecl</a>&gt;</td><td class="name" onclick="toggle('isDerivedFrom1')"><a name="isDerivedFrom1Anchor">isDerivedFrom</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1NamedDecl.html">NamedDecl</a>&gt; Base</td></tr>
+<tr><td colspan="4" class="doc" id="isDerivedFrom1"><pre>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&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>&gt;, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCInterfaceDecl.html">ObjCInterfaceDecl</a>&gt;
+</pre></td></tr>
+
+
+<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCInterfaceDecl.html">ObjCInterfaceDecl</a>&gt;</td><td class="name" onclick="toggle('isDirectlyDerivedFrom1')"><a name="isDirectlyDerivedFrom1Anchor">isDirectlyDerivedFrom</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1NamedDecl.html">NamedDecl</a>&gt; Base</td></tr>
+<tr><td colspan="4" class="doc" id="isDirectlyDerivedFrom1"><pre>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
+</pre></td></tr>
+
+
+<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCInterfaceDecl.html">ObjCInterfaceDecl</a>&gt;</td><td class="name" onclick="toggle('isSameOrDerivedFrom1')"><a name="isSameOrDerivedFrom1Anchor">isSameOrDerivedFrom</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1NamedDecl.html">NamedDecl</a>&gt; Base</td></tr>
+<tr><td colspan="4" class="doc" id="isSameOrDerivedFrom1"><pre>Similar to isDerivedFrom(), but also matches classes that directly
+match Base.
+</pre></td></tr>
+
+
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCMessageExpr.html">ObjCMessageExpr</a>&gt;</td><td class="name" onclick="toggle('hasAnyArgument3')"><a name="hasAnyArgument3Anchor">hasAnyArgument</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasAnyArgument3"><pre>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
index 6bf20ead1b0..e6d25069f1c 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -2611,8 +2611,9 @@ hasOverloadedOperatorName(StringRef Name) {
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 @@ hasOverloadedOperatorName(StringRef Name) {
/// 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<NamedDecl>, 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<CXXRecordDecl>, Matcher<ObjCInterfaceDecl>
+AST_POLYMORPHIC_MATCHER_P(
+ isDerivedFrom,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl),
+ internal::Matcher<NamedDecl>, Base) {
+ // Check if the node is a C++ struct/union/class.
+ if (const auto *RD = dyn_cast<CXXRecordDecl>(&Node))
+ return Finder->classIsDerivedFrom(RD, Base, Builder, /*Directly=*/false);
+
+ // The node must be an Objective-C class.
+ const auto *InterfaceDecl = cast<ObjCInterfaceDecl>(&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<CXXRecordDecl>(&Node))
+ return Matcher<CXXRecordDecl>(M).matches(*RD, Finder, Builder);
+
+ const auto *InterfaceDecl = cast<ObjCInterfaceDecl>(&Node);
+ return Matcher<ObjCInterfaceDecl>(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<NamedDecl>, Base, 0) {
- return Matcher<CXXRecordDecl>(anyOf(Base, isDerivedFrom(Base)))
- .matches(Node, Finder, Builder);
+AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
+ isSameOrDerivedFrom,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl),
+ internal::Matcher<NamedDecl>, Base, 0) {
+ const auto M = anyOf(Base, isDerivedFrom(Base));
+
+ if (const auto *RD = dyn_cast<CXXRecordDecl>(&Node))
+ return Matcher<CXXRecordDecl>(M).matches(*RD, Finder, Builder);
+
+ const auto *InterfaceDecl = cast<ObjCInterfaceDecl>(&Node);
+ return Matcher<ObjCInterfaceDecl>(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<CXXRecordDecl>(&Node))
+ return Matcher<CXXRecordDecl>(M).matches(*RD, Finder, Builder);
+
+ const auto *InterfaceDecl = cast<ObjCInterfaceDecl>(&Node);
+ return Matcher<ObjCInterfaceDecl>(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 @@ AST_MATCHER_P_OVERLOAD(CXXRecordDecl, isSameOrDerivedFrom, std::string,
/// 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<NamedDecl>, Base, 0) {
- return Finder->classIsDerivedFrom(&Node, Base, Builder, /*Directly=*/true);
+AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
+ isDirectlyDerivedFrom,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl),
+ internal::Matcher<NamedDecl>, Base, 0) {
+ // Check if the node is a C++ struct/union/class.
+ if (const auto *RD = dyn_cast<CXXRecordDecl>(&Node))
+ return Finder->classIsDerivedFrom(RD, Base, Builder, /*Directly=*/true);
+
+ // The node must be an Objective-C class.
+ const auto *InterfaceDecl = cast<ObjCInterfaceDecl>(&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<CXXRecordDecl>(&Node))
+ return Matcher<CXXRecordDecl>(M).matches(*RD, Finder, Builder);
+
+ const auto *InterfaceDecl = cast<ObjCInterfaceDecl>(&Node);
+ return Matcher<ObjCInterfaceDecl>(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
index 7b68d598c8b..46e47a3100b 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -971,7 +971,7 @@ public:
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 @@ public:
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<NamedDecl> &Base,
+ BoundNodesTreeBuilder *Builder,
+ bool Directly) = 0;
+
template <typename T>
bool matchesChildOf(const T &Node, const DynTypedMatcher &Matcher,
BoundNodesTreeBuilder *Builder,
diff --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
index 6a4715ebe1b..6eb4a2c4268 100644
--- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
@@ -374,6 +374,12 @@ public:
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 @@ public:
BoundNodesTreeBuilder *Builder,
bool Directly) override;
+ bool objcClassIsDerivedFrom(const ObjCInterfaceDecl *Declaration,
+ const Matcher<NamedDecl> &Base,
+ BoundNodesTreeBuilder *Builder,
+ bool Directly) override;
+
// Implements ASTMatchFinder::matchesChildOf.
bool matchesChildOf(const ast_type_traits::DynTypedNode &Node,
const DynTypedMatcher &Matcher,
@@ -763,6 +774,23 @@ private:
return false;
}
+ bool
+ objcClassHasMatchingCompatibilityAlias(const ObjCInterfaceDecl *InterfaceDecl,
+ const Matcher<NamedDecl> &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 @@ private:
// Maps a canonical type to its TypedefDecls.
llvm::DenseMap<const Type*, std::set<const TypedefNameDecl*> > TypeAliases;
+ // Maps an Objective-C interface to its ObjCCompatibleAliasDecls.
+ llvm::DenseMap<const ObjCInterfaceDecl *,
+ llvm::SmallPtrSet<const ObjCCompatibleAliasDecl *, 2>>
+ CompatibleAliases;
+
// Maps (matcher, node) -> the match result for memoization.
typedef std::map<MatchKey, MemoizedMatchResult> MemoizationMap;
MemoizationMap ResultCache;
@@ -813,7 +846,7 @@ getAsCXXRecordDeclOrPrimaryTemplate(const Type *TypeNode) {
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 @@ bool MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *Declaration,
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<NamedDecl> &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
index 2038e9b6c00..52cd042c965 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -573,6 +573,111 @@ TEST(DeclarationMatcher, IsDerivedFromEmptyName) {
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));
diff --git a/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp b/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp
index cf016a120b1..4e5c0ffe2b4 100644
--- a/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp
+++ b/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp
@@ -439,7 +439,7 @@ TEST_F(RegistryTest, Errors) {
Error.get()).isNull());
EXPECT_EQ("Incorrect type for arg 1. "
"(Expected = Matcher<CXXRecordDecl>) != "
- "(Actual = Matcher<CXXRecordDecl>&Matcher"
+ "(Actual = Matcher<CXXRecordDecl|ObjCInterfaceDecl>&Matcher"
"<MemberExpr|UnresolvedMemberExpr|CXXDependentScopeMemberExpr>)",
Error->toString());
}
OpenPOWER on IntegriCloud