summaryrefslogtreecommitdiff
path: root/include/clang/ASTMatchers
diff options
context:
space:
mode:
Diffstat (limited to 'include/clang/ASTMatchers')
-rw-r--r--include/clang/ASTMatchers/ASTMatchers.h186
-rw-r--r--include/clang/ASTMatchers/ASTMatchersInternal.h25
2 files changed, 158 insertions, 53 deletions
diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h
index 063d8217d9aa..e34b31cbda88 100644
--- a/include/clang/ASTMatchers/ASTMatchers.h
+++ b/include/clang/ASTMatchers/ASTMatchers.h
@@ -19,15 +19,15 @@
//
// For more complicated match expressions we're often interested in accessing
// multiple parts of the matched AST nodes once a match is found. In that case,
-// use the id(...) matcher around the match expressions that match the nodes
-// you want to access.
+// call `.bind("name")` on match expressions that match the nodes you want to
+// access.
//
// For example, when we're interested in child classes of a certain class, we
// would write:
-// cxxRecordDecl(hasName("MyClass"), has(id("child", recordDecl())))
+// cxxRecordDecl(hasName("MyClass"), has(recordDecl().bind("child")))
// When the match is found via the MatchFinder, a user provided callback will
// be called with a BoundNodes instance that contains a mapping from the
-// strings that we provided for the id(...) calls to the nodes that were
+// strings that we provided for the `.bind()` calls to the nodes that were
// matched.
// In the given example, each time our matcher finds a match we get a callback
// where "child" is bound to the RecordDecl node of the matching child
@@ -131,15 +131,6 @@ private:
internal::BoundNodesMap MyBoundNodes;
};
-/// If the provided matcher matches a node, binds the node to \c ID.
-///
-/// FIXME: Do we want to support this now that we have bind()?
-template <typename T>
-internal::Matcher<T> id(StringRef ID,
- const internal::BindableMatcher<T> &InnerMatcher) {
- return InnerMatcher.bind(ID);
-}
-
/// Types of matchers for the top-level classes in the AST class
/// hierarchy.
/// @{
@@ -2611,8 +2602,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,33 +2624,128 @@ 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);
+///
+/// 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) {
- assert(!BaseName.empty());
- return isDerivedFrom(hasName(BaseName)).matches(Node, Finder, Builder);
+AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
+ isDerivedFrom,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl),
+ std::string, BaseName, 1) {
+ if (BaseName.empty())
+ return false;
+
+ 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) {
- assert(!BaseName.empty());
- return isSameOrDerivedFrom(hasName(BaseName)).matches(Node, Finder, Builder);
+AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
+ isSameOrDerivedFrom,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl),
+ std::string, BaseName, 1) {
+ if (BaseName.empty())
+ return false;
+
+ 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++ 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.
+///
+/// Example matches Y, C (Base == hasName("X"))
+/// \code
+/// 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
+/// \endcode
+///
+/// In the following example, Bar matches isDerivedFrom(hasName("X")):
+/// \code
+/// class Foo;
+/// typedef Foo X;
+/// class Bar : public Foo {}; // derived from a type that X is a typedef of
+/// \endcode
+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_POLYMORPHIC_MATCHER_P_OVERLOAD(
+ isDirectlyDerivedFrom,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl),
+ std::string, BaseName, 1) {
+ if (BaseName.empty())
+ return false;
+ 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.
///
@@ -6358,10 +6445,9 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt, CUDAKernelCallExpr>
/// expr(nullPointerConstant())
/// matches the initializer for v1, v2, v3, cp, and ip. Does not match the
/// initializer for i.
-AST_MATCHER_FUNCTION(internal::Matcher<Expr>, nullPointerConstant) {
- return anyOf(
- gnuNullExpr(), cxxNullPtrLiteralExpr(),
- integerLiteral(equals(0), hasParent(expr(hasType(pointerType())))));
+AST_MATCHER(Expr, nullPointerConstant) {
+ return Node.isNullPointerConstant(Finder->getASTContext(),
+ Expr::NPC_ValueDependentIsNull);
}
/// Matches declaration of the function the statement belongs to
@@ -6375,7 +6461,7 @@ AST_MATCHER_FUNCTION(internal::Matcher<Expr>, nullPointerConstant) {
/// \endcode
/// returnStmt(forFunction(hasName("operator=")))
/// matches 'return *this'
-/// but does match 'return > 0'
+/// but does not match 'return v > 0'
AST_MATCHER_P(Stmt, forFunction, internal::Matcher<FunctionDecl>,
InnerMatcher) {
const auto &Parents = Finder->getASTContext().getParents(Node);
@@ -6498,14 +6584,15 @@ AST_MATCHER(FunctionDecl, hasTrailingReturn) {
}
/// Matches expressions that match InnerMatcher that are possibly wrapped in an
-/// elidable constructor.
+/// elidable constructor and other corresponding bookkeeping nodes.
///
-/// In C++17 copy elidable constructors are no longer being
-/// generated in the AST as it is not permitted by the standard. They are
-/// however part of the AST in C++14 and earlier. Therefore, to write a matcher
-/// that works in all language modes, the matcher has to skip elidable
-/// constructor AST nodes if they appear in the AST. This matcher can be used to
-/// skip those elidable constructors.
+/// In C++17, elidable copy constructors are no longer being generated in the
+/// AST as it is not permitted by the standard. They are, however, part of the
+/// AST in C++14 and earlier. So, a matcher must abstract over these differences
+/// to work in all language modes. This matcher skips elidable constructor-call
+/// AST nodes, `ExprWithCleanups` nodes wrapping elidable constructor-calls and
+/// various implicit nodes inside the constructor calls, all of which will not
+/// appear in the C++17 AST.
///
/// Given
///
@@ -6517,13 +6604,20 @@ AST_MATCHER(FunctionDecl, hasTrailingReturn) {
/// }
/// \endcode
///
-/// ``varDecl(hasInitializer(any(
-/// ignoringElidableConstructorCall(callExpr()),
-/// exprWithCleanups(ignoringElidableConstructorCall(callExpr()))))``
-/// matches ``H D = G()``
+/// ``varDecl(hasInitializer(ignoringElidableConstructorCall(callExpr())))``
+/// matches ``H D = G()`` in C++11 through C++17 (and beyond).
AST_MATCHER_P(Expr, ignoringElidableConstructorCall,
ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
- if (const auto *CtorExpr = dyn_cast<CXXConstructExpr>(&Node)) {
+ // E tracks the node that we are examining.
+ const Expr *E = &Node;
+ // If present, remove an outer `ExprWithCleanups` corresponding to the
+ // underlying `CXXConstructExpr`. This check won't cover all cases of added
+ // `ExprWithCleanups` corresponding to `CXXConstructExpr` nodes (because the
+ // EWC is placed on the outermost node of the expression, which this may not
+ // be), but, it still improves the coverage of this matcher.
+ if (const auto *CleanupsExpr = dyn_cast<ExprWithCleanups>(&Node))
+ E = CleanupsExpr->getSubExpr();
+ if (const auto *CtorExpr = dyn_cast<CXXConstructExpr>(E)) {
if (CtorExpr->isElidable()) {
if (const auto *MaterializeTemp =
dyn_cast<MaterializeTemporaryExpr>(CtorExpr->getArg(0))) {
diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h
index b1bb0bfa3218..e9fa920b6bce 100644
--- a/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -183,7 +183,8 @@ public:
/// Note that we're using std::map here, as for memoization:
/// - we need a comparison operator
/// - we need an assignment operator
- using IDToNodeMap = std::map<std::string, ast_type_traits::DynTypedNode>;
+ using IDToNodeMap =
+ std::map<std::string, ast_type_traits::DynTypedNode, std::less<>>;
const IDToNodeMap &getMap() const {
return NodeMap;
@@ -971,13 +972,23 @@ 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 considered to be also derived from itself.
+ /// A class is not considered to be derived from itself.
virtual bool classIsDerivedFrom(const CXXRecordDecl *Declaration,
const Matcher<NamedDecl> &Base,
- BoundNodesTreeBuilder *Builder) = 0;
+ 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,
@@ -1315,7 +1326,7 @@ class ForEachMatcher : public WrapperMatcherInterface<T> {
///
/// Input matchers can have any type (including other polymorphic matcher
/// types), and the actual Matcher<T> is generated on demand with an implicit
-/// coversion operator.
+/// conversion operator.
template <typename... Ps> class VariadicOperatorMatcher {
public:
VariadicOperatorMatcher(DynTypedMatcher::VariadicOperator Op, Ps &&... Params)
@@ -1324,14 +1335,14 @@ public:
template <typename T> operator Matcher<T>() const {
return DynTypedMatcher::constructVariadic(
Op, ast_type_traits::ASTNodeKind::getFromNodeKind<T>(),
- getMatchers<T>(llvm::index_sequence_for<Ps...>()))
+ getMatchers<T>(std::index_sequence_for<Ps...>()))
.template unconditionalConvertTo<T>();
}
private:
// Helper method to unpack the tuple into a vector.
template <typename T, std::size_t... Is>
- std::vector<DynTypedMatcher> getMatchers(llvm::index_sequence<Is...>) const {
+ std::vector<DynTypedMatcher> getMatchers(std::index_sequence<Is...>) const {
return {Matcher<T>(std::get<Is>(Params))...};
}