summaryrefslogtreecommitdiff
path: root/include/clang/ASTMatchers/ASTMatchers.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/clang/ASTMatchers/ASTMatchers.h')
-rw-r--r--include/clang/ASTMatchers/ASTMatchers.h186
1 files changed, 140 insertions, 46 deletions
diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h
index 063d8217d9aa5..e34b31cbda880 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))) {