diff options
Diffstat (limited to 'include/clang/ASTMatchers/ASTMatchers.h')
| -rw-r--r-- | include/clang/ASTMatchers/ASTMatchers.h | 429 |
1 files changed, 400 insertions, 29 deletions
diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h index 5cdb964a92c29..063d8217d9aa5 100644 --- a/include/clang/ASTMatchers/ASTMatchers.h +++ b/include/clang/ASTMatchers/ASTMatchers.h @@ -1,9 +1,8 @@ //===- ASTMatchers.h - Structural query framework ---------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -57,10 +56,12 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/OpenMPClause.h" #include "clang/AST/OperationKinds.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" +#include "clang/AST/StmtOpenMP.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/TemplateName.h" #include "clang/AST/Type.h" @@ -1137,6 +1138,17 @@ extern const internal::VariadicDynCastAllOfMatcher<Decl, CXXMethodDecl> extern const internal::VariadicDynCastAllOfMatcher<Decl, CXXConversionDecl> cxxConversionDecl; +/// Matches user-defined and implicitly generated deduction guide. +/// +/// Example matches the deduction guide. +/// \code +/// template<typename T> +/// class X { X(int) }; +/// X(int) -> X<int>; +/// \endcode +extern const internal::VariadicDynCastAllOfMatcher<Decl, CXXDeductionGuideDecl> + cxxDeductionGuideDecl; + /// Matches variable declarations. /// /// Note: this does not match declarations of member variables, which are @@ -2159,6 +2171,10 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt, CompoundLiteralExpr> extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXNullPtrLiteralExpr> cxxNullPtrLiteralExpr; +/// Matches GNU __builtin_choose_expr. +extern const internal::VariadicDynCastAllOfMatcher<Stmt, ChooseExpr> + chooseExpr; + /// Matches GNU __null expression. extern const internal::VariadicDynCastAllOfMatcher<Stmt, GNUNullExpr> gnuNullExpr; @@ -2487,6 +2503,9 @@ AST_MATCHER_P(UnaryExprOrTypeTraitExpr, hasArgumentOfType, /// \endcode /// unaryExprOrTypeTraitExpr(ofKind(UETT_SizeOf)) /// matches \c sizeof(x) +/// +/// If the matcher is use from clang-query, UnaryExprOrTypeTrait parameter +/// should be passed as a quoted string. e.g., ofKind("UETT_SizeOf"). AST_MATCHER_P(UnaryExprOrTypeTraitExpr, ofKind, UnaryExprOrTypeTrait, Kind) { return Node.getKind() == Kind; } @@ -2887,14 +2906,22 @@ AST_MATCHER_P(NamedDecl, hasUnderlyingDecl, internal::Matcher<NamedDecl>, InnerMatcher.matches(*UnderlyingDecl, Finder, Builder); } -/// Matches on the implicit object argument of a member call expression. +/// Matches on the implicit object argument of a member call expression, after +/// stripping off any parentheses or implicit casts. /// -/// Example matches y.x() -/// (matcher = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y")))))) +/// Given /// \code -/// class Y { public: void x(); }; -/// void z() { Y y; y.x(); } +/// class Y { public: void m(); }; +/// Y g(); +/// class X : public Y {}; +/// void z(Y y, X x) { y.m(); (g()).m(); x.m(); } /// \endcode +/// cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y"))))) +/// matches `y.m()` and `(g()).m()`. +/// cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("X"))))) +/// matches `x.m()`. +/// cxxMemberCallExpr(on(callExpr())) +/// matches `(g()).m()`. /// /// FIXME: Overload to allow directly matching types? AST_MATCHER_P(CXXMemberCallExpr, on, internal::Matcher<Expr>, @@ -2922,10 +2949,59 @@ AST_MATCHER_P(ObjCMessageExpr, hasReceiverType, internal::Matcher<QualType>, return InnerMatcher.matches(TypeDecl, Finder, Builder); } +/// Returns true when the Objective-C method declaration is a class method. +/// +/// Example +/// matcher = objcMethodDecl(isClassMethod()) +/// matches +/// \code +/// @interface I + (void)foo; @end +/// \endcode +/// but not +/// \code +/// @interface I - (void)bar; @end +/// \endcode +AST_MATCHER(ObjCMethodDecl, isClassMethod) { + return Node.isClassMethod(); +} + +/// Returns true when the Objective-C method declaration is an instance method. +/// +/// Example +/// matcher = objcMethodDecl(isInstanceMethod()) +/// matches +/// \code +/// @interface I - (void)bar; @end +/// \endcode +/// but not +/// \code +/// @interface I + (void)foo; @end +/// \endcode +AST_MATCHER(ObjCMethodDecl, isInstanceMethod) { + return Node.isInstanceMethod(); +} + +/// Returns true when the Objective-C message is sent to a class. +/// +/// Example +/// matcher = objcMessageExpr(isClassMessage()) +/// matches +/// \code +/// [NSString stringWithFormat:@"format"]; +/// \endcode +/// but not +/// \code +/// NSString *x = @"hello"; +/// [x containsString:@"h"]; +/// \endcode +AST_MATCHER(ObjCMessageExpr, isClassMessage) { + return Node.isClassMessage(); +} + /// Returns true when the Objective-C message is sent to an instance. /// /// Example -/// matcher = objcMessagaeExpr(isInstanceMessage()) +/// matcher = objcMessageExpr(isInstanceMessage()) /// matches /// \code /// NSString *x = @"hello"; @@ -3254,6 +3330,23 @@ AST_MATCHER_P_OVERLOAD(QualType, references, internal::Matcher<Decl>, .matches(Node, Finder, Builder); } +/// Matches on the implicit object argument of a member call expression. Unlike +/// `on`, matches the argument directly without stripping away anything. +/// +/// Given +/// \code +/// class Y { public: void m(); }; +/// Y g(); +/// class X : public Y { void g(); }; +/// void z(Y y, X x) { y.m(); x.m(); x.g(); (g()).m(); } +/// \endcode +/// cxxMemberCallExpr(onImplicitObjectArgument(hasType( +/// cxxRecordDecl(hasName("Y"))))) +/// matches `y.m()`, `x.m()` and (g()).m(), but not `x.g()`. +/// cxxMemberCallExpr(on(callExpr())) +/// does not match `(g()).m()`, because the parens are not ignored. +/// +/// FIXME: Overload to allow directly matching types? AST_MATCHER_P(CXXMemberCallExpr, onImplicitObjectArgument, internal::Matcher<Expr>, InnerMatcher) { const Expr *ExprNode = Node.getImplicitObjectArgument(); @@ -3261,8 +3354,22 @@ AST_MATCHER_P(CXXMemberCallExpr, onImplicitObjectArgument, InnerMatcher.matches(*ExprNode, Finder, Builder)); } -/// Matches if the expression's type either matches the specified -/// matcher, or is a pointer to a type that matches the InnerMatcher. +/// Matches if the type of the expression's implicit object argument either +/// matches the InnerMatcher, or is a pointer to a type that matches the +/// InnerMatcher. +/// +/// Given +/// \code +/// class Y { public: void m(); }; +/// class X : public Y { void g(); }; +/// void z() { Y y; y.m(); Y *p; p->m(); X x; x.m(); x.g(); } +/// \endcode +/// cxxMemberCallExpr(thisPointerType(hasDeclaration( +/// cxxRecordDecl(hasName("Y"))))) +/// matches `y.m()`, `p->m()` and `x.m()`. +/// cxxMemberCallExpr(thisPointerType(hasDeclaration( +/// cxxRecordDecl(hasName("X"))))) +/// matches `x.g()`. AST_MATCHER_P_OVERLOAD(CXXMemberCallExpr, thisPointerType, internal::Matcher<QualType>, InnerMatcher, 0) { return onImplicitObjectArgument( @@ -4439,6 +4546,9 @@ AST_POLYMORPHIC_MATCHER_P(hasSourceExpression, /// \code /// int *p = 0; /// \endcode +/// +/// If the matcher is use from clang-query, CastKind parameter +/// should be passed as a quoted string. e.g., ofKind("CK_NullToPointer"). AST_MATCHER_P(CastExpr, hasCastKind, CastKind, Kind) { return Node.getCastKind() == Kind; } @@ -4964,18 +5074,22 @@ AST_MATCHER_P(MemberExpr, member, return InnerMatcher.matches(*Node.getMemberDecl(), Finder, Builder); } -/// Matches a member expression where the object expression is -/// matched by a given matcher. +/// Matches a member expression where the object expression is matched by a +/// given matcher. Implicit object expressions are included; that is, it matches +/// use of implicit `this`. /// /// Given /// \code -/// struct X { int m; }; -/// void f(X x) { x.m; m; } +/// struct X { +/// int m; +/// int f(X x) { x.m; return m; } +/// }; /// \endcode -/// memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X"))))))) -/// matches "x.m" and "m" -/// with hasObjectExpression(...) -/// matching "x" and the implicit object expression of "m" which has type X*. +/// memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X"))))) +/// matches `x.m`, but not `m`; however, +/// memberExpr(hasObjectExpression(hasType(pointsTo( +// cxxRecordDecl(hasName("X")))))) +/// matches `m` (aka. `this->m`), but not `x.m`. AST_POLYMORPHIC_MATCHER_P( hasObjectExpression, AST_POLYMORPHIC_SUPPORTED_TYPES(MemberExpr, UnresolvedMemberExpr, @@ -6051,26 +6165,63 @@ AST_MATCHER(CXXConstructorDecl, isDelegatingConstructor) { return Node.isDelegatingConstructor(); } -/// Matches constructor and conversion declarations that are marked with -/// the explicit keyword. +/// Matches constructor, conversion function, and deduction guide declarations +/// that have an explicit specifier if this explicit specifier is resolved to +/// true. /// /// Given /// \code +/// template<bool b> /// struct S { /// S(int); // #1 /// explicit S(double); // #2 /// operator int(); // #3 /// explicit operator bool(); // #4 +/// explicit(false) S(bool) // # 7 +/// explicit(true) S(char) // # 8 +/// explicit(b) S(S) // # 9 /// }; +/// S(int) -> S<true> // #5 +/// explicit S(double) -> S<false> // #6 /// \endcode -/// cxxConstructorDecl(isExplicit()) will match #2, but not #1. +/// cxxConstructorDecl(isExplicit()) will match #2 and #8, but not #1, #7 or #9. /// cxxConversionDecl(isExplicit()) will match #4, but not #3. -AST_POLYMORPHIC_MATCHER(isExplicit, - AST_POLYMORPHIC_SUPPORTED_TYPES(CXXConstructorDecl, - CXXConversionDecl)) { +/// cxxDeductionGuideDecl(isExplicit()) will match #6, but not #5. +AST_POLYMORPHIC_MATCHER(isExplicit, AST_POLYMORPHIC_SUPPORTED_TYPES( + CXXConstructorDecl, CXXConversionDecl, + CXXDeductionGuideDecl)) { return Node.isExplicit(); } +/// Matches the expression in an explicit specifier if present in the given +/// declaration. +/// +/// Given +/// \code +/// template<bool b> +/// struct S { +/// S(int); // #1 +/// explicit S(double); // #2 +/// operator int(); // #3 +/// explicit operator bool(); // #4 +/// explicit(false) S(bool) // # 7 +/// explicit(true) S(char) // # 8 +/// explicit(b) S(S) // # 9 +/// }; +/// S(int) -> S<true> // #5 +/// explicit S(double) -> S<false> // #6 +/// \endcode +/// cxxConstructorDecl(hasExplicitSpecifier(constantExpr())) will match #7, #8 and #9, but not #1 or #2. +/// cxxConversionDecl(hasExplicitSpecifier(constantExpr())) will not match #3 or #4. +/// cxxDeductionGuideDecl(hasExplicitSpecifier(constantExpr())) will not match #5 or #6. +AST_MATCHER_P(FunctionDecl, hasExplicitSpecifier, internal::Matcher<Expr>, + InnerMatcher) { + ExplicitSpecifier ES = ExplicitSpecifier::getFromDecl(&Node); + if (!ES.getExpr()) + return false; + return InnerMatcher.matches(*ES.getExpr(), Finder, Builder); +} + /// Matches function and namespace declarations that are marked with /// the inline keyword. /// @@ -6109,6 +6260,29 @@ AST_MATCHER(NamespaceDecl, isAnonymous) { return Node.isAnonymousNamespace(); } +/// Matches declarations in the namespace `std`, but not in nested namespaces. +/// +/// Given +/// \code +/// class vector {}; +/// namespace foo { +/// class vector {}; +/// namespace std { +/// class vector {}; +/// } +/// } +/// namespace std { +/// inline namespace __1 { +/// class vector {}; // #1 +/// namespace experimental { +/// class vector {}; +/// } +/// } +/// } +/// \endcode +/// cxxRecordDecl(hasName("vector"), isInStdNamespace()) will match only #1. +AST_MATCHER(Decl, isInStdNamespace) { return Node.isInStdNamespace(); } + /// If the given case statement does not use the GNU case range /// extension, matches the constant given in the statement. /// @@ -6133,7 +6307,7 @@ AST_MATCHER_P(CaseStmt, hasCaseConstant, internal::Matcher<Expr>, /// __attribute__((device)) void f() { ... } /// \endcode /// decl(hasAttr(clang::attr::CUDADevice)) matches the function declaration of -/// f. If the matcher is use from clang-query, attr::Kind parameter should be +/// f. If the matcher is used from clang-query, attr::Kind parameter should be /// passed as a quoted string. e.g., hasAttr("attr::CUDADevice"). AST_MATCHER_P(Decl, hasAttr, attr::Kind, AttrKind) { for (const auto *Attr : Node.attrs()) { @@ -6284,8 +6458,8 @@ AST_MATCHER(CXXNewExpr, isArray) { /// cxxNewExpr(hasArraySize(intgerLiteral(equals(10)))) /// matches the expression 'new MyClass[10]'. AST_MATCHER_P(CXXNewExpr, hasArraySize, internal::Matcher<Expr>, InnerMatcher) { - return Node.isArray() && - InnerMatcher.matches(*Node.getArraySize(), Finder, Builder); + return Node.isArray() && *Node.getArraySize() && + InnerMatcher.matches(**Node.getArraySize(), Finder, Builder); } /// Matches a class declaration that is defined. @@ -6323,6 +6497,203 @@ AST_MATCHER(FunctionDecl, hasTrailingReturn) { return false; } +/// Matches expressions that match InnerMatcher that are possibly wrapped in an +/// elidable constructor. +/// +/// 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. +/// +/// Given +/// +/// \code +/// struct H {}; +/// H G(); +/// void f() { +/// H D = G(); +/// } +/// \endcode +/// +/// ``varDecl(hasInitializer(any( +/// ignoringElidableConstructorCall(callExpr()), +/// exprWithCleanups(ignoringElidableConstructorCall(callExpr()))))`` +/// matches ``H D = G()`` +AST_MATCHER_P(Expr, ignoringElidableConstructorCall, + ast_matchers::internal::Matcher<Expr>, InnerMatcher) { + if (const auto *CtorExpr = dyn_cast<CXXConstructExpr>(&Node)) { + if (CtorExpr->isElidable()) { + if (const auto *MaterializeTemp = + dyn_cast<MaterializeTemporaryExpr>(CtorExpr->getArg(0))) { + return InnerMatcher.matches(*MaterializeTemp->GetTemporaryExpr(), + Finder, Builder); + } + } + } + return InnerMatcher.matches(Node, Finder, Builder); +} + +//----------------------------------------------------------------------------// +// OpenMP handling. +//----------------------------------------------------------------------------// + +/// Matches any ``#pragma omp`` executable directive. +/// +/// Given +/// +/// \code +/// #pragma omp parallel +/// #pragma omp parallel default(none) +/// #pragma omp taskyield +/// \endcode +/// +/// ``ompExecutableDirective()`` matches ``omp parallel``, +/// ``omp parallel default(none)`` and ``omp taskyield``. +extern const internal::VariadicDynCastAllOfMatcher<Stmt, OMPExecutableDirective> + ompExecutableDirective; + +/// Matches standalone OpenMP directives, +/// i.e., directives that can't have a structured block. +/// +/// Given +/// +/// \code +/// #pragma omp parallel +/// {} +/// #pragma omp taskyield +/// \endcode +/// +/// ``ompExecutableDirective(isStandaloneDirective()))`` matches +/// ``omp taskyield``. +AST_MATCHER(OMPExecutableDirective, isStandaloneDirective) { + return Node.isStandaloneDirective(); +} + +/// Matches the Stmt AST node that is marked as being the structured-block +/// of an OpenMP executable directive. +/// +/// Given +/// +/// \code +/// #pragma omp parallel +/// {} +/// \endcode +/// +/// ``stmt(isOMPStructuredBlock()))`` matches ``{}``. +AST_MATCHER(Stmt, isOMPStructuredBlock) { return Node.isOMPStructuredBlock(); } + +/// Matches the structured-block of the OpenMP executable directive +/// +/// Prerequisite: the executable directive must not be standalone directive. +/// If it is, it will never match. +/// +/// Given +/// +/// \code +/// #pragma omp parallel +/// ; +/// #pragma omp parallel +/// {} +/// \endcode +/// +/// ``ompExecutableDirective(hasStructuredBlock(nullStmt()))`` will match ``;`` +AST_MATCHER_P(OMPExecutableDirective, hasStructuredBlock, + internal::Matcher<Stmt>, InnerMatcher) { + if (Node.isStandaloneDirective()) + return false; // Standalone directives have no structured blocks. + return InnerMatcher.matches(*Node.getStructuredBlock(), Finder, Builder); +} + +/// Matches any clause in an OpenMP directive. +/// +/// Given +/// +/// \code +/// #pragma omp parallel +/// #pragma omp parallel default(none) +/// \endcode +/// +/// ``ompExecutableDirective(hasAnyClause(anything()))`` matches +/// ``omp parallel default(none)``. +AST_MATCHER_P(OMPExecutableDirective, hasAnyClause, + internal::Matcher<OMPClause>, InnerMatcher) { + ArrayRef<OMPClause *> Clauses = Node.clauses(); + return matchesFirstInPointerRange(InnerMatcher, Clauses.begin(), + Clauses.end(), Finder, Builder); +} + +/// Matches OpenMP ``default`` clause. +/// +/// Given +/// +/// \code +/// #pragma omp parallel default(none) +/// #pragma omp parallel default(shared) +/// #pragma omp parallel +/// \endcode +/// +/// ``ompDefaultClause()`` matches ``default(none)`` and ``default(shared)``. +extern const internal::VariadicDynCastAllOfMatcher<OMPClause, OMPDefaultClause> + ompDefaultClause; + +/// Matches if the OpenMP ``default`` clause has ``none`` kind specified. +/// +/// Given +/// +/// \code +/// #pragma omp parallel +/// #pragma omp parallel default(none) +/// #pragma omp parallel default(shared) +/// \endcode +/// +/// ``ompDefaultClause(isNoneKind())`` matches only ``default(none)``. +AST_MATCHER(OMPDefaultClause, isNoneKind) { + return Node.getDefaultKind() == OMPC_DEFAULT_none; +} + +/// Matches if the OpenMP ``default`` clause has ``shared`` kind specified. +/// +/// Given +/// +/// \code +/// #pragma omp parallel +/// #pragma omp parallel default(none) +/// #pragma omp parallel default(shared) +/// \endcode +/// +/// ``ompDefaultClause(isSharedKind())`` matches only ``default(shared)``. +AST_MATCHER(OMPDefaultClause, isSharedKind) { + return Node.getDefaultKind() == OMPC_DEFAULT_shared; +} + +/// Matches if the OpenMP directive is allowed to contain the specified OpenMP +/// clause kind. +/// +/// Given +/// +/// \code +/// #pragma omp parallel +/// #pragma omp parallel for +/// #pragma omp for +/// \endcode +/// +/// `ompExecutableDirective(isAllowedToContainClause(OMPC_default))`` matches +/// ``omp parallel`` and ``omp parallel for``. +/// +/// If the matcher is use from clang-query, ``OpenMPClauseKind`` parameter +/// should be passed as a quoted string. e.g., +/// ``isAllowedToContainClauseKind("OMPC_default").`` +AST_MATCHER_P(OMPExecutableDirective, isAllowedToContainClauseKind, + OpenMPClauseKind, CKind) { + return isAllowedClauseForDirective(Node.getDirectiveKind(), CKind); +} + +//----------------------------------------------------------------------------// +// End OpenMP handling. +//----------------------------------------------------------------------------// + } // namespace ast_matchers } // namespace clang |
