diff options
Diffstat (limited to 'include/clang/ASTMatchers')
-rw-r--r-- | include/clang/ASTMatchers/ASTMatchers.h | 186 | ||||
-rw-r--r-- | include/clang/ASTMatchers/ASTMatchersInternal.h | 25 |
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))...}; } |