
`hasTemplateArgument` and `templateArgumentCountIs` are always used together. It is more convenient to make then support `FunctionDecl`.
8883 lines
263 KiB
C++
8883 lines
263 KiB
C++
//===- ASTMatchers.h - Structural query framework ---------------*- C++ -*-===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements matchers to be used together with the MatchFinder to
|
|
// match AST nodes.
|
|
//
|
|
// Matchers are created by generator functions, which can be combined in
|
|
// a functional in-language DSL to express queries over the C++ AST.
|
|
//
|
|
// For example, to match a class with a certain name, one would call:
|
|
// cxxRecordDecl(hasName("MyClass"))
|
|
// which returns a matcher that can be used to find all AST nodes that declare
|
|
// a class named 'MyClass'.
|
|
//
|
|
// 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,
|
|
// 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(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 `.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
|
|
// class declaration.
|
|
//
|
|
// See ASTMatchersInternal.h for a more in-depth explanation of the
|
|
// implementation details of the matcher framework.
|
|
//
|
|
// See ASTMatchFinder.h for how to use the generated matchers to run over
|
|
// an AST.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_CLANG_ASTMATCHERS_ASTMATCHERS_H
|
|
#define LLVM_CLANG_ASTMATCHERS_ASTMATCHERS_H
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/ASTTypeTraits.h"
|
|
#include "clang/AST/Attr.h"
|
|
#include "clang/AST/CXXInheritance.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/DeclCXX.h"
|
|
#include "clang/AST/DeclFriend.h"
|
|
#include "clang/AST/DeclObjC.h"
|
|
#include "clang/AST/DeclTemplate.h"
|
|
#include "clang/AST/Expr.h"
|
|
#include "clang/AST/ExprCXX.h"
|
|
#include "clang/AST/ExprObjC.h"
|
|
#include "clang/AST/LambdaCapture.h"
|
|
#include "clang/AST/NestedNameSpecifier.h"
|
|
#include "clang/AST/OpenMPClause.h"
|
|
#include "clang/AST/OperationKinds.h"
|
|
#include "clang/AST/ParentMapContext.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"
|
|
#include "clang/AST/TypeLoc.h"
|
|
#include "clang/ASTMatchers/ASTMatchersInternal.h"
|
|
#include "clang/ASTMatchers/ASTMatchersMacros.h"
|
|
#include "clang/Basic/AttrKinds.h"
|
|
#include "clang/Basic/ExceptionSpecificationType.h"
|
|
#include "clang/Basic/FileManager.h"
|
|
#include "clang/Basic/IdentifierTable.h"
|
|
#include "clang/Basic/LLVM.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "clang/Basic/Specifiers.h"
|
|
#include "clang/Basic/TypeTraits.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/Regex.h"
|
|
#include <cassert>
|
|
#include <cstddef>
|
|
#include <iterator>
|
|
#include <limits>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace clang {
|
|
namespace ast_matchers {
|
|
|
|
/// Maps string IDs to AST nodes matched by parts of a matcher.
|
|
///
|
|
/// The bound nodes are generated by calling \c bind("id") on the node matchers
|
|
/// of the nodes we want to access later.
|
|
///
|
|
/// The instances of BoundNodes are created by \c MatchFinder when the user's
|
|
/// callbacks are executed every time a match is found.
|
|
class BoundNodes {
|
|
public:
|
|
/// Returns the AST node bound to \c ID.
|
|
///
|
|
/// Returns NULL if there was no node bound to \c ID or if there is a node but
|
|
/// it cannot be converted to the specified type.
|
|
template <typename T>
|
|
const T *getNodeAs(StringRef ID) const {
|
|
return MyBoundNodes.getNodeAs<T>(ID);
|
|
}
|
|
|
|
/// Type of mapping from binding identifiers to bound nodes. This type
|
|
/// is an associative container with a key type of \c std::string and a value
|
|
/// type of \c clang::DynTypedNode
|
|
using IDToNodeMap = internal::BoundNodesMap::IDToNodeMap;
|
|
|
|
/// Retrieve mapping from binding identifiers to bound nodes.
|
|
const IDToNodeMap &getMap() const {
|
|
return MyBoundNodes.getMap();
|
|
}
|
|
|
|
private:
|
|
friend class internal::BoundNodesTreeBuilder;
|
|
|
|
/// Create BoundNodes from a pre-filled map of bindings.
|
|
BoundNodes(internal::BoundNodesMap &MyBoundNodes)
|
|
: MyBoundNodes(MyBoundNodes) {}
|
|
|
|
internal::BoundNodesMap MyBoundNodes;
|
|
};
|
|
|
|
/// Types of matchers for the top-level classes in the AST class
|
|
/// hierarchy.
|
|
/// @{
|
|
using DeclarationMatcher = internal::Matcher<Decl>;
|
|
using StatementMatcher = internal::Matcher<Stmt>;
|
|
using TypeMatcher = internal::Matcher<QualType>;
|
|
using TypeLocMatcher = internal::Matcher<TypeLoc>;
|
|
using NestedNameSpecifierMatcher = internal::Matcher<NestedNameSpecifier>;
|
|
using NestedNameSpecifierLocMatcher = internal::Matcher<NestedNameSpecifierLoc>;
|
|
using CXXBaseSpecifierMatcher = internal::Matcher<CXXBaseSpecifier>;
|
|
using CXXCtorInitializerMatcher = internal::Matcher<CXXCtorInitializer>;
|
|
using TemplateArgumentMatcher = internal::Matcher<TemplateArgument>;
|
|
using TemplateArgumentLocMatcher = internal::Matcher<TemplateArgumentLoc>;
|
|
using LambdaCaptureMatcher = internal::Matcher<LambdaCapture>;
|
|
using AttrMatcher = internal::Matcher<Attr>;
|
|
/// @}
|
|
|
|
/// Matches any node.
|
|
///
|
|
/// Useful when another matcher requires a child matcher, but there's no
|
|
/// additional constraint. This will often be used with an explicit conversion
|
|
/// to an \c internal::Matcher<> type such as \c TypeMatcher.
|
|
///
|
|
/// Example: \c DeclarationMatcher(anything()) matches all declarations, e.g.,
|
|
/// \code
|
|
/// "int* p" and "void f()" in
|
|
/// int* p;
|
|
/// void f();
|
|
/// \endcode
|
|
///
|
|
/// Usable as: Any Matcher
|
|
inline internal::TrueMatcher anything() { return internal::TrueMatcher(); }
|
|
|
|
/// Matches the top declaration context.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int X;
|
|
/// namespace NS {
|
|
/// int Y;
|
|
/// } // namespace NS
|
|
/// \endcode
|
|
/// decl(hasDeclContext(translationUnitDecl()))
|
|
/// matches "int X", but not "int Y".
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, TranslationUnitDecl>
|
|
translationUnitDecl;
|
|
|
|
/// Matches typedef declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// typedef int X;
|
|
/// using Y = int;
|
|
/// \endcode
|
|
/// typedefDecl()
|
|
/// matches "typedef int X", but not "using Y = int"
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, TypedefDecl>
|
|
typedefDecl;
|
|
|
|
/// Matches typedef name declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// typedef int X;
|
|
/// using Y = int;
|
|
/// \endcode
|
|
/// typedefNameDecl()
|
|
/// matches "typedef int X" and "using Y = int"
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, TypedefNameDecl>
|
|
typedefNameDecl;
|
|
|
|
/// Matches type alias declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// typedef int X;
|
|
/// using Y = int;
|
|
/// \endcode
|
|
/// typeAliasDecl()
|
|
/// matches "using Y = int", but not "typedef int X"
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, TypeAliasDecl>
|
|
typeAliasDecl;
|
|
|
|
/// Matches type alias template declarations.
|
|
///
|
|
/// typeAliasTemplateDecl() matches
|
|
/// \code
|
|
/// template <typename T>
|
|
/// using Y = X<T>;
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, TypeAliasTemplateDecl>
|
|
typeAliasTemplateDecl;
|
|
|
|
/// Matches AST nodes that were expanded within the main-file.
|
|
///
|
|
/// Example matches X but not Y
|
|
/// (matcher = cxxRecordDecl(isExpansionInMainFile())
|
|
/// \code
|
|
/// #include <Y.h>
|
|
/// class X {};
|
|
/// \endcode
|
|
/// Y.h:
|
|
/// \code
|
|
/// class Y {};
|
|
/// \endcode
|
|
///
|
|
/// Usable as: Matcher<Decl>, Matcher<Stmt>, Matcher<TypeLoc>
|
|
AST_POLYMORPHIC_MATCHER(isExpansionInMainFile,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc)) {
|
|
auto &SourceManager = Finder->getASTContext().getSourceManager();
|
|
return SourceManager.isInMainFile(
|
|
SourceManager.getExpansionLoc(Node.getBeginLoc()));
|
|
}
|
|
|
|
/// Matches AST nodes that were expanded within system-header-files.
|
|
///
|
|
/// Example matches Y but not X
|
|
/// (matcher = cxxRecordDecl(isExpansionInSystemHeader())
|
|
/// \code
|
|
/// #include <SystemHeader.h>
|
|
/// class X {};
|
|
/// \endcode
|
|
/// SystemHeader.h:
|
|
/// \code
|
|
/// class Y {};
|
|
/// \endcode
|
|
///
|
|
/// Usable as: Matcher<Decl>, Matcher<Stmt>, Matcher<TypeLoc>
|
|
AST_POLYMORPHIC_MATCHER(isExpansionInSystemHeader,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc)) {
|
|
auto &SourceManager = Finder->getASTContext().getSourceManager();
|
|
auto ExpansionLoc = SourceManager.getExpansionLoc(Node.getBeginLoc());
|
|
if (ExpansionLoc.isInvalid()) {
|
|
return false;
|
|
}
|
|
return SourceManager.isInSystemHeader(ExpansionLoc);
|
|
}
|
|
|
|
/// Matches AST nodes that were expanded within files whose name is
|
|
/// partially matching a given regex.
|
|
///
|
|
/// Example matches Y but not X
|
|
/// (matcher = cxxRecordDecl(isExpansionInFileMatching("AST.*"))
|
|
/// \code
|
|
/// #include "ASTMatcher.h"
|
|
/// class X {};
|
|
/// \endcode
|
|
/// ASTMatcher.h:
|
|
/// \code
|
|
/// class Y {};
|
|
/// \endcode
|
|
///
|
|
/// Usable as: Matcher<Decl>, Matcher<Stmt>, Matcher<TypeLoc>
|
|
AST_POLYMORPHIC_MATCHER_REGEX(isExpansionInFileMatching,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt,
|
|
TypeLoc),
|
|
RegExp) {
|
|
auto &SourceManager = Finder->getASTContext().getSourceManager();
|
|
auto ExpansionLoc = SourceManager.getExpansionLoc(Node.getBeginLoc());
|
|
if (ExpansionLoc.isInvalid()) {
|
|
return false;
|
|
}
|
|
auto FileEntry =
|
|
SourceManager.getFileEntryRefForID(SourceManager.getFileID(ExpansionLoc));
|
|
if (!FileEntry) {
|
|
return false;
|
|
}
|
|
|
|
auto Filename = FileEntry->getName();
|
|
return RegExp->match(Filename);
|
|
}
|
|
|
|
/// Matches statements that are (transitively) expanded from the named macro.
|
|
/// Does not match if only part of the statement is expanded from that macro or
|
|
/// if different parts of the statement are expanded from different
|
|
/// appearances of the macro.
|
|
AST_POLYMORPHIC_MATCHER_P(isExpandedFromMacro,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc),
|
|
std::string, MacroName) {
|
|
// Verifies that the statement' beginning and ending are both expanded from
|
|
// the same instance of the given macro.
|
|
auto& Context = Finder->getASTContext();
|
|
std::optional<SourceLocation> B =
|
|
internal::getExpansionLocOfMacro(MacroName, Node.getBeginLoc(), Context);
|
|
if (!B) return false;
|
|
std::optional<SourceLocation> E =
|
|
internal::getExpansionLocOfMacro(MacroName, Node.getEndLoc(), Context);
|
|
if (!E) return false;
|
|
return *B == *E;
|
|
}
|
|
|
|
/// Matches declarations.
|
|
///
|
|
/// Examples matches \c X, \c C, and the friend declaration inside \c C;
|
|
/// \code
|
|
/// void X();
|
|
/// class C {
|
|
/// friend X;
|
|
/// };
|
|
/// \endcode
|
|
extern const internal::VariadicAllOfMatcher<Decl> decl;
|
|
|
|
/// Matches decomposition-declarations.
|
|
///
|
|
/// Examples matches the declaration node with \c foo and \c bar, but not
|
|
/// \c number.
|
|
/// (matcher = declStmt(has(decompositionDecl())))
|
|
///
|
|
/// \code
|
|
/// int number = 42;
|
|
/// auto [foo, bar] = std::make_pair{42, 42};
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, DecompositionDecl>
|
|
decompositionDecl;
|
|
|
|
/// Matches binding declarations
|
|
/// Example matches \c foo and \c bar
|
|
/// (matcher = bindingDecl()
|
|
///
|
|
/// \code
|
|
/// auto [foo, bar] = std::make_pair{42, 42};
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, BindingDecl>
|
|
bindingDecl;
|
|
|
|
/// Matches a declaration of a linkage specification.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// extern "C" {}
|
|
/// \endcode
|
|
/// linkageSpecDecl()
|
|
/// matches "extern "C" {}"
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, LinkageSpecDecl>
|
|
linkageSpecDecl;
|
|
|
|
/// Matches a declaration of anything that could have a name.
|
|
///
|
|
/// Example matches \c X, \c S, the anonymous union type, \c i, and \c U;
|
|
/// \code
|
|
/// typedef int X;
|
|
/// struct S {
|
|
/// union {
|
|
/// int i;
|
|
/// } U;
|
|
/// };
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, NamedDecl> namedDecl;
|
|
|
|
/// Matches a declaration of label.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// goto FOO;
|
|
/// FOO: bar();
|
|
/// \endcode
|
|
/// labelDecl()
|
|
/// matches 'FOO:'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, LabelDecl> labelDecl;
|
|
|
|
/// Matches a declaration of a namespace.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace {}
|
|
/// namespace test {}
|
|
/// \endcode
|
|
/// namespaceDecl()
|
|
/// matches "namespace {}" and "namespace test {}"
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, NamespaceDecl>
|
|
namespaceDecl;
|
|
|
|
/// Matches a declaration of a namespace alias.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace test {}
|
|
/// namespace alias = ::test;
|
|
/// \endcode
|
|
/// namespaceAliasDecl()
|
|
/// matches "namespace alias" but not "namespace test"
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, NamespaceAliasDecl>
|
|
namespaceAliasDecl;
|
|
|
|
/// Matches class, struct, and union declarations.
|
|
///
|
|
/// Example matches \c X, \c Z, \c U, and \c S
|
|
/// \code
|
|
/// class X;
|
|
/// template<class T> class Z {};
|
|
/// struct S {};
|
|
/// union U {};
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, RecordDecl> recordDecl;
|
|
|
|
/// Matches C++ class declarations.
|
|
///
|
|
/// Example matches \c X, \c Z
|
|
/// \code
|
|
/// class X;
|
|
/// template<class T> class Z {};
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, CXXRecordDecl>
|
|
cxxRecordDecl;
|
|
|
|
/// Matches C++ class template declarations.
|
|
///
|
|
/// Example matches \c Z
|
|
/// \code
|
|
/// template<class T> class Z {};
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, ClassTemplateDecl>
|
|
classTemplateDecl;
|
|
|
|
/// Matches C++ class template specializations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<typename T> class A {};
|
|
/// template<> class A<double> {};
|
|
/// A<int> a;
|
|
/// \endcode
|
|
/// classTemplateSpecializationDecl()
|
|
/// matches the specializations \c A<int> and \c A<double>
|
|
extern const internal::VariadicDynCastAllOfMatcher<
|
|
Decl, ClassTemplateSpecializationDecl>
|
|
classTemplateSpecializationDecl;
|
|
|
|
/// Matches C++ class template partial specializations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<class T1, class T2, int I>
|
|
/// class A {};
|
|
///
|
|
/// template<class T, int I>
|
|
/// class A<T, T*, I> {};
|
|
///
|
|
/// template<>
|
|
/// class A<int, int, 1> {};
|
|
/// \endcode
|
|
/// classTemplatePartialSpecializationDecl()
|
|
/// matches the specialization \c A<T,T*,I> but not \c A<int,int,1>
|
|
extern const internal::VariadicDynCastAllOfMatcher<
|
|
Decl, ClassTemplatePartialSpecializationDecl>
|
|
classTemplatePartialSpecializationDecl;
|
|
|
|
/// Matches declarator declarations (field, variable, function
|
|
/// and non-type template parameter declarations).
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class X { int y; };
|
|
/// \endcode
|
|
/// declaratorDecl()
|
|
/// matches \c int y.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, DeclaratorDecl>
|
|
declaratorDecl;
|
|
|
|
/// Matches parameter variable declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void f(int x);
|
|
/// \endcode
|
|
/// parmVarDecl()
|
|
/// matches \c int x.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, ParmVarDecl>
|
|
parmVarDecl;
|
|
|
|
/// Matches C++ access specifier declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class C {
|
|
/// public:
|
|
/// int a;
|
|
/// };
|
|
/// \endcode
|
|
/// accessSpecDecl()
|
|
/// matches 'public:'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, AccessSpecDecl>
|
|
accessSpecDecl;
|
|
|
|
/// Matches class bases.
|
|
///
|
|
/// Examples matches \c public virtual B.
|
|
/// \code
|
|
/// class B {};
|
|
/// class C : public virtual B {};
|
|
/// \endcode
|
|
extern const internal::VariadicAllOfMatcher<CXXBaseSpecifier> cxxBaseSpecifier;
|
|
|
|
/// Matches constructor initializers.
|
|
///
|
|
/// Examples matches \c i(42).
|
|
/// \code
|
|
/// class C {
|
|
/// C() : i(42) {}
|
|
/// int i;
|
|
/// };
|
|
/// \endcode
|
|
extern const internal::VariadicAllOfMatcher<CXXCtorInitializer>
|
|
cxxCtorInitializer;
|
|
|
|
/// Matches template arguments.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T> struct C {};
|
|
/// C<int> c;
|
|
/// \endcode
|
|
/// templateArgument()
|
|
/// matches 'int' in C<int>.
|
|
extern const internal::VariadicAllOfMatcher<TemplateArgument> templateArgument;
|
|
|
|
/// Matches template arguments (with location info).
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T> struct C {};
|
|
/// C<int> c;
|
|
/// \endcode
|
|
/// templateArgumentLoc()
|
|
/// matches 'int' in C<int>.
|
|
extern const internal::VariadicAllOfMatcher<TemplateArgumentLoc>
|
|
templateArgumentLoc;
|
|
|
|
/// Matches template name.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T> class X { };
|
|
/// X<int> xi;
|
|
/// \endcode
|
|
/// templateName()
|
|
/// matches 'X' in X<int>.
|
|
extern const internal::VariadicAllOfMatcher<TemplateName> templateName;
|
|
|
|
/// Matches non-type template parameter declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T, int N> struct C {};
|
|
/// \endcode
|
|
/// nonTypeTemplateParmDecl()
|
|
/// matches 'N', but not 'T'.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl,
|
|
NonTypeTemplateParmDecl>
|
|
nonTypeTemplateParmDecl;
|
|
|
|
/// Matches template type parameter declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T, int N> struct C {};
|
|
/// \endcode
|
|
/// templateTypeParmDecl()
|
|
/// matches 'T', but not 'N'.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, TemplateTypeParmDecl>
|
|
templateTypeParmDecl;
|
|
|
|
/// Matches template template parameter declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <template <typename> class Z, int N> struct C {};
|
|
/// \endcode
|
|
/// templateTypeParmDecl()
|
|
/// matches 'Z', but not 'N'.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl,
|
|
TemplateTemplateParmDecl>
|
|
templateTemplateParmDecl;
|
|
|
|
/// Matches public C++ declarations and C++ base specifers that specify public
|
|
/// inheritance.
|
|
///
|
|
/// Examples:
|
|
/// \code
|
|
/// class C {
|
|
/// public: int a; // fieldDecl(isPublic()) matches 'a'
|
|
/// protected: int b;
|
|
/// private: int c;
|
|
/// };
|
|
/// \endcode
|
|
///
|
|
/// \code
|
|
/// class Base {};
|
|
/// class Derived1 : public Base {}; // matches 'Base'
|
|
/// struct Derived2 : Base {}; // matches 'Base'
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER(isPublic,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl,
|
|
CXXBaseSpecifier)) {
|
|
return getAccessSpecifier(Node) == AS_public;
|
|
}
|
|
|
|
/// Matches protected C++ declarations and C++ base specifers that specify
|
|
/// protected inheritance.
|
|
///
|
|
/// Examples:
|
|
/// \code
|
|
/// class C {
|
|
/// public: int a;
|
|
/// protected: int b; // fieldDecl(isProtected()) matches 'b'
|
|
/// private: int c;
|
|
/// };
|
|
/// \endcode
|
|
///
|
|
/// \code
|
|
/// class Base {};
|
|
/// class Derived : protected Base {}; // matches 'Base'
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER(isProtected,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl,
|
|
CXXBaseSpecifier)) {
|
|
return getAccessSpecifier(Node) == AS_protected;
|
|
}
|
|
|
|
/// Matches private C++ declarations and C++ base specifers that specify private
|
|
/// inheritance.
|
|
///
|
|
/// Examples:
|
|
/// \code
|
|
/// class C {
|
|
/// public: int a;
|
|
/// protected: int b;
|
|
/// private: int c; // fieldDecl(isPrivate()) matches 'c'
|
|
/// };
|
|
/// \endcode
|
|
///
|
|
/// \code
|
|
/// struct Base {};
|
|
/// struct Derived1 : private Base {}; // matches 'Base'
|
|
/// class Derived2 : Base {}; // matches 'Base'
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER(isPrivate,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl,
|
|
CXXBaseSpecifier)) {
|
|
return getAccessSpecifier(Node) == AS_private;
|
|
}
|
|
|
|
/// Matches non-static data members that are bit-fields.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class C {
|
|
/// int a : 2;
|
|
/// int b;
|
|
/// };
|
|
/// \endcode
|
|
/// fieldDecl(isBitField())
|
|
/// matches 'int a;' but not 'int b;'.
|
|
AST_MATCHER(FieldDecl, isBitField) {
|
|
return Node.isBitField();
|
|
}
|
|
|
|
/// Matches non-static data members that are bit-fields of the specified
|
|
/// bit width.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class C {
|
|
/// int a : 2;
|
|
/// int b : 4;
|
|
/// int c : 2;
|
|
/// };
|
|
/// \endcode
|
|
/// fieldDecl(hasBitWidth(2))
|
|
/// matches 'int a;' and 'int c;' but not 'int b;'.
|
|
AST_MATCHER_P(FieldDecl, hasBitWidth, unsigned, Width) {
|
|
return Node.isBitField() && Node.getBitWidthValue() == Width;
|
|
}
|
|
|
|
/// Matches non-static data members that have an in-class initializer.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class C {
|
|
/// int a = 2;
|
|
/// int b = 3;
|
|
/// int c;
|
|
/// };
|
|
/// \endcode
|
|
/// fieldDecl(hasInClassInitializer(integerLiteral(equals(2))))
|
|
/// matches 'int a;' but not 'int b;'.
|
|
/// fieldDecl(hasInClassInitializer(anything()))
|
|
/// matches 'int a;' and 'int b;' but not 'int c;'.
|
|
AST_MATCHER_P(FieldDecl, hasInClassInitializer, internal::Matcher<Expr>,
|
|
InnerMatcher) {
|
|
const Expr *Initializer = Node.getInClassInitializer();
|
|
return (Initializer != nullptr &&
|
|
InnerMatcher.matches(*Initializer, Finder, Builder));
|
|
}
|
|
|
|
/// Determines whether the function is "main", which is the entry point
|
|
/// into an executable program.
|
|
AST_MATCHER(FunctionDecl, isMain) {
|
|
return Node.isMain();
|
|
}
|
|
|
|
/// Matches the specialized template of a specialization declaration.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<typename T> class A {}; #1
|
|
/// template<> class A<int> {}; #2
|
|
/// \endcode
|
|
/// classTemplateSpecializationDecl(hasSpecializedTemplate(classTemplateDecl()))
|
|
/// matches '#2' with classTemplateDecl() matching the class template
|
|
/// declaration of 'A' at #1.
|
|
AST_MATCHER_P(ClassTemplateSpecializationDecl, hasSpecializedTemplate,
|
|
internal::Matcher<ClassTemplateDecl>, InnerMatcher) {
|
|
const ClassTemplateDecl* Decl = Node.getSpecializedTemplate();
|
|
return (Decl != nullptr &&
|
|
InnerMatcher.matches(*Decl, Finder, Builder));
|
|
}
|
|
|
|
/// Matches an entity that has been implicitly added by the compiler (e.g.
|
|
/// implicit default/copy constructors).
|
|
AST_POLYMORPHIC_MATCHER(isImplicit,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Attr,
|
|
LambdaCapture)) {
|
|
return Node.isImplicit();
|
|
}
|
|
|
|
/// Matches templateSpecializationTypes, class template specializations,
|
|
/// variable template specializations, and function template specializations
|
|
/// that have at least one TemplateArgument matching the given InnerMatcher.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<typename T> class A {};
|
|
/// template<> class A<double> {};
|
|
/// A<int> a;
|
|
///
|
|
/// template<typename T> f() {};
|
|
/// void func() { f<int>(); };
|
|
/// \endcode
|
|
///
|
|
/// \endcode
|
|
/// classTemplateSpecializationDecl(hasAnyTemplateArgument(
|
|
/// refersToType(asString("int"))))
|
|
/// matches the specialization \c A<int>
|
|
///
|
|
/// functionDecl(hasAnyTemplateArgument(refersToType(asString("int"))))
|
|
/// matches the specialization \c f<int>
|
|
AST_POLYMORPHIC_MATCHER_P(
|
|
hasAnyTemplateArgument,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
|
|
VarTemplateSpecializationDecl, FunctionDecl,
|
|
TemplateSpecializationType),
|
|
internal::Matcher<TemplateArgument>, InnerMatcher) {
|
|
ArrayRef<TemplateArgument> List =
|
|
internal::getTemplateSpecializationArgs(Node);
|
|
return matchesFirstInRange(InnerMatcher, List.begin(), List.end(), Finder,
|
|
Builder) != List.end();
|
|
}
|
|
|
|
/// Causes all nested matchers to be matched with the specified traversal kind.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void foo()
|
|
/// {
|
|
/// int i = 3.0;
|
|
/// }
|
|
/// \endcode
|
|
/// The matcher
|
|
/// \code
|
|
/// traverse(TK_IgnoreUnlessSpelledInSource,
|
|
/// varDecl(hasInitializer(floatLiteral().bind("init")))
|
|
/// )
|
|
/// \endcode
|
|
/// matches the variable declaration with "init" bound to the "3.0".
|
|
template <typename T>
|
|
internal::Matcher<T> traverse(TraversalKind TK,
|
|
const internal::Matcher<T> &InnerMatcher) {
|
|
return internal::DynTypedMatcher::constructRestrictedWrapper(
|
|
new internal::TraversalMatcher<T>(TK, InnerMatcher),
|
|
InnerMatcher.getID().first)
|
|
.template unconditionalConvertTo<T>();
|
|
}
|
|
|
|
template <typename T>
|
|
internal::BindableMatcher<T>
|
|
traverse(TraversalKind TK, const internal::BindableMatcher<T> &InnerMatcher) {
|
|
return internal::BindableMatcher<T>(
|
|
internal::DynTypedMatcher::constructRestrictedWrapper(
|
|
new internal::TraversalMatcher<T>(TK, InnerMatcher),
|
|
InnerMatcher.getID().first)
|
|
.template unconditionalConvertTo<T>());
|
|
}
|
|
|
|
template <typename... T>
|
|
internal::TraversalWrapper<internal::VariadicOperatorMatcher<T...>>
|
|
traverse(TraversalKind TK,
|
|
const internal::VariadicOperatorMatcher<T...> &InnerMatcher) {
|
|
return internal::TraversalWrapper<internal::VariadicOperatorMatcher<T...>>(
|
|
TK, InnerMatcher);
|
|
}
|
|
|
|
template <template <typename ToArg, typename FromArg> class ArgumentAdapterT,
|
|
typename T, typename ToTypes>
|
|
internal::TraversalWrapper<
|
|
internal::ArgumentAdaptingMatcherFuncAdaptor<ArgumentAdapterT, T, ToTypes>>
|
|
traverse(TraversalKind TK, const internal::ArgumentAdaptingMatcherFuncAdaptor<
|
|
ArgumentAdapterT, T, ToTypes> &InnerMatcher) {
|
|
return internal::TraversalWrapper<
|
|
internal::ArgumentAdaptingMatcherFuncAdaptor<ArgumentAdapterT, T,
|
|
ToTypes>>(TK, InnerMatcher);
|
|
}
|
|
|
|
template <template <typename T, typename... P> class MatcherT, typename... P,
|
|
typename ReturnTypesF>
|
|
internal::TraversalWrapper<
|
|
internal::PolymorphicMatcher<MatcherT, ReturnTypesF, P...>>
|
|
traverse(TraversalKind TK,
|
|
const internal::PolymorphicMatcher<MatcherT, ReturnTypesF, P...>
|
|
&InnerMatcher) {
|
|
return internal::TraversalWrapper<
|
|
internal::PolymorphicMatcher<MatcherT, ReturnTypesF, P...>>(TK,
|
|
InnerMatcher);
|
|
}
|
|
|
|
template <typename... T>
|
|
internal::Matcher<typename internal::GetClade<T...>::Type>
|
|
traverse(TraversalKind TK, const internal::MapAnyOfHelper<T...> &InnerMatcher) {
|
|
return traverse(TK, InnerMatcher.with());
|
|
}
|
|
|
|
/// Matches expressions that match InnerMatcher after any implicit AST
|
|
/// nodes are stripped off.
|
|
///
|
|
/// Parentheses and explicit casts are not discarded.
|
|
/// Given
|
|
/// \code
|
|
/// class C {};
|
|
/// C a = C();
|
|
/// C b;
|
|
/// C c = b;
|
|
/// \endcode
|
|
/// The matchers
|
|
/// \code
|
|
/// varDecl(hasInitializer(ignoringImplicit(cxxConstructExpr())))
|
|
/// \endcode
|
|
/// would match the declarations for a, b, and c.
|
|
/// While
|
|
/// \code
|
|
/// varDecl(hasInitializer(cxxConstructExpr()))
|
|
/// \endcode
|
|
/// only match the declarations for b and c.
|
|
AST_MATCHER_P(Expr, ignoringImplicit, internal::Matcher<Expr>,
|
|
InnerMatcher) {
|
|
return InnerMatcher.matches(*Node.IgnoreImplicit(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches expressions that match InnerMatcher after any implicit casts
|
|
/// are stripped off.
|
|
///
|
|
/// Parentheses and explicit casts are not discarded.
|
|
/// Given
|
|
/// \code
|
|
/// int arr[5];
|
|
/// int a = 0;
|
|
/// char b = 0;
|
|
/// const int c = a;
|
|
/// int *d = arr;
|
|
/// long e = (long) 0l;
|
|
/// \endcode
|
|
/// The matchers
|
|
/// \code
|
|
/// varDecl(hasInitializer(ignoringImpCasts(integerLiteral())))
|
|
/// varDecl(hasInitializer(ignoringImpCasts(declRefExpr())))
|
|
/// \endcode
|
|
/// would match the declarations for a, b, c, and d, but not e.
|
|
/// While
|
|
/// \code
|
|
/// varDecl(hasInitializer(integerLiteral()))
|
|
/// varDecl(hasInitializer(declRefExpr()))
|
|
/// \endcode
|
|
/// only match the declarations for a.
|
|
AST_MATCHER_P(Expr, ignoringImpCasts,
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
return InnerMatcher.matches(*Node.IgnoreImpCasts(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches expressions that match InnerMatcher after parentheses and
|
|
/// casts are stripped off.
|
|
///
|
|
/// Implicit and non-C Style casts are also discarded.
|
|
/// Given
|
|
/// \code
|
|
/// int a = 0;
|
|
/// char b = (0);
|
|
/// void* c = reinterpret_cast<char*>(0);
|
|
/// char d = char(0);
|
|
/// \endcode
|
|
/// The matcher
|
|
/// varDecl(hasInitializer(ignoringParenCasts(integerLiteral())))
|
|
/// would match the declarations for a, b, c, and d.
|
|
/// while
|
|
/// varDecl(hasInitializer(integerLiteral()))
|
|
/// only match the declaration for a.
|
|
AST_MATCHER_P(Expr, ignoringParenCasts, internal::Matcher<Expr>, InnerMatcher) {
|
|
return InnerMatcher.matches(*Node.IgnoreParenCasts(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches expressions that match InnerMatcher after implicit casts and
|
|
/// parentheses are stripped off.
|
|
///
|
|
/// Explicit casts are not discarded.
|
|
/// Given
|
|
/// \code
|
|
/// int arr[5];
|
|
/// int a = 0;
|
|
/// char b = (0);
|
|
/// const int c = a;
|
|
/// int *d = (arr);
|
|
/// long e = ((long) 0l);
|
|
/// \endcode
|
|
/// The matchers
|
|
/// varDecl(hasInitializer(ignoringParenImpCasts(integerLiteral())))
|
|
/// varDecl(hasInitializer(ignoringParenImpCasts(declRefExpr())))
|
|
/// would match the declarations for a, b, c, and d, but not e.
|
|
/// while
|
|
/// varDecl(hasInitializer(integerLiteral()))
|
|
/// varDecl(hasInitializer(declRefExpr()))
|
|
/// would only match the declaration for a.
|
|
AST_MATCHER_P(Expr, ignoringParenImpCasts,
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
return InnerMatcher.matches(*Node.IgnoreParenImpCasts(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches types that match InnerMatcher after any parens are stripped.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void (*fp)(void);
|
|
/// \endcode
|
|
/// The matcher
|
|
/// \code
|
|
/// varDecl(hasType(pointerType(pointee(ignoringParens(functionType())))))
|
|
/// \endcode
|
|
/// would match the declaration for fp.
|
|
AST_MATCHER_P_OVERLOAD(QualType, ignoringParens, internal::Matcher<QualType>,
|
|
InnerMatcher, 0) {
|
|
return InnerMatcher.matches(Node.IgnoreParens(), Finder, Builder);
|
|
}
|
|
|
|
/// Overload \c ignoringParens for \c Expr.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// const char* str = ("my-string");
|
|
/// \endcode
|
|
/// The matcher
|
|
/// \code
|
|
/// implicitCastExpr(hasSourceExpression(ignoringParens(stringLiteral())))
|
|
/// \endcode
|
|
/// would match the implicit cast resulting from the assignment.
|
|
AST_MATCHER_P_OVERLOAD(Expr, ignoringParens, internal::Matcher<Expr>,
|
|
InnerMatcher, 1) {
|
|
const Expr *E = Node.IgnoreParens();
|
|
return InnerMatcher.matches(*E, Finder, Builder);
|
|
}
|
|
|
|
/// Matches expressions that are instantiation-dependent even if it is
|
|
/// neither type- nor value-dependent.
|
|
///
|
|
/// In the following example, the expression sizeof(sizeof(T() + T()))
|
|
/// is instantiation-dependent (since it involves a template parameter T),
|
|
/// but is neither type- nor value-dependent, since the type of the inner
|
|
/// sizeof is known (std::size_t) and therefore the size of the outer
|
|
/// sizeof is known.
|
|
/// \code
|
|
/// template<typename T>
|
|
/// void f(T x, T y) { sizeof(sizeof(T() + T()); }
|
|
/// \endcode
|
|
/// expr(isInstantiationDependent()) matches sizeof(sizeof(T() + T())
|
|
AST_MATCHER(Expr, isInstantiationDependent) {
|
|
return Node.isInstantiationDependent();
|
|
}
|
|
|
|
/// Matches expressions that are type-dependent because the template type
|
|
/// is not yet instantiated.
|
|
///
|
|
/// For example, the expressions "x" and "x + y" are type-dependent in
|
|
/// the following code, but "y" is not type-dependent:
|
|
/// \code
|
|
/// template<typename T>
|
|
/// void add(T x, int y) {
|
|
/// x + y;
|
|
/// }
|
|
/// \endcode
|
|
/// expr(isTypeDependent()) matches x + y
|
|
AST_MATCHER(Expr, isTypeDependent) { return Node.isTypeDependent(); }
|
|
|
|
/// Matches expression that are value-dependent because they contain a
|
|
/// non-type template parameter.
|
|
///
|
|
/// For example, the array bound of "Chars" in the following example is
|
|
/// value-dependent.
|
|
/// \code
|
|
/// template<int Size> int f() { return Size; }
|
|
/// \endcode
|
|
/// expr(isValueDependent()) matches return Size
|
|
AST_MATCHER(Expr, isValueDependent) { return Node.isValueDependent(); }
|
|
|
|
/// Matches templateSpecializationType, class template specializations,
|
|
/// variable template specializations, and function template specializations
|
|
/// where the n'th TemplateArgument matches the given InnerMatcher.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<typename T, typename U> class A {};
|
|
/// A<bool, int> b;
|
|
/// A<int, bool> c;
|
|
///
|
|
/// template<typename T> void f() {}
|
|
/// void func() { f<int>(); };
|
|
/// \endcode
|
|
/// classTemplateSpecializationDecl(hasTemplateArgument(
|
|
/// 1, refersToType(asString("int"))))
|
|
/// matches the specialization \c A<bool, int>
|
|
///
|
|
/// functionDecl(hasTemplateArgument(0, refersToType(asString("int"))))
|
|
/// matches the specialization \c f<int>
|
|
AST_POLYMORPHIC_MATCHER_P2(
|
|
hasTemplateArgument,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
|
|
VarTemplateSpecializationDecl, FunctionDecl,
|
|
TemplateSpecializationType),
|
|
unsigned, N, internal::Matcher<TemplateArgument>, InnerMatcher) {
|
|
ArrayRef<TemplateArgument> List =
|
|
internal::getTemplateSpecializationArgs(Node);
|
|
if (List.size() <= N)
|
|
return false;
|
|
return InnerMatcher.matches(List[N], Finder, Builder);
|
|
}
|
|
|
|
/// Matches if the number of template arguments equals \p N.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<typename T> struct C {};
|
|
/// C<int> c;
|
|
/// template<typename T> void f() {}
|
|
/// void func() { f<int>(); };
|
|
/// \endcode
|
|
///
|
|
/// classTemplateSpecializationDecl(templateArgumentCountIs(1))
|
|
/// matches C<int>.
|
|
///
|
|
/// functionDecl(templateArgumentCountIs(1))
|
|
/// matches f<int>();
|
|
AST_POLYMORPHIC_MATCHER_P(
|
|
templateArgumentCountIs,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
|
|
VarTemplateSpecializationDecl, FunctionDecl,
|
|
TemplateSpecializationType),
|
|
unsigned, N) {
|
|
return internal::getTemplateSpecializationArgs(Node).size() == N;
|
|
}
|
|
|
|
/// Matches a TemplateArgument that refers to a certain type.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct X {};
|
|
/// template<typename T> struct A {};
|
|
/// A<X> a;
|
|
/// \endcode
|
|
/// classTemplateSpecializationDecl(hasAnyTemplateArgument(refersToType(
|
|
/// recordType(hasDeclaration(recordDecl(hasName("X")))))))
|
|
/// matches the specialization of \c struct A generated by \c A<X>.
|
|
AST_MATCHER_P(TemplateArgument, refersToType,
|
|
internal::Matcher<QualType>, InnerMatcher) {
|
|
if (Node.getKind() != TemplateArgument::Type)
|
|
return false;
|
|
return InnerMatcher.matches(Node.getAsType(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches a TemplateArgument that refers to a certain template.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<template <typename> class S> class X {};
|
|
/// template<typename T> class Y {};
|
|
/// X<Y> xi;
|
|
/// \endcode
|
|
/// classTemplateSpecializationDecl(hasAnyTemplateArgument(
|
|
/// refersToTemplate(templateName())))
|
|
/// matches the specialization \c X<Y>
|
|
AST_MATCHER_P(TemplateArgument, refersToTemplate,
|
|
internal::Matcher<TemplateName>, InnerMatcher) {
|
|
if (Node.getKind() != TemplateArgument::Template)
|
|
return false;
|
|
return InnerMatcher.matches(Node.getAsTemplate(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches a canonical TemplateArgument that refers to a certain
|
|
/// declaration.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct B { int next; };
|
|
/// template<int(B::*next_ptr)> struct A {};
|
|
/// A<&B::next> a;
|
|
/// \endcode
|
|
/// classTemplateSpecializationDecl(hasAnyTemplateArgument(
|
|
/// refersToDeclaration(fieldDecl(hasName("next")))))
|
|
/// matches the specialization \c A<&B::next> with \c fieldDecl(...) matching
|
|
/// \c B::next
|
|
AST_MATCHER_P(TemplateArgument, refersToDeclaration,
|
|
internal::Matcher<Decl>, InnerMatcher) {
|
|
if (Node.getKind() == TemplateArgument::Declaration)
|
|
return InnerMatcher.matches(*Node.getAsDecl(), Finder, Builder);
|
|
return false;
|
|
}
|
|
|
|
/// Matches a sugar TemplateArgument that refers to a certain expression.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct B { int next; };
|
|
/// template<int(B::*next_ptr)> struct A {};
|
|
/// A<&B::next> a;
|
|
/// \endcode
|
|
/// templateSpecializationType(hasAnyTemplateArgument(
|
|
/// isExpr(hasDescendant(declRefExpr(to(fieldDecl(hasName("next"))))))))
|
|
/// matches the specialization \c A<&B::next> with \c fieldDecl(...) matching
|
|
/// \c B::next
|
|
AST_MATCHER_P(TemplateArgument, isExpr, internal::Matcher<Expr>, InnerMatcher) {
|
|
if (Node.getKind() == TemplateArgument::Expression)
|
|
return InnerMatcher.matches(*Node.getAsExpr(), Finder, Builder);
|
|
return false;
|
|
}
|
|
|
|
/// Matches a TemplateArgument that is an integral value.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<int T> struct C {};
|
|
/// C<42> c;
|
|
/// \endcode
|
|
/// classTemplateSpecializationDecl(
|
|
/// hasAnyTemplateArgument(isIntegral()))
|
|
/// matches the implicit instantiation of C in C<42>
|
|
/// with isIntegral() matching 42.
|
|
AST_MATCHER(TemplateArgument, isIntegral) {
|
|
return Node.getKind() == TemplateArgument::Integral;
|
|
}
|
|
|
|
/// Matches a TemplateArgument that refers to an integral type.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<int T> struct C {};
|
|
/// C<42> c;
|
|
/// \endcode
|
|
/// classTemplateSpecializationDecl(
|
|
/// hasAnyTemplateArgument(refersToIntegralType(asString("int"))))
|
|
/// matches the implicit instantiation of C in C<42>.
|
|
AST_MATCHER_P(TemplateArgument, refersToIntegralType,
|
|
internal::Matcher<QualType>, InnerMatcher) {
|
|
if (Node.getKind() != TemplateArgument::Integral)
|
|
return false;
|
|
return InnerMatcher.matches(Node.getIntegralType(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches a TemplateArgument of integral type with a given value.
|
|
///
|
|
/// Note that 'Value' is a string as the template argument's value is
|
|
/// an arbitrary precision integer. 'Value' must be euqal to the canonical
|
|
/// representation of that integral value in base 10.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<int T> struct C {};
|
|
/// C<42> c;
|
|
/// \endcode
|
|
/// classTemplateSpecializationDecl(
|
|
/// hasAnyTemplateArgument(equalsIntegralValue("42")))
|
|
/// matches the implicit instantiation of C in C<42>.
|
|
AST_MATCHER_P(TemplateArgument, equalsIntegralValue,
|
|
std::string, Value) {
|
|
if (Node.getKind() != TemplateArgument::Integral)
|
|
return false;
|
|
return toString(Node.getAsIntegral(), 10) == Value;
|
|
}
|
|
|
|
/// Matches an Objective-C autorelease pool statement.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// @autoreleasepool {
|
|
/// int x = 0;
|
|
/// }
|
|
/// \endcode
|
|
/// autoreleasePoolStmt(stmt()) matches the declaration of "x"
|
|
/// inside the autorelease pool.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt,
|
|
ObjCAutoreleasePoolStmt> autoreleasePoolStmt;
|
|
|
|
/// Matches any export declaration.
|
|
///
|
|
/// Example matches following declarations.
|
|
/// \code
|
|
/// export void foo();
|
|
/// export { void foo(); }
|
|
/// export namespace { void foo(); }
|
|
/// export int v;
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, ExportDecl> exportDecl;
|
|
|
|
/// Matches any value declaration.
|
|
///
|
|
/// Example matches A, B, C and F
|
|
/// \code
|
|
/// enum X { A, B, C };
|
|
/// void F();
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, ValueDecl> valueDecl;
|
|
|
|
/// Matches C++ constructor declarations.
|
|
///
|
|
/// Example matches Foo::Foo() and Foo::Foo(int)
|
|
/// \code
|
|
/// class Foo {
|
|
/// public:
|
|
/// Foo();
|
|
/// Foo(int);
|
|
/// int DoSomething();
|
|
/// };
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, CXXConstructorDecl>
|
|
cxxConstructorDecl;
|
|
|
|
/// Matches explicit C++ destructor declarations.
|
|
///
|
|
/// Example matches Foo::~Foo()
|
|
/// \code
|
|
/// class Foo {
|
|
/// public:
|
|
/// virtual ~Foo();
|
|
/// };
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, CXXDestructorDecl>
|
|
cxxDestructorDecl;
|
|
|
|
/// Matches enum declarations.
|
|
///
|
|
/// Example matches X
|
|
/// \code
|
|
/// enum X {
|
|
/// A, B, C
|
|
/// };
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, EnumDecl> enumDecl;
|
|
|
|
/// Matches enum constants.
|
|
///
|
|
/// Example matches A, B, C
|
|
/// \code
|
|
/// enum X {
|
|
/// A, B, C
|
|
/// };
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, EnumConstantDecl>
|
|
enumConstantDecl;
|
|
|
|
/// Matches tag declarations.
|
|
///
|
|
/// Example matches X, Z, U, S, E
|
|
/// \code
|
|
/// class X;
|
|
/// template<class T> class Z {};
|
|
/// struct S {};
|
|
/// union U {};
|
|
/// enum E {
|
|
/// A, B, C
|
|
/// };
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, TagDecl> tagDecl;
|
|
|
|
/// Matches method declarations.
|
|
///
|
|
/// Example matches y
|
|
/// \code
|
|
/// class X { void y(); };
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, CXXMethodDecl>
|
|
cxxMethodDecl;
|
|
|
|
/// Matches conversion operator declarations.
|
|
///
|
|
/// Example matches the operator.
|
|
/// \code
|
|
/// class X { operator int() const; };
|
|
/// \endcode
|
|
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 concept declarations.
|
|
///
|
|
/// Example matches integral
|
|
/// \code
|
|
/// template<typename T>
|
|
/// concept integral = std::is_integral_v<T>;
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, ConceptDecl>
|
|
conceptDecl;
|
|
|
|
/// Matches variable declarations.
|
|
///
|
|
/// Note: this does not match declarations of member variables, which are
|
|
/// "field" declarations in Clang parlance.
|
|
///
|
|
/// Example matches a
|
|
/// \code
|
|
/// int a;
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, VarDecl> varDecl;
|
|
|
|
/// Matches field declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class X { int m; };
|
|
/// \endcode
|
|
/// fieldDecl()
|
|
/// matches 'm'.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, FieldDecl> fieldDecl;
|
|
|
|
/// Matches indirect field declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct X { struct { int a; }; };
|
|
/// \endcode
|
|
/// indirectFieldDecl()
|
|
/// matches 'a'.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, IndirectFieldDecl>
|
|
indirectFieldDecl;
|
|
|
|
/// Matches function declarations.
|
|
///
|
|
/// Example matches f
|
|
/// \code
|
|
/// void f();
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, FunctionDecl>
|
|
functionDecl;
|
|
|
|
/// Matches C++ function template declarations.
|
|
///
|
|
/// Example matches f
|
|
/// \code
|
|
/// template<class T> void f(T t) {}
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, FunctionTemplateDecl>
|
|
functionTemplateDecl;
|
|
|
|
/// Matches friend declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class X { friend void foo(); };
|
|
/// \endcode
|
|
/// friendDecl()
|
|
/// matches 'friend void foo()'.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, FriendDecl> friendDecl;
|
|
|
|
/// Matches statements.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// { ++a; }
|
|
/// \endcode
|
|
/// stmt()
|
|
/// matches both the compound statement '{ ++a; }' and '++a'.
|
|
extern const internal::VariadicAllOfMatcher<Stmt> stmt;
|
|
|
|
/// Matches declaration statements.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int a;
|
|
/// \endcode
|
|
/// declStmt()
|
|
/// matches 'int a'.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, DeclStmt> declStmt;
|
|
|
|
/// Matches member expressions.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class Y {
|
|
/// void x() { this->x(); x(); Y y; y.x(); a; this->b; Y::b; }
|
|
/// int a; static int b;
|
|
/// };
|
|
/// \endcode
|
|
/// memberExpr()
|
|
/// matches this->x, x, y.x, a, this->b
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, MemberExpr> memberExpr;
|
|
|
|
/// Matches unresolved member expressions.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct X {
|
|
/// template <class T> void f();
|
|
/// void g();
|
|
/// };
|
|
/// template <class T> void h() { X x; x.f<T>(); x.g(); }
|
|
/// \endcode
|
|
/// unresolvedMemberExpr()
|
|
/// matches x.f<T>
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, UnresolvedMemberExpr>
|
|
unresolvedMemberExpr;
|
|
|
|
/// Matches member expressions where the actual member referenced could not be
|
|
/// resolved because the base expression or the member name was dependent.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <class T> void f() { T t; t.g(); }
|
|
/// \endcode
|
|
/// cxxDependentScopeMemberExpr()
|
|
/// matches t.g
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt,
|
|
CXXDependentScopeMemberExpr>
|
|
cxxDependentScopeMemberExpr;
|
|
|
|
/// Matches call expressions.
|
|
///
|
|
/// Example matches x.y() and y()
|
|
/// \code
|
|
/// X x;
|
|
/// x.y();
|
|
/// y();
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CallExpr> callExpr;
|
|
|
|
/// Matches call expressions which were resolved using ADL.
|
|
///
|
|
/// Example matches y(x) but not y(42) or NS::y(x).
|
|
/// \code
|
|
/// namespace NS {
|
|
/// struct X {};
|
|
/// void y(X);
|
|
/// }
|
|
///
|
|
/// void y(...);
|
|
///
|
|
/// void test() {
|
|
/// NS::X x;
|
|
/// y(x); // Matches
|
|
/// NS::y(x); // Doesn't match
|
|
/// y(42); // Doesn't match
|
|
/// using NS::y;
|
|
/// y(x); // Found by both unqualified lookup and ADL, doesn't match
|
|
// }
|
|
/// \endcode
|
|
AST_MATCHER(CallExpr, usesADL) { return Node.usesADL(); }
|
|
|
|
/// Matches lambda expressions.
|
|
///
|
|
/// Example matches [&](){return 5;}
|
|
/// \code
|
|
/// [&](){return 5;}
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, LambdaExpr> lambdaExpr;
|
|
|
|
/// Matches member call expressions.
|
|
///
|
|
/// Example matches x.y()
|
|
/// \code
|
|
/// X x;
|
|
/// x.y();
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXMemberCallExpr>
|
|
cxxMemberCallExpr;
|
|
|
|
/// Matches ObjectiveC Message invocation expressions.
|
|
///
|
|
/// The innermost message send invokes the "alloc" class method on the
|
|
/// NSString class, while the outermost message send invokes the
|
|
/// "initWithString" instance method on the object returned from
|
|
/// NSString's "alloc". This matcher should match both message sends.
|
|
/// \code
|
|
/// [[NSString alloc] initWithString:@"Hello"]
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCMessageExpr>
|
|
objcMessageExpr;
|
|
|
|
/// Matches ObjectiveC String literal expressions.
|
|
///
|
|
/// Example matches @"abcd"
|
|
/// \code
|
|
/// NSString *s = @"abcd";
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCStringLiteral>
|
|
objcStringLiteral;
|
|
|
|
/// Matches Objective-C interface declarations.
|
|
///
|
|
/// Example matches Foo
|
|
/// \code
|
|
/// @interface Foo
|
|
/// @end
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, ObjCInterfaceDecl>
|
|
objcInterfaceDecl;
|
|
|
|
/// Matches Objective-C implementation declarations.
|
|
///
|
|
/// Example matches Foo
|
|
/// \code
|
|
/// @implementation Foo
|
|
/// @end
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, ObjCImplementationDecl>
|
|
objcImplementationDecl;
|
|
|
|
/// Matches Objective-C protocol declarations.
|
|
///
|
|
/// Example matches FooDelegate
|
|
/// \code
|
|
/// @protocol FooDelegate
|
|
/// @end
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, ObjCProtocolDecl>
|
|
objcProtocolDecl;
|
|
|
|
/// Matches Objective-C category declarations.
|
|
///
|
|
/// Example matches Foo (Additions)
|
|
/// \code
|
|
/// @interface Foo (Additions)
|
|
/// @end
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, ObjCCategoryDecl>
|
|
objcCategoryDecl;
|
|
|
|
/// Matches Objective-C category definitions.
|
|
///
|
|
/// Example matches Foo (Additions)
|
|
/// \code
|
|
/// @implementation Foo (Additions)
|
|
/// @end
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, ObjCCategoryImplDecl>
|
|
objcCategoryImplDecl;
|
|
|
|
/// Matches Objective-C method declarations.
|
|
///
|
|
/// Example matches both declaration and definition of -[Foo method]
|
|
/// \code
|
|
/// @interface Foo
|
|
/// - (void)method;
|
|
/// @end
|
|
///
|
|
/// @implementation Foo
|
|
/// - (void)method {}
|
|
/// @end
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, ObjCMethodDecl>
|
|
objcMethodDecl;
|
|
|
|
/// Matches block declarations.
|
|
///
|
|
/// Example matches the declaration of the nameless block printing an input
|
|
/// integer.
|
|
///
|
|
/// \code
|
|
/// myFunc(^(int p) {
|
|
/// printf("%d", p);
|
|
/// })
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, BlockDecl>
|
|
blockDecl;
|
|
|
|
/// Matches Objective-C instance variable declarations.
|
|
///
|
|
/// Example matches _enabled
|
|
/// \code
|
|
/// @implementation Foo {
|
|
/// BOOL _enabled;
|
|
/// }
|
|
/// @end
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, ObjCIvarDecl>
|
|
objcIvarDecl;
|
|
|
|
/// Matches Objective-C property declarations.
|
|
///
|
|
/// Example matches enabled
|
|
/// \code
|
|
/// @interface Foo
|
|
/// @property BOOL enabled;
|
|
/// @end
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, ObjCPropertyDecl>
|
|
objcPropertyDecl;
|
|
|
|
/// Matches Objective-C \@throw statements.
|
|
///
|
|
/// Example matches \@throw
|
|
/// \code
|
|
/// @throw obj;
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCAtThrowStmt>
|
|
objcThrowStmt;
|
|
|
|
/// Matches Objective-C @try statements.
|
|
///
|
|
/// Example matches @try
|
|
/// \code
|
|
/// @try {}
|
|
/// @catch (...) {}
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCAtTryStmt>
|
|
objcTryStmt;
|
|
|
|
/// Matches Objective-C @catch statements.
|
|
///
|
|
/// Example matches @catch
|
|
/// \code
|
|
/// @try {}
|
|
/// @catch (...) {}
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCAtCatchStmt>
|
|
objcCatchStmt;
|
|
|
|
/// Matches Objective-C @finally statements.
|
|
///
|
|
/// Example matches @finally
|
|
/// \code
|
|
/// @try {}
|
|
/// @finally {}
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCAtFinallyStmt>
|
|
objcFinallyStmt;
|
|
|
|
/// Matches expressions that introduce cleanups to be run at the end
|
|
/// of the sub-expression's evaluation.
|
|
///
|
|
/// Example matches std::string()
|
|
/// \code
|
|
/// const std::string str = std::string();
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ExprWithCleanups>
|
|
exprWithCleanups;
|
|
|
|
/// Matches init list expressions.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int a[] = { 1, 2 };
|
|
/// struct B { int x, y; };
|
|
/// B b = { 5, 6 };
|
|
/// \endcode
|
|
/// initListExpr()
|
|
/// matches "{ 1, 2 }" and "{ 5, 6 }"
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, InitListExpr>
|
|
initListExpr;
|
|
|
|
/// Matches the syntactic form of init list expressions
|
|
/// (if expression have it).
|
|
AST_MATCHER_P(InitListExpr, hasSyntacticForm,
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
const Expr *SyntForm = Node.getSyntacticForm();
|
|
return (SyntForm != nullptr &&
|
|
InnerMatcher.matches(*SyntForm, Finder, Builder));
|
|
}
|
|
|
|
/// Matches C++ initializer list expressions.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// std::vector<int> a({ 1, 2, 3 });
|
|
/// std::vector<int> b = { 4, 5 };
|
|
/// int c[] = { 6, 7 };
|
|
/// std::pair<int, int> d = { 8, 9 };
|
|
/// \endcode
|
|
/// cxxStdInitializerListExpr()
|
|
/// matches "{ 1, 2, 3 }" and "{ 4, 5 }"
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt,
|
|
CXXStdInitializerListExpr>
|
|
cxxStdInitializerListExpr;
|
|
|
|
/// Matches implicit initializers of init list expressions.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// point ptarray[10] = { [2].y = 1.0, [2].x = 2.0, [0].x = 1.0 };
|
|
/// \endcode
|
|
/// implicitValueInitExpr()
|
|
/// matches "[0].y" (implicitly)
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ImplicitValueInitExpr>
|
|
implicitValueInitExpr;
|
|
|
|
/// Matches paren list expressions.
|
|
/// ParenListExprs don't have a predefined type and are used for late parsing.
|
|
/// In the final AST, they can be met in template declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<typename T> class X {
|
|
/// void f() {
|
|
/// X x(*this);
|
|
/// int a = 0, b = 1; int i = (a, b);
|
|
/// }
|
|
/// };
|
|
/// \endcode
|
|
/// parenListExpr() matches "*this" but NOT matches (a, b) because (a, b)
|
|
/// has a predefined type and is a ParenExpr, not a ParenListExpr.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ParenListExpr>
|
|
parenListExpr;
|
|
|
|
/// Matches substitutions of non-type template parameters.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <int N>
|
|
/// struct A { static const int n = N; };
|
|
/// struct B : public A<42> {};
|
|
/// \endcode
|
|
/// substNonTypeTemplateParmExpr()
|
|
/// matches "N" in the right-hand side of "static const int n = N;"
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt,
|
|
SubstNonTypeTemplateParmExpr>
|
|
substNonTypeTemplateParmExpr;
|
|
|
|
/// Matches using declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace X { int x; }
|
|
/// using X::x;
|
|
/// \endcode
|
|
/// usingDecl()
|
|
/// matches \code using X::x \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, UsingDecl> usingDecl;
|
|
|
|
/// Matches using-enum declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace X { enum x {...}; }
|
|
/// using enum X::x;
|
|
/// \endcode
|
|
/// usingEnumDecl()
|
|
/// matches \code using enum X::x \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, UsingEnumDecl>
|
|
usingEnumDecl;
|
|
|
|
/// Matches using namespace declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace X { int x; }
|
|
/// using namespace X;
|
|
/// \endcode
|
|
/// usingDirectiveDecl()
|
|
/// matches \code using namespace X \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, UsingDirectiveDecl>
|
|
usingDirectiveDecl;
|
|
|
|
/// Matches reference to a name that can be looked up during parsing
|
|
/// but could not be resolved to a specific declaration.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<typename T>
|
|
/// T foo() { T a; return a; }
|
|
/// template<typename T>
|
|
/// void bar() {
|
|
/// foo<T>();
|
|
/// }
|
|
/// \endcode
|
|
/// unresolvedLookupExpr()
|
|
/// matches \code foo<T>() \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, UnresolvedLookupExpr>
|
|
unresolvedLookupExpr;
|
|
|
|
/// Matches unresolved using value declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<typename X>
|
|
/// class C : private X {
|
|
/// using X::x;
|
|
/// };
|
|
/// \endcode
|
|
/// unresolvedUsingValueDecl()
|
|
/// matches \code using X::x \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl,
|
|
UnresolvedUsingValueDecl>
|
|
unresolvedUsingValueDecl;
|
|
|
|
/// Matches unresolved using value declarations that involve the
|
|
/// typename.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T>
|
|
/// struct Base { typedef T Foo; };
|
|
///
|
|
/// template<typename T>
|
|
/// struct S : private Base<T> {
|
|
/// using typename Base<T>::Foo;
|
|
/// };
|
|
/// \endcode
|
|
/// unresolvedUsingTypenameDecl()
|
|
/// matches \code using Base<T>::Foo \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl,
|
|
UnresolvedUsingTypenameDecl>
|
|
unresolvedUsingTypenameDecl;
|
|
|
|
/// Matches a constant expression wrapper.
|
|
///
|
|
/// Example matches the constant in the case statement:
|
|
/// (matcher = constantExpr())
|
|
/// \code
|
|
/// switch (a) {
|
|
/// case 37: break;
|
|
/// }
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ConstantExpr>
|
|
constantExpr;
|
|
|
|
/// Matches parentheses used in expressions.
|
|
///
|
|
/// Example matches (foo() + 1)
|
|
/// \code
|
|
/// int foo() { return 1; }
|
|
/// int a = (foo() + 1);
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ParenExpr> parenExpr;
|
|
|
|
/// Matches constructor call expressions (including implicit ones).
|
|
///
|
|
/// Example matches string(ptr, n) and ptr within arguments of f
|
|
/// (matcher = cxxConstructExpr())
|
|
/// \code
|
|
/// void f(const string &a, const string &b);
|
|
/// char *ptr;
|
|
/// int n;
|
|
/// f(string(ptr, n), ptr);
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXConstructExpr>
|
|
cxxConstructExpr;
|
|
|
|
/// Matches unresolved constructor call expressions.
|
|
///
|
|
/// Example matches T(t) in return statement of f
|
|
/// (matcher = cxxUnresolvedConstructExpr())
|
|
/// \code
|
|
/// template <typename T>
|
|
/// void f(const T& t) { return T(t); }
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt,
|
|
CXXUnresolvedConstructExpr>
|
|
cxxUnresolvedConstructExpr;
|
|
|
|
/// Matches implicit and explicit this expressions.
|
|
///
|
|
/// Example matches the implicit this expression in "return i".
|
|
/// (matcher = cxxThisExpr())
|
|
/// \code
|
|
/// struct foo {
|
|
/// int i;
|
|
/// int f() { return i; }
|
|
/// };
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXThisExpr>
|
|
cxxThisExpr;
|
|
|
|
/// Matches nodes where temporaries are created.
|
|
///
|
|
/// Example matches FunctionTakesString(GetStringByValue())
|
|
/// (matcher = cxxBindTemporaryExpr())
|
|
/// \code
|
|
/// FunctionTakesString(GetStringByValue());
|
|
/// FunctionTakesStringByPointer(GetStringPointer());
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXBindTemporaryExpr>
|
|
cxxBindTemporaryExpr;
|
|
|
|
/// Matches nodes where temporaries are materialized.
|
|
///
|
|
/// Example: Given
|
|
/// \code
|
|
/// struct T {void func();};
|
|
/// T f();
|
|
/// void g(T);
|
|
/// \endcode
|
|
/// materializeTemporaryExpr() matches 'f()' in these statements
|
|
/// \code
|
|
/// T u(f());
|
|
/// g(f());
|
|
/// f().func();
|
|
/// \endcode
|
|
/// but does not match
|
|
/// \code
|
|
/// f();
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt,
|
|
MaterializeTemporaryExpr>
|
|
materializeTemporaryExpr;
|
|
|
|
/// Matches new expressions.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// new X;
|
|
/// \endcode
|
|
/// cxxNewExpr()
|
|
/// matches 'new X'.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXNewExpr> cxxNewExpr;
|
|
|
|
/// Matches delete expressions.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// delete X;
|
|
/// \endcode
|
|
/// cxxDeleteExpr()
|
|
/// matches 'delete X'.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXDeleteExpr>
|
|
cxxDeleteExpr;
|
|
|
|
/// Matches noexcept expressions.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// bool a() noexcept;
|
|
/// bool b() noexcept(true);
|
|
/// bool c() noexcept(false);
|
|
/// bool d() noexcept(noexcept(a()));
|
|
/// bool e = noexcept(b()) || noexcept(c());
|
|
/// \endcode
|
|
/// cxxNoexceptExpr()
|
|
/// matches `noexcept(a())`, `noexcept(b())` and `noexcept(c())`.
|
|
/// doesn't match the noexcept specifier in the declarations a, b, c or d.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXNoexceptExpr>
|
|
cxxNoexceptExpr;
|
|
|
|
/// Matches a loop initializing the elements of an array in a number of contexts:
|
|
/// * in the implicit copy/move constructor for a class with an array member
|
|
/// * when a lambda-expression captures an array by value
|
|
/// * when a decomposition declaration decomposes an array
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void testLambdaCapture() {
|
|
/// int a[10];
|
|
/// auto Lam1 = [a]() {
|
|
/// return;
|
|
/// };
|
|
/// }
|
|
/// \endcode
|
|
/// arrayInitLoopExpr() matches the implicit loop that initializes each element of
|
|
/// the implicit array field inside the lambda object, that represents the array `a`
|
|
/// captured by value.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ArrayInitLoopExpr>
|
|
arrayInitLoopExpr;
|
|
|
|
/// The arrayInitIndexExpr consists of two subexpressions: a common expression
|
|
/// (the source array) that is evaluated once up-front, and a per-element initializer
|
|
/// that runs once for each array element. Within the per-element initializer,
|
|
/// the current index may be obtained via an ArrayInitIndexExpr.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void testStructBinding() {
|
|
/// int a[2] = {1, 2};
|
|
/// auto [x, y] = a;
|
|
/// }
|
|
/// \endcode
|
|
/// arrayInitIndexExpr() matches the array index that implicitly iterates
|
|
/// over the array `a` to copy each element to the anonymous array
|
|
/// that backs the structured binding `[x, y]` elements of which are
|
|
/// referred to by their aliases `x` and `y`.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ArrayInitIndexExpr>
|
|
arrayInitIndexExpr;
|
|
|
|
/// Matches array subscript expressions.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int i = a[1];
|
|
/// \endcode
|
|
/// arraySubscriptExpr()
|
|
/// matches "a[1]"
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ArraySubscriptExpr>
|
|
arraySubscriptExpr;
|
|
|
|
/// Matches the value of a default argument at the call site.
|
|
///
|
|
/// Example matches the CXXDefaultArgExpr placeholder inserted for the
|
|
/// default value of the second parameter in the call expression f(42)
|
|
/// (matcher = cxxDefaultArgExpr())
|
|
/// \code
|
|
/// void f(int x, int y = 0);
|
|
/// f(42);
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXDefaultArgExpr>
|
|
cxxDefaultArgExpr;
|
|
|
|
/// Matches overloaded operator calls.
|
|
///
|
|
/// Note that if an operator isn't overloaded, it won't match. Instead, use
|
|
/// binaryOperator matcher.
|
|
/// Currently it does not match operators such as new delete.
|
|
/// FIXME: figure out why these do not match?
|
|
///
|
|
/// Example matches both operator<<((o << b), c) and operator<<(o, b)
|
|
/// (matcher = cxxOperatorCallExpr())
|
|
/// \code
|
|
/// ostream &operator<< (ostream &out, int i) { };
|
|
/// ostream &o; int b = 1, c = 1;
|
|
/// o << b << c;
|
|
/// \endcode
|
|
/// See also the binaryOperation() matcher for more-general matching of binary
|
|
/// uses of this AST node.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXOperatorCallExpr>
|
|
cxxOperatorCallExpr;
|
|
|
|
/// Matches C++17 fold expressions.
|
|
///
|
|
/// Example matches `(0 + ... + args)`:
|
|
/// \code
|
|
/// template <typename... Args>
|
|
/// auto sum(Args... args) {
|
|
/// return (0 + ... + args);
|
|
/// }
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXFoldExpr>
|
|
cxxFoldExpr;
|
|
|
|
/// Matches rewritten binary operators
|
|
///
|
|
/// Example matches use of "<":
|
|
/// \code
|
|
/// #include <compare>
|
|
/// struct HasSpaceshipMem {
|
|
/// int a;
|
|
/// constexpr auto operator<=>(const HasSpaceshipMem&) const = default;
|
|
/// };
|
|
/// void compare() {
|
|
/// HasSpaceshipMem hs1, hs2;
|
|
/// if (hs1 < hs2)
|
|
/// return;
|
|
/// }
|
|
/// \endcode
|
|
/// See also the binaryOperation() matcher for more-general matching
|
|
/// of this AST node.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt,
|
|
CXXRewrittenBinaryOperator>
|
|
cxxRewrittenBinaryOperator;
|
|
|
|
/// Matches expressions.
|
|
///
|
|
/// Example matches x()
|
|
/// \code
|
|
/// void f() { x(); }
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, Expr> expr;
|
|
|
|
/// Matches expressions that refer to declarations.
|
|
///
|
|
/// Example matches x in if (x)
|
|
/// \code
|
|
/// bool x;
|
|
/// if (x) {}
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, DeclRefExpr>
|
|
declRefExpr;
|
|
|
|
/// Matches expressions that refer to dependent scope declarations.
|
|
///
|
|
/// example matches T::v;
|
|
/// \code
|
|
/// template <class T> class X : T { void f() { T::v; } };
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt,
|
|
DependentScopeDeclRefExpr>
|
|
dependentScopeDeclRefExpr;
|
|
|
|
/// Matches a reference to an ObjCIvar.
|
|
///
|
|
/// Example: matches "a" in "init" method:
|
|
/// \code
|
|
/// @implementation A {
|
|
/// NSString *a;
|
|
/// }
|
|
/// - (void) init {
|
|
/// a = @"hello";
|
|
/// }
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCIvarRefExpr>
|
|
objcIvarRefExpr;
|
|
|
|
/// Matches a reference to a block.
|
|
///
|
|
/// Example: matches "^{}":
|
|
/// \code
|
|
/// void f() { ^{}(); }
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, BlockExpr> blockExpr;
|
|
|
|
/// Matches if statements.
|
|
///
|
|
/// Example matches 'if (x) {}'
|
|
/// \code
|
|
/// if (x) {}
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, IfStmt> ifStmt;
|
|
|
|
/// Matches for statements.
|
|
///
|
|
/// Example matches 'for (;;) {}'
|
|
/// \code
|
|
/// for (;;) {}
|
|
/// int i[] = {1, 2, 3}; for (auto a : i);
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ForStmt> forStmt;
|
|
|
|
/// Matches the increment statement of a for loop.
|
|
///
|
|
/// Example:
|
|
/// forStmt(hasIncrement(unaryOperator(hasOperatorName("++"))))
|
|
/// matches '++x' in
|
|
/// \code
|
|
/// for (x; x < N; ++x) { }
|
|
/// \endcode
|
|
AST_MATCHER_P(ForStmt, hasIncrement, internal::Matcher<Stmt>,
|
|
InnerMatcher) {
|
|
const Stmt *const Increment = Node.getInc();
|
|
return (Increment != nullptr &&
|
|
InnerMatcher.matches(*Increment, Finder, Builder));
|
|
}
|
|
|
|
/// Matches the initialization statement of a for loop.
|
|
///
|
|
/// Example:
|
|
/// forStmt(hasLoopInit(declStmt()))
|
|
/// matches 'int x = 0' in
|
|
/// \code
|
|
/// for (int x = 0; x < N; ++x) { }
|
|
/// \endcode
|
|
AST_MATCHER_P(ForStmt, hasLoopInit, internal::Matcher<Stmt>,
|
|
InnerMatcher) {
|
|
const Stmt *const Init = Node.getInit();
|
|
return (Init != nullptr && InnerMatcher.matches(*Init, Finder, Builder));
|
|
}
|
|
|
|
/// Matches range-based for statements.
|
|
///
|
|
/// cxxForRangeStmt() matches 'for (auto a : i)'
|
|
/// \code
|
|
/// int i[] = {1, 2, 3}; for (auto a : i);
|
|
/// for(int j = 0; j < 5; ++j);
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXForRangeStmt>
|
|
cxxForRangeStmt;
|
|
|
|
/// Matches the initialization statement of a for loop.
|
|
///
|
|
/// Example:
|
|
/// forStmt(hasLoopVariable(anything()))
|
|
/// matches 'int x' in
|
|
/// \code
|
|
/// for (int x : a) { }
|
|
/// \endcode
|
|
AST_MATCHER_P(CXXForRangeStmt, hasLoopVariable, internal::Matcher<VarDecl>,
|
|
InnerMatcher) {
|
|
const VarDecl *const Var = Node.getLoopVariable();
|
|
return (Var != nullptr && InnerMatcher.matches(*Var, Finder, Builder));
|
|
}
|
|
|
|
/// Matches the range initialization statement of a for loop.
|
|
///
|
|
/// Example:
|
|
/// forStmt(hasRangeInit(anything()))
|
|
/// matches 'a' in
|
|
/// \code
|
|
/// for (int x : a) { }
|
|
/// \endcode
|
|
AST_MATCHER_P(CXXForRangeStmt, hasRangeInit, internal::Matcher<Expr>,
|
|
InnerMatcher) {
|
|
const Expr *const Init = Node.getRangeInit();
|
|
return (Init != nullptr && InnerMatcher.matches(*Init, Finder, Builder));
|
|
}
|
|
|
|
/// Matches while statements.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// while (true) {}
|
|
/// \endcode
|
|
/// whileStmt()
|
|
/// matches 'while (true) {}'.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, WhileStmt> whileStmt;
|
|
|
|
/// Matches do statements.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// do {} while (true);
|
|
/// \endcode
|
|
/// doStmt()
|
|
/// matches 'do {} while(true)'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, DoStmt> doStmt;
|
|
|
|
/// Matches break statements.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// while (true) { break; }
|
|
/// \endcode
|
|
/// breakStmt()
|
|
/// matches 'break'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, BreakStmt> breakStmt;
|
|
|
|
/// Matches continue statements.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// while (true) { continue; }
|
|
/// \endcode
|
|
/// continueStmt()
|
|
/// matches 'continue'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ContinueStmt>
|
|
continueStmt;
|
|
|
|
/// Matches co_return statements.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// while (true) { co_return; }
|
|
/// \endcode
|
|
/// coreturnStmt()
|
|
/// matches 'co_return'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CoreturnStmt>
|
|
coreturnStmt;
|
|
|
|
/// Matches return statements.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// return 1;
|
|
/// \endcode
|
|
/// returnStmt()
|
|
/// matches 'return 1'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ReturnStmt> returnStmt;
|
|
|
|
/// Matches goto statements.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// goto FOO;
|
|
/// FOO: bar();
|
|
/// \endcode
|
|
/// gotoStmt()
|
|
/// matches 'goto FOO'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, GotoStmt> gotoStmt;
|
|
|
|
/// Matches label statements.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// goto FOO;
|
|
/// FOO: bar();
|
|
/// \endcode
|
|
/// labelStmt()
|
|
/// matches 'FOO:'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, LabelStmt> labelStmt;
|
|
|
|
/// Matches address of label statements (GNU extension).
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// FOO: bar();
|
|
/// void *ptr = &&FOO;
|
|
/// goto *bar;
|
|
/// \endcode
|
|
/// addrLabelExpr()
|
|
/// matches '&&FOO'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, AddrLabelExpr>
|
|
addrLabelExpr;
|
|
|
|
/// Matches switch statements.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// switch(a) { case 42: break; default: break; }
|
|
/// \endcode
|
|
/// switchStmt()
|
|
/// matches 'switch(a)'.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, SwitchStmt> switchStmt;
|
|
|
|
/// Matches case and default statements inside switch statements.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// switch(a) { case 42: break; default: break; }
|
|
/// \endcode
|
|
/// switchCase()
|
|
/// matches 'case 42:' and 'default:'.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, SwitchCase> switchCase;
|
|
|
|
/// Matches case statements inside switch statements.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// switch(a) { case 42: break; default: break; }
|
|
/// \endcode
|
|
/// caseStmt()
|
|
/// matches 'case 42:'.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CaseStmt> caseStmt;
|
|
|
|
/// Matches default statements inside switch statements.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// switch(a) { case 42: break; default: break; }
|
|
/// \endcode
|
|
/// defaultStmt()
|
|
/// matches 'default:'.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, DefaultStmt>
|
|
defaultStmt;
|
|
|
|
/// Matches compound statements.
|
|
///
|
|
/// Example matches '{}' and '{{}}' in 'for (;;) {{}}'
|
|
/// \code
|
|
/// for (;;) {{}}
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CompoundStmt>
|
|
compoundStmt;
|
|
|
|
/// Matches catch statements.
|
|
///
|
|
/// \code
|
|
/// try {} catch(int i) {}
|
|
/// \endcode
|
|
/// cxxCatchStmt()
|
|
/// matches 'catch(int i)'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXCatchStmt>
|
|
cxxCatchStmt;
|
|
|
|
/// Matches try statements.
|
|
///
|
|
/// \code
|
|
/// try {} catch(int i) {}
|
|
/// \endcode
|
|
/// cxxTryStmt()
|
|
/// matches 'try {}'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXTryStmt> cxxTryStmt;
|
|
|
|
/// Matches throw expressions.
|
|
///
|
|
/// \code
|
|
/// try { throw 5; } catch(int i) {}
|
|
/// \endcode
|
|
/// cxxThrowExpr()
|
|
/// matches 'throw 5'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXThrowExpr>
|
|
cxxThrowExpr;
|
|
|
|
/// Matches null statements.
|
|
///
|
|
/// \code
|
|
/// foo();;
|
|
/// \endcode
|
|
/// nullStmt()
|
|
/// matches the second ';'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, NullStmt> nullStmt;
|
|
|
|
/// Matches asm statements.
|
|
///
|
|
/// \code
|
|
/// int i = 100;
|
|
/// __asm("mov al, 2");
|
|
/// \endcode
|
|
/// asmStmt()
|
|
/// matches '__asm("mov al, 2")'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, AsmStmt> asmStmt;
|
|
|
|
/// Matches bool literals.
|
|
///
|
|
/// Example matches true
|
|
/// \code
|
|
/// true
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXBoolLiteralExpr>
|
|
cxxBoolLiteral;
|
|
|
|
/// Matches string literals (also matches wide string literals).
|
|
///
|
|
/// Example matches "abcd", L"abcd"
|
|
/// \code
|
|
/// char *s = "abcd";
|
|
/// wchar_t *ws = L"abcd";
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, StringLiteral>
|
|
stringLiteral;
|
|
|
|
/// Matches character literals (also matches wchar_t).
|
|
///
|
|
/// Not matching Hex-encoded chars (e.g. 0x1234, which is a IntegerLiteral),
|
|
/// though.
|
|
///
|
|
/// Example matches 'a', L'a'
|
|
/// \code
|
|
/// char ch = 'a';
|
|
/// wchar_t chw = L'a';
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CharacterLiteral>
|
|
characterLiteral;
|
|
|
|
/// Matches integer literals of all sizes / encodings, e.g.
|
|
/// 1, 1L, 0x1 and 1U.
|
|
///
|
|
/// Does not match character-encoded integers such as L'a'.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, IntegerLiteral>
|
|
integerLiteral;
|
|
|
|
/// Matches float literals of all sizes / encodings, e.g.
|
|
/// 1.0, 1.0f, 1.0L and 1e10.
|
|
///
|
|
/// Does not match implicit conversions such as
|
|
/// \code
|
|
/// float a = 10;
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, FloatingLiteral>
|
|
floatLiteral;
|
|
|
|
/// Matches imaginary literals, which are based on integer and floating
|
|
/// point literals e.g.: 1i, 1.0i
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ImaginaryLiteral>
|
|
imaginaryLiteral;
|
|
|
|
/// Matches fixed-point literals eg.
|
|
/// 0.5r, 0.5hr, 0.5lr, 0.5uhr, 0.5ur, 0.5ulr
|
|
/// 1.0k, 1.0hk, 1.0lk, 1.0uhk, 1.0uk, 1.0ulk
|
|
/// Exponents 1.0e10k
|
|
/// Hexadecimal numbers 0x0.2p2r
|
|
///
|
|
/// Does not match implicit conversions such as first two lines:
|
|
/// \code
|
|
/// short _Accum sa = 2;
|
|
/// _Accum a = 12.5;
|
|
/// _Accum b = 1.25hk;
|
|
/// _Fract c = 0.25hr;
|
|
/// _Fract v = 0.35uhr;
|
|
/// _Accum g = 1.45uhk;
|
|
/// _Accum decexp1 = 1.575e1k;
|
|
/// \endcode
|
|
/// \compile_args{-ffixed-point;-std=c99}
|
|
///
|
|
/// The matcher \matcher{fixedPointLiteral()} matches
|
|
/// \match{1.25hk}, \match{0.25hr}, \match{0.35uhr},
|
|
/// \match{1.45uhk}, \match{1.575e1k}, but does not
|
|
/// match \nomatch{12.5} and \nomatch{2} from the code block.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, FixedPointLiteral>
|
|
fixedPointLiteral;
|
|
|
|
/// Matches user defined literal operator call.
|
|
///
|
|
/// Example match: "foo"_suffix
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, UserDefinedLiteral>
|
|
userDefinedLiteral;
|
|
|
|
/// Matches compound (i.e. non-scalar) literals
|
|
///
|
|
/// Example match: {1}, (1, 2)
|
|
/// \code
|
|
/// int array[4] = {1};
|
|
/// vector int myvec = (vector int)(1, 2);
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CompoundLiteralExpr>
|
|
compoundLiteralExpr;
|
|
|
|
/// Matches co_await expressions.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// co_await 1;
|
|
/// \endcode
|
|
/// coawaitExpr()
|
|
/// matches 'co_await 1'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CoawaitExpr>
|
|
coawaitExpr;
|
|
/// Matches co_await expressions where the type of the promise is dependent
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, DependentCoawaitExpr>
|
|
dependentCoawaitExpr;
|
|
/// Matches co_yield expressions.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// co_yield 1;
|
|
/// \endcode
|
|
/// coyieldExpr()
|
|
/// matches 'co_yield 1'
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CoyieldExpr>
|
|
coyieldExpr;
|
|
|
|
/// Matches coroutine body statements.
|
|
///
|
|
/// coroutineBodyStmt() matches the coroutine below
|
|
/// \code
|
|
/// generator<int> gen() {
|
|
/// co_return;
|
|
/// }
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CoroutineBodyStmt>
|
|
coroutineBodyStmt;
|
|
|
|
/// Matches nullptr literal.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXNullPtrLiteralExpr>
|
|
cxxNullPtrLiteralExpr;
|
|
|
|
/// Matches GNU __builtin_choose_expr.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ChooseExpr>
|
|
chooseExpr;
|
|
|
|
/// Matches builtin function __builtin_convertvector.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ConvertVectorExpr>
|
|
convertVectorExpr;
|
|
|
|
/// Matches GNU __null expression.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, GNUNullExpr>
|
|
gnuNullExpr;
|
|
|
|
/// Matches C11 _Generic expression.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, GenericSelectionExpr>
|
|
genericSelectionExpr;
|
|
|
|
/// Matches atomic builtins.
|
|
/// Example matches __atomic_load_n(ptr, 1)
|
|
/// \code
|
|
/// void foo() { int *ptr; __atomic_load_n(ptr, 1); }
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, AtomicExpr> atomicExpr;
|
|
|
|
/// Matches statement expression (GNU extension).
|
|
///
|
|
/// Example match: ({ int X = 4; X; })
|
|
/// \code
|
|
/// int C = ({ int X = 4; X; });
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, StmtExpr> stmtExpr;
|
|
|
|
/// Matches binary operator expressions.
|
|
///
|
|
/// Example matches a || b
|
|
/// \code
|
|
/// !(a || b)
|
|
/// \endcode
|
|
/// See also the binaryOperation() matcher for more-general matching.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, BinaryOperator>
|
|
binaryOperator;
|
|
|
|
/// Matches unary operator expressions.
|
|
///
|
|
/// Example matches !a
|
|
/// \code
|
|
/// !a || b
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, UnaryOperator>
|
|
unaryOperator;
|
|
|
|
/// Matches conditional operator expressions.
|
|
///
|
|
/// Example matches a ? b : c
|
|
/// \code
|
|
/// (a ? b : c) + 42
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ConditionalOperator>
|
|
conditionalOperator;
|
|
|
|
/// Matches binary conditional operator expressions (GNU extension).
|
|
///
|
|
/// Example matches a ?: b
|
|
/// \code
|
|
/// (a ?: b) + 42;
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt,
|
|
BinaryConditionalOperator>
|
|
binaryConditionalOperator;
|
|
|
|
/// Matches opaque value expressions. They are used as helpers
|
|
/// to reference another expressions and can be met
|
|
/// in BinaryConditionalOperators, for example.
|
|
///
|
|
/// Example matches 'a'
|
|
/// \code
|
|
/// (a ?: c) + 42;
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, OpaqueValueExpr>
|
|
opaqueValueExpr;
|
|
|
|
/// Matches a C++ static_assert declaration.
|
|
///
|
|
/// Example:
|
|
/// staticAssertDecl()
|
|
/// matches
|
|
/// static_assert(sizeof(S) == sizeof(int))
|
|
/// in
|
|
/// \code
|
|
/// struct S {
|
|
/// int x;
|
|
/// };
|
|
/// static_assert(sizeof(S) == sizeof(int));
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Decl, StaticAssertDecl>
|
|
staticAssertDecl;
|
|
|
|
/// Matches a reinterpret_cast expression.
|
|
///
|
|
/// Either the source expression or the destination type can be matched
|
|
/// using has(), but hasDestinationType() is more specific and can be
|
|
/// more readable.
|
|
///
|
|
/// Example matches reinterpret_cast<char*>(&p) in
|
|
/// \code
|
|
/// void* p = reinterpret_cast<char*>(&p);
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXReinterpretCastExpr>
|
|
cxxReinterpretCastExpr;
|
|
|
|
/// Matches a C++ static_cast expression.
|
|
///
|
|
/// \see hasDestinationType
|
|
/// \see reinterpretCast
|
|
///
|
|
/// Example:
|
|
/// cxxStaticCastExpr()
|
|
/// matches
|
|
/// static_cast<long>(8)
|
|
/// in
|
|
/// \code
|
|
/// long eight(static_cast<long>(8));
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXStaticCastExpr>
|
|
cxxStaticCastExpr;
|
|
|
|
/// Matches a dynamic_cast expression.
|
|
///
|
|
/// Example:
|
|
/// cxxDynamicCastExpr()
|
|
/// matches
|
|
/// dynamic_cast<D*>(&b);
|
|
/// in
|
|
/// \code
|
|
/// struct B { virtual ~B() {} }; struct D : B {};
|
|
/// B b;
|
|
/// D* p = dynamic_cast<D*>(&b);
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXDynamicCastExpr>
|
|
cxxDynamicCastExpr;
|
|
|
|
/// Matches a const_cast expression.
|
|
///
|
|
/// Example: Matches const_cast<int*>(&r) in
|
|
/// \code
|
|
/// int n = 42;
|
|
/// const int &r(n);
|
|
/// int* p = const_cast<int*>(&r);
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXConstCastExpr>
|
|
cxxConstCastExpr;
|
|
|
|
/// Matches a C-style cast expression.
|
|
///
|
|
/// Example: Matches (int) 2.2f in
|
|
/// \code
|
|
/// int i = (int) 2.2f;
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CStyleCastExpr>
|
|
cStyleCastExpr;
|
|
|
|
/// Matches explicit cast expressions.
|
|
///
|
|
/// Matches any cast expression written in user code, whether it be a
|
|
/// C-style cast, a functional-style cast, or a keyword cast.
|
|
///
|
|
/// Does not match implicit conversions.
|
|
///
|
|
/// Note: the name "explicitCast" is chosen to match Clang's terminology, as
|
|
/// Clang uses the term "cast" to apply to implicit conversions as well as to
|
|
/// actual cast expressions.
|
|
///
|
|
/// \see hasDestinationType.
|
|
///
|
|
/// Example: matches all five of the casts in
|
|
/// \code
|
|
/// int((int)(reinterpret_cast<int>(static_cast<int>(const_cast<int>(42)))))
|
|
/// \endcode
|
|
/// but does not match the implicit conversion in
|
|
/// \code
|
|
/// long ell = 42;
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ExplicitCastExpr>
|
|
explicitCastExpr;
|
|
|
|
/// Matches the implicit cast nodes of Clang's AST.
|
|
///
|
|
/// This matches many different places, including function call return value
|
|
/// eliding, as well as any type conversions.
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ImplicitCastExpr>
|
|
implicitCastExpr;
|
|
|
|
/// Matches any cast nodes of Clang's AST.
|
|
///
|
|
/// Example: castExpr() matches each of the following:
|
|
/// \code
|
|
/// (int) 3;
|
|
/// const_cast<Expr *>(SubExpr);
|
|
/// char c = 0;
|
|
/// \endcode
|
|
/// but does not match
|
|
/// \code
|
|
/// int i = (0);
|
|
/// int k = 0;
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CastExpr> castExpr;
|
|
|
|
/// Matches functional cast expressions
|
|
///
|
|
/// Example: Matches Foo(bar);
|
|
/// \code
|
|
/// Foo f = bar;
|
|
/// Foo g = (Foo) bar;
|
|
/// Foo h = Foo(bar);
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXFunctionalCastExpr>
|
|
cxxFunctionalCastExpr;
|
|
|
|
/// Matches functional cast expressions having N != 1 arguments
|
|
///
|
|
/// Example: Matches Foo(bar, bar)
|
|
/// \code
|
|
/// Foo h = Foo(bar, bar);
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXTemporaryObjectExpr>
|
|
cxxTemporaryObjectExpr;
|
|
|
|
/// Matches predefined identifier expressions [C99 6.4.2.2].
|
|
///
|
|
/// Example: Matches __func__
|
|
/// \code
|
|
/// printf("%s", __func__);
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, PredefinedExpr>
|
|
predefinedExpr;
|
|
|
|
/// Matches C99 designated initializer expressions [C99 6.7.8].
|
|
///
|
|
/// Example: Matches { [2].y = 1.0, [0].x = 1.0 }
|
|
/// \code
|
|
/// point ptarray[10] = { [2].y = 1.0, [0].x = 1.0 };
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, DesignatedInitExpr>
|
|
designatedInitExpr;
|
|
|
|
/// Matches designated initializer expressions that contain
|
|
/// a specific number of designators.
|
|
///
|
|
/// Example: Given
|
|
/// \code
|
|
/// point ptarray[10] = { [2].y = 1.0, [0].x = 1.0 };
|
|
/// point ptarray2[10] = { [2].y = 1.0, [2].x = 0.0, [0].x = 1.0 };
|
|
/// \endcode
|
|
/// designatorCountIs(2)
|
|
/// matches '{ [2].y = 1.0, [0].x = 1.0 }',
|
|
/// but not '{ [2].y = 1.0, [2].x = 0.0, [0].x = 1.0 }'.
|
|
AST_MATCHER_P(DesignatedInitExpr, designatorCountIs, unsigned, N) {
|
|
return Node.size() == N;
|
|
}
|
|
|
|
/// Matches \c QualTypes in the clang AST.
|
|
extern const internal::VariadicAllOfMatcher<QualType> qualType;
|
|
|
|
/// Matches \c Types in the clang AST.
|
|
extern const internal::VariadicAllOfMatcher<Type> type;
|
|
|
|
/// Matches \c TypeLocs in the clang AST.
|
|
extern const internal::VariadicAllOfMatcher<TypeLoc> typeLoc;
|
|
|
|
/// Matches if any of the given matchers matches.
|
|
///
|
|
/// Unlike \c anyOf, \c eachOf will generate a match result for each
|
|
/// matching submatcher.
|
|
///
|
|
/// For example, in:
|
|
/// \code
|
|
/// class A { int a; int b; };
|
|
/// \endcode
|
|
/// The matcher:
|
|
/// \code
|
|
/// cxxRecordDecl(eachOf(has(fieldDecl(hasName("a")).bind("v")),
|
|
/// has(fieldDecl(hasName("b")).bind("v"))))
|
|
/// \endcode
|
|
/// will generate two results binding "v", the first of which binds
|
|
/// the field declaration of \c a, the second the field declaration of
|
|
/// \c b.
|
|
///
|
|
/// Usable as: Any Matcher
|
|
extern const internal::VariadicOperatorMatcherFunc<
|
|
2, std::numeric_limits<unsigned>::max()>
|
|
eachOf;
|
|
|
|
/// Matches if any of the given matchers matches.
|
|
///
|
|
/// Usable as: Any Matcher
|
|
extern const internal::VariadicOperatorMatcherFunc<
|
|
2, std::numeric_limits<unsigned>::max()>
|
|
anyOf;
|
|
|
|
/// Matches if all given matchers match.
|
|
///
|
|
/// Usable as: Any Matcher
|
|
extern const internal::VariadicOperatorMatcherFunc<
|
|
2, std::numeric_limits<unsigned>::max()>
|
|
allOf;
|
|
|
|
/// Matches any node regardless of the submatcher.
|
|
///
|
|
/// However, \c optionally will retain any bindings generated by the submatcher.
|
|
/// Useful when additional information which may or may not present about a main
|
|
/// matching node is desired.
|
|
///
|
|
/// For example, in:
|
|
/// \code
|
|
/// class Foo {
|
|
/// int bar;
|
|
/// }
|
|
/// \endcode
|
|
/// The matcher:
|
|
/// \code
|
|
/// cxxRecordDecl(
|
|
/// optionally(has(
|
|
/// fieldDecl(hasName("bar")).bind("var")
|
|
/// ))).bind("record")
|
|
/// \endcode
|
|
/// will produce a result binding for both "record" and "var".
|
|
/// The matcher will produce a "record" binding for even if there is no data
|
|
/// member named "bar" in that class.
|
|
///
|
|
/// Usable as: Any Matcher
|
|
extern const internal::VariadicOperatorMatcherFunc<1, 1> optionally;
|
|
|
|
/// Matches sizeof (C99), alignof (C++11) and vec_step (OpenCL)
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// Foo x = bar;
|
|
/// int y = sizeof(x) + alignof(x);
|
|
/// \endcode
|
|
/// unaryExprOrTypeTraitExpr()
|
|
/// matches \c sizeof(x) and \c alignof(x)
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt,
|
|
UnaryExprOrTypeTraitExpr>
|
|
unaryExprOrTypeTraitExpr;
|
|
|
|
/// Matches any of the \p NodeMatchers with InnerMatchers nested within
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// if (true);
|
|
/// for (; true; );
|
|
/// \endcode
|
|
/// with the matcher
|
|
/// \code
|
|
/// mapAnyOf(ifStmt, forStmt).with(
|
|
/// hasCondition(cxxBoolLiteralExpr(equals(true)))
|
|
/// ).bind("trueCond")
|
|
/// \endcode
|
|
/// matches the \c if and the \c for. It is equivalent to:
|
|
/// \code
|
|
/// auto trueCond = hasCondition(cxxBoolLiteralExpr(equals(true)));
|
|
/// anyOf(
|
|
/// ifStmt(trueCond).bind("trueCond"),
|
|
/// forStmt(trueCond).bind("trueCond")
|
|
/// );
|
|
/// \endcode
|
|
///
|
|
/// The with() chain-call accepts zero or more matchers which are combined
|
|
/// as-if with allOf() in each of the node matchers.
|
|
/// Usable as: Any Matcher
|
|
template <typename T, typename... U>
|
|
auto mapAnyOf(internal::VariadicDynCastAllOfMatcher<T, U> const &...) {
|
|
return internal::MapAnyOfHelper<U...>();
|
|
}
|
|
|
|
/// Matches nodes which can be used with binary operators.
|
|
///
|
|
/// The code
|
|
/// \code
|
|
/// var1 != var2;
|
|
/// \endcode
|
|
/// might be represented in the clang AST as a binaryOperator, a
|
|
/// cxxOperatorCallExpr or a cxxRewrittenBinaryOperator, depending on
|
|
///
|
|
/// * whether the types of var1 and var2 are fundamental (binaryOperator) or at
|
|
/// least one is a class type (cxxOperatorCallExpr)
|
|
/// * whether the code appears in a template declaration, if at least one of the
|
|
/// vars is a dependent-type (binaryOperator)
|
|
/// * whether the code relies on a rewritten binary operator, such as a
|
|
/// spaceship operator or an inverted equality operator
|
|
/// (cxxRewrittenBinaryOperator)
|
|
///
|
|
/// This matcher elides details in places where the matchers for the nodes are
|
|
/// compatible.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// binaryOperation(
|
|
/// hasOperatorName("!="),
|
|
/// hasLHS(expr().bind("lhs")),
|
|
/// hasRHS(expr().bind("rhs"))
|
|
/// )
|
|
/// \endcode
|
|
/// matches each use of "!=" in:
|
|
/// \code
|
|
/// struct S{
|
|
/// bool operator!=(const S&) const;
|
|
/// };
|
|
///
|
|
/// void foo()
|
|
/// {
|
|
/// 1 != 2;
|
|
/// S() != S();
|
|
/// }
|
|
///
|
|
/// template<typename T>
|
|
/// void templ()
|
|
/// {
|
|
/// 1 != 2;
|
|
/// T() != S();
|
|
/// }
|
|
/// struct HasOpEq
|
|
/// {
|
|
/// bool operator==(const HasOpEq &) const;
|
|
/// };
|
|
///
|
|
/// void inverse()
|
|
/// {
|
|
/// HasOpEq s1;
|
|
/// HasOpEq s2;
|
|
/// if (s1 != s2)
|
|
/// return;
|
|
/// }
|
|
///
|
|
/// struct HasSpaceship
|
|
/// {
|
|
/// bool operator<=>(const HasOpEq &) const;
|
|
/// };
|
|
///
|
|
/// void use_spaceship()
|
|
/// {
|
|
/// HasSpaceship s1;
|
|
/// HasSpaceship s2;
|
|
/// if (s1 != s2)
|
|
/// return;
|
|
/// }
|
|
/// \endcode
|
|
extern const internal::MapAnyOfMatcher<BinaryOperator, CXXOperatorCallExpr,
|
|
CXXRewrittenBinaryOperator>
|
|
binaryOperation;
|
|
|
|
/// Matches function calls and constructor calls
|
|
///
|
|
/// Because CallExpr and CXXConstructExpr do not share a common
|
|
/// base class with API accessing arguments etc, AST Matchers for code
|
|
/// which should match both are typically duplicated. This matcher
|
|
/// removes the need for duplication.
|
|
///
|
|
/// Given code
|
|
/// \code
|
|
/// struct ConstructorTakesInt
|
|
/// {
|
|
/// ConstructorTakesInt(int i) {}
|
|
/// };
|
|
///
|
|
/// void callTakesInt(int i)
|
|
/// {
|
|
/// }
|
|
///
|
|
/// void doCall()
|
|
/// {
|
|
/// callTakesInt(42);
|
|
/// }
|
|
///
|
|
/// void doConstruct()
|
|
/// {
|
|
/// ConstructorTakesInt cti(42);
|
|
/// }
|
|
/// \endcode
|
|
///
|
|
/// The matcher
|
|
/// \code
|
|
/// invocation(hasArgument(0, integerLiteral(equals(42))))
|
|
/// \endcode
|
|
/// matches the expression in both doCall and doConstruct
|
|
extern const internal::MapAnyOfMatcher<CallExpr, CXXConstructExpr> invocation;
|
|
|
|
/// Matches unary expressions that have a specific type of argument.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int a, c; float b; int s = sizeof(a) + sizeof(b) + alignof(c);
|
|
/// \endcode
|
|
/// unaryExprOrTypeTraitExpr(hasArgumentOfType(asString("int"))
|
|
/// matches \c sizeof(a) and \c alignof(c)
|
|
AST_MATCHER_P(UnaryExprOrTypeTraitExpr, hasArgumentOfType,
|
|
internal::Matcher<QualType>, InnerMatcher) {
|
|
const QualType ArgumentType = Node.getTypeOfArgument();
|
|
return InnerMatcher.matches(ArgumentType, Finder, Builder);
|
|
}
|
|
|
|
/// Matches unary expressions of a certain kind.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int x;
|
|
/// int s = sizeof(x) + alignof(x)
|
|
/// \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;
|
|
}
|
|
|
|
/// Same as unaryExprOrTypeTraitExpr, but only matching
|
|
/// alignof.
|
|
inline internal::BindableMatcher<Stmt> alignOfExpr(
|
|
const internal::Matcher<UnaryExprOrTypeTraitExpr> &InnerMatcher) {
|
|
return stmt(unaryExprOrTypeTraitExpr(
|
|
allOf(anyOf(ofKind(UETT_AlignOf), ofKind(UETT_PreferredAlignOf)),
|
|
InnerMatcher)));
|
|
}
|
|
|
|
/// Same as unaryExprOrTypeTraitExpr, but only matching
|
|
/// sizeof.
|
|
inline internal::BindableMatcher<Stmt> sizeOfExpr(
|
|
const internal::Matcher<UnaryExprOrTypeTraitExpr> &InnerMatcher) {
|
|
return stmt(unaryExprOrTypeTraitExpr(
|
|
allOf(ofKind(UETT_SizeOf), InnerMatcher)));
|
|
}
|
|
|
|
/// Matches NamedDecl nodes that have the specified name.
|
|
///
|
|
/// Supports specifying enclosing namespaces or classes by prefixing the name
|
|
/// with '<enclosing>::'.
|
|
/// Does not match typedefs of an underlying type with the given name.
|
|
///
|
|
/// Example matches X (Name == "X")
|
|
/// \code
|
|
/// class X;
|
|
/// \endcode
|
|
///
|
|
/// Example matches X (Name is one of "::a::b::X", "a::b::X", "b::X", "X")
|
|
/// \code
|
|
/// namespace a { namespace b { class X; } }
|
|
/// \endcode
|
|
inline internal::Matcher<NamedDecl> hasName(StringRef Name) {
|
|
return internal::Matcher<NamedDecl>(
|
|
new internal::HasNameMatcher({std::string(Name)}));
|
|
}
|
|
|
|
/// Matches NamedDecl nodes that have any of the specified names.
|
|
///
|
|
/// This matcher is only provided as a performance optimization of hasName.
|
|
/// \code
|
|
/// hasAnyName(a, b, c)
|
|
/// \endcode
|
|
/// is equivalent to, but faster than
|
|
/// \code
|
|
/// anyOf(hasName(a), hasName(b), hasName(c))
|
|
/// \endcode
|
|
extern const internal::VariadicFunction<internal::Matcher<NamedDecl>, StringRef,
|
|
internal::hasAnyNameFunc>
|
|
hasAnyName;
|
|
|
|
/// Matches NamedDecl nodes whose fully qualified names contain
|
|
/// a substring matched by the given RegExp.
|
|
///
|
|
/// Supports specifying enclosing namespaces or classes by
|
|
/// prefixing the name with '<enclosing>::'. Does not match typedefs
|
|
/// of an underlying type with the given name.
|
|
///
|
|
/// Example matches X (regexp == "::X")
|
|
/// \code
|
|
/// class X;
|
|
/// \endcode
|
|
///
|
|
/// Example matches X (regexp is one of "::X", "^foo::.*X", among others)
|
|
/// \code
|
|
/// namespace foo { namespace bar { class X; } }
|
|
/// \endcode
|
|
AST_MATCHER_REGEX(NamedDecl, matchesName, RegExp) {
|
|
std::string FullNameString = "::" + Node.getQualifiedNameAsString();
|
|
return RegExp->match(FullNameString);
|
|
}
|
|
|
|
/// Matches overloaded operator names.
|
|
///
|
|
/// Matches overloaded operator names specified in strings without the
|
|
/// "operator" prefix: e.g. "<<".
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// class A { int operator*(); };
|
|
/// const A &operator<<(const A &a, const A &b);
|
|
/// A a;
|
|
/// a << a; // <-- This matches
|
|
/// \endcode
|
|
///
|
|
/// \c cxxOperatorCallExpr(hasOverloadedOperatorName("<<"))) matches the
|
|
/// specified line and
|
|
/// \c cxxRecordDecl(hasMethod(hasOverloadedOperatorName("*")))
|
|
/// matches the declaration of \c A.
|
|
///
|
|
/// Usable as: Matcher<CXXOperatorCallExpr>, Matcher<FunctionDecl>
|
|
inline internal::PolymorphicMatcher<
|
|
internal::HasOverloadedOperatorNameMatcher,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(CXXOperatorCallExpr, FunctionDecl),
|
|
std::vector<std::string>>
|
|
hasOverloadedOperatorName(StringRef Name) {
|
|
return internal::PolymorphicMatcher<
|
|
internal::HasOverloadedOperatorNameMatcher,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(CXXOperatorCallExpr, FunctionDecl),
|
|
std::vector<std::string>>({std::string(Name)});
|
|
}
|
|
|
|
/// Matches overloaded operator names.
|
|
///
|
|
/// Matches overloaded operator names specified in strings without the
|
|
/// "operator" prefix: e.g. "<<".
|
|
///
|
|
/// hasAnyOverloadedOperatorName("+", "-")
|
|
/// Is equivalent to
|
|
/// anyOf(hasOverloadedOperatorName("+"), hasOverloadedOperatorName("-"))
|
|
extern const internal::VariadicFunction<
|
|
internal::PolymorphicMatcher<internal::HasOverloadedOperatorNameMatcher,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(
|
|
CXXOperatorCallExpr, FunctionDecl),
|
|
std::vector<std::string>>,
|
|
StringRef, internal::hasAnyOverloadedOperatorNameFunc>
|
|
hasAnyOverloadedOperatorName;
|
|
|
|
/// Matches template-dependent, but known, member names.
|
|
///
|
|
/// In template declarations, dependent members are not resolved and so can
|
|
/// not be matched to particular named declarations.
|
|
///
|
|
/// This matcher allows to match on the known name of members.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T>
|
|
/// struct S {
|
|
/// void mem();
|
|
/// };
|
|
/// template <typename T>
|
|
/// void x() {
|
|
/// S<T> s;
|
|
/// s.mem();
|
|
/// }
|
|
/// \endcode
|
|
/// \c cxxDependentScopeMemberExpr(hasMemberName("mem")) matches `s.mem()`
|
|
AST_MATCHER_P(CXXDependentScopeMemberExpr, hasMemberName, std::string, N) {
|
|
return Node.getMember().getAsString() == N;
|
|
}
|
|
|
|
/// Matches template-dependent, but known, member names against an already-bound
|
|
/// node
|
|
///
|
|
/// In template declarations, dependent members are not resolved and so can
|
|
/// not be matched to particular named declarations.
|
|
///
|
|
/// This matcher allows to match on the name of already-bound VarDecl, FieldDecl
|
|
/// and CXXMethodDecl nodes.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T>
|
|
/// struct S {
|
|
/// void mem();
|
|
/// };
|
|
/// template <typename T>
|
|
/// void x() {
|
|
/// S<T> s;
|
|
/// s.mem();
|
|
/// }
|
|
/// \endcode
|
|
/// The matcher
|
|
/// @code
|
|
/// \c cxxDependentScopeMemberExpr(
|
|
/// hasObjectExpression(declRefExpr(hasType(templateSpecializationType(
|
|
/// hasDeclaration(classTemplateDecl(has(cxxRecordDecl(has(
|
|
/// cxxMethodDecl(hasName("mem")).bind("templMem")
|
|
/// )))))
|
|
/// )))),
|
|
/// memberHasSameNameAsBoundNode("templMem")
|
|
/// )
|
|
/// @endcode
|
|
/// first matches and binds the @c mem member of the @c S template, then
|
|
/// compares its name to the usage in @c s.mem() in the @c x function template
|
|
AST_MATCHER_P(CXXDependentScopeMemberExpr, memberHasSameNameAsBoundNode,
|
|
std::string, BindingID) {
|
|
auto MemberName = Node.getMember().getAsString();
|
|
|
|
return Builder->removeBindings(
|
|
[this, MemberName](const BoundNodesMap &Nodes) {
|
|
const DynTypedNode &BN = Nodes.getNode(this->BindingID);
|
|
if (const auto *ND = BN.get<NamedDecl>()) {
|
|
if (!isa<FieldDecl, CXXMethodDecl, VarDecl>(ND))
|
|
return true;
|
|
return ND->getName() != MemberName;
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
|
|
/// Matches the dependent name of a DependentScopeDeclRefExpr or
|
|
/// DependentNameType
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// template <class T> class X : T { void f() { T::v; } };
|
|
/// \endcode
|
|
/// \c dependentScopeDeclRefExpr(hasDependentName("v")) matches `T::v`
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// template <typename T> struct declToImport {
|
|
/// typedef typename T::type dependent_name;
|
|
/// };
|
|
/// \endcode
|
|
/// \c dependentNameType(hasDependentName("type")) matches `T::type`
|
|
AST_POLYMORPHIC_MATCHER_P(hasDependentName,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(
|
|
DependentScopeDeclRefExpr, DependentNameType),
|
|
std::string, N) {
|
|
return internal::getDependentName(Node) == N;
|
|
}
|
|
|
|
/// 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.
|
|
///
|
|
/// Example matches Y, Z, 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
|
|
///
|
|
/// 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_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);
|
|
}
|
|
|
|
/// Matches C++ classes that have a direct or indirect base matching \p
|
|
/// BaseSpecMatcher.
|
|
///
|
|
/// Example:
|
|
/// matcher hasAnyBase(hasType(cxxRecordDecl(hasName("SpecialBase"))))
|
|
/// \code
|
|
/// class Foo;
|
|
/// class Bar : Foo {};
|
|
/// class Baz : Bar {};
|
|
/// class SpecialBase;
|
|
/// class Proxy : SpecialBase {}; // matches Proxy
|
|
/// class IndirectlyDerived : Proxy {}; //matches IndirectlyDerived
|
|
/// \endcode
|
|
///
|
|
// FIXME: Refactor this and isDerivedFrom to reuse implementation.
|
|
AST_MATCHER_P(CXXRecordDecl, hasAnyBase, internal::Matcher<CXXBaseSpecifier>,
|
|
BaseSpecMatcher) {
|
|
return internal::matchesAnyBase(Node, BaseSpecMatcher, Finder, Builder);
|
|
}
|
|
|
|
/// Matches C++ classes that have a direct base matching \p BaseSpecMatcher.
|
|
///
|
|
/// Example:
|
|
/// matcher hasDirectBase(hasType(cxxRecordDecl(hasName("SpecialBase"))))
|
|
/// \code
|
|
/// class Foo;
|
|
/// class Bar : Foo {};
|
|
/// class Baz : Bar {};
|
|
/// class SpecialBase;
|
|
/// class Proxy : SpecialBase {}; // matches Proxy
|
|
/// class IndirectlyDerived : Proxy {}; // doesn't match
|
|
/// \endcode
|
|
AST_MATCHER_P(CXXRecordDecl, hasDirectBase, internal::Matcher<CXXBaseSpecifier>,
|
|
BaseSpecMatcher) {
|
|
return Node.hasDefinition() &&
|
|
llvm::any_of(Node.bases(), [&](const CXXBaseSpecifier &Base) {
|
|
return BaseSpecMatcher.matches(Base, Finder, Builder);
|
|
});
|
|
}
|
|
|
|
/// Similar to \c isDerivedFrom(), but also matches classes that directly
|
|
/// match \c Base.
|
|
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_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.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// class A { void func(); };
|
|
/// class B { void member(); };
|
|
/// \endcode
|
|
///
|
|
/// \c cxxRecordDecl(hasMethod(hasName("func"))) matches the declaration of
|
|
/// \c A but not \c B.
|
|
AST_MATCHER_P(CXXRecordDecl, hasMethod, internal::Matcher<CXXMethodDecl>,
|
|
InnerMatcher) {
|
|
BoundNodesTreeBuilder Result(*Builder);
|
|
auto MatchIt = matchesFirstInPointerRange(InnerMatcher, Node.method_begin(),
|
|
Node.method_end(), Finder, &Result);
|
|
if (MatchIt == Node.method_end())
|
|
return false;
|
|
|
|
if (Finder->isTraversalIgnoringImplicitNodes() && (*MatchIt)->isImplicit())
|
|
return false;
|
|
*Builder = std::move(Result);
|
|
return true;
|
|
}
|
|
|
|
/// Matches the generated class of lambda expressions.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// auto x = []{};
|
|
/// \endcode
|
|
///
|
|
/// \c cxxRecordDecl(isLambda()) matches the implicit class declaration of
|
|
/// \c decltype(x)
|
|
AST_MATCHER(CXXRecordDecl, isLambda) {
|
|
return Node.isLambda();
|
|
}
|
|
|
|
/// Matches AST nodes that have child AST nodes that match the
|
|
/// provided matcher.
|
|
///
|
|
/// Example matches X, Y
|
|
/// (matcher = cxxRecordDecl(has(cxxRecordDecl(hasName("X")))
|
|
/// \code
|
|
/// class X {}; // Matches X, because X::X is a class of name X inside X.
|
|
/// class Y { class X {}; };
|
|
/// class Z { class Y { class X {}; }; }; // Does not match Z.
|
|
/// \endcode
|
|
///
|
|
/// ChildT must be an AST base type.
|
|
///
|
|
/// Usable as: Any Matcher
|
|
/// Note that has is direct matcher, so it also matches things like implicit
|
|
/// casts and paren casts. If you are matching with expr then you should
|
|
/// probably consider using ignoringParenImpCasts like:
|
|
/// has(ignoringParenImpCasts(expr())).
|
|
extern const internal::ArgumentAdaptingMatcherFunc<internal::HasMatcher> has;
|
|
|
|
/// Matches AST nodes that have descendant AST nodes that match the
|
|
/// provided matcher.
|
|
///
|
|
/// Example matches X, Y, Z
|
|
/// (matcher = cxxRecordDecl(hasDescendant(cxxRecordDecl(hasName("X")))))
|
|
/// \code
|
|
/// class X {}; // Matches X, because X::X is a class of name X inside X.
|
|
/// class Y { class X {}; };
|
|
/// class Z { class Y { class X {}; }; };
|
|
/// \endcode
|
|
///
|
|
/// DescendantT must be an AST base type.
|
|
///
|
|
/// Usable as: Any Matcher
|
|
extern const internal::ArgumentAdaptingMatcherFunc<
|
|
internal::HasDescendantMatcher>
|
|
hasDescendant;
|
|
|
|
/// Matches AST nodes that have child AST nodes that match the
|
|
/// provided matcher.
|
|
///
|
|
/// Example matches X, Y, Y::X, Z::Y, Z::Y::X
|
|
/// (matcher = cxxRecordDecl(forEach(cxxRecordDecl(hasName("X")))
|
|
/// \code
|
|
/// class X {};
|
|
/// class Y { class X {}; }; // Matches Y, because Y::X is a class of name X
|
|
/// // inside Y.
|
|
/// class Z { class Y { class X {}; }; }; // Does not match Z.
|
|
/// \endcode
|
|
///
|
|
/// ChildT must be an AST base type.
|
|
///
|
|
/// As opposed to 'has', 'forEach' will cause a match for each result that
|
|
/// matches instead of only on the first one.
|
|
///
|
|
/// Usable as: Any Matcher
|
|
extern const internal::ArgumentAdaptingMatcherFunc<internal::ForEachMatcher>
|
|
forEach;
|
|
|
|
/// Matches AST nodes that have descendant AST nodes that match the
|
|
/// provided matcher.
|
|
///
|
|
/// Example matches X, A, A::X, B, B::C, B::C::X
|
|
/// (matcher = cxxRecordDecl(forEachDescendant(cxxRecordDecl(hasName("X")))))
|
|
/// \code
|
|
/// class X {};
|
|
/// class A { class X {}; }; // Matches A, because A::X is a class of name
|
|
/// // X inside A.
|
|
/// class B { class C { class X {}; }; };
|
|
/// \endcode
|
|
///
|
|
/// DescendantT must be an AST base type.
|
|
///
|
|
/// As opposed to 'hasDescendant', 'forEachDescendant' will cause a match for
|
|
/// each result that matches instead of only on the first one.
|
|
///
|
|
/// Note: Recursively combined ForEachDescendant can cause many matches:
|
|
/// cxxRecordDecl(forEachDescendant(cxxRecordDecl(
|
|
/// forEachDescendant(cxxRecordDecl())
|
|
/// )))
|
|
/// will match 10 times (plus injected class name matches) on:
|
|
/// \code
|
|
/// class A { class B { class C { class D { class E {}; }; }; }; };
|
|
/// \endcode
|
|
///
|
|
/// Usable as: Any Matcher
|
|
extern const internal::ArgumentAdaptingMatcherFunc<
|
|
internal::ForEachDescendantMatcher>
|
|
forEachDescendant;
|
|
|
|
/// Matches if the node or any descendant matches.
|
|
///
|
|
/// Generates results for each match.
|
|
///
|
|
/// For example, in:
|
|
/// \code
|
|
/// class A { class B {}; class C {}; };
|
|
/// \endcode
|
|
/// The matcher:
|
|
/// \code
|
|
/// cxxRecordDecl(hasName("::A"),
|
|
/// findAll(cxxRecordDecl(isDefinition()).bind("m")))
|
|
/// \endcode
|
|
/// will generate results for \c A, \c B and \c C.
|
|
///
|
|
/// Usable as: Any Matcher
|
|
template <typename T>
|
|
internal::Matcher<T> findAll(const internal::Matcher<T> &Matcher) {
|
|
return eachOf(Matcher, forEachDescendant(Matcher));
|
|
}
|
|
|
|
/// Matches AST nodes that have a parent that matches the provided
|
|
/// matcher.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void f() { for (;;) { int x = 42; if (true) { int x = 43; } } }
|
|
/// \endcode
|
|
/// \c compoundStmt(hasParent(ifStmt())) matches "{ int x = 43; }".
|
|
///
|
|
/// Usable as: Any Matcher
|
|
extern const internal::ArgumentAdaptingMatcherFunc<
|
|
internal::HasParentMatcher,
|
|
internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc, Attr>,
|
|
internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc, Attr>>
|
|
hasParent;
|
|
|
|
/// Matches AST nodes that have an ancestor that matches the provided
|
|
/// matcher.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void f() { if (true) { int x = 42; } }
|
|
/// void g() { for (;;) { int x = 43; } }
|
|
/// \endcode
|
|
/// \c expr(integerLiteral(hasAncestor(ifStmt()))) matches \c 42, but not 43.
|
|
///
|
|
/// Usable as: Any Matcher
|
|
extern const internal::ArgumentAdaptingMatcherFunc<
|
|
internal::HasAncestorMatcher,
|
|
internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc, Attr>,
|
|
internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc, Attr>>
|
|
hasAncestor;
|
|
|
|
/// Matches if the provided matcher does not match.
|
|
///
|
|
/// Example matches Y (matcher = cxxRecordDecl(unless(hasName("X"))))
|
|
/// \code
|
|
/// class X {};
|
|
/// class Y {};
|
|
/// \endcode
|
|
///
|
|
/// Usable as: Any Matcher
|
|
extern const internal::VariadicOperatorMatcherFunc<1, 1> unless;
|
|
|
|
/// Matches a node if the declaration associated with that node
|
|
/// matches the given matcher.
|
|
///
|
|
/// The associated declaration is:
|
|
/// - for type nodes, the declaration of the underlying type
|
|
/// - for CallExpr, the declaration of the callee
|
|
/// - for MemberExpr, the declaration of the referenced member
|
|
/// - for CXXConstructExpr, the declaration of the constructor
|
|
/// - for CXXNewExpr, the declaration of the operator new
|
|
/// - for ObjCIvarExpr, the declaration of the ivar
|
|
///
|
|
/// For type nodes, hasDeclaration will generally match the declaration of the
|
|
/// sugared type. Given
|
|
/// \code
|
|
/// class X {};
|
|
/// typedef X Y;
|
|
/// Y y;
|
|
/// \endcode
|
|
/// in varDecl(hasType(hasDeclaration(decl()))) the decl will match the
|
|
/// typedefDecl. A common use case is to match the underlying, desugared type.
|
|
/// This can be achieved by using the hasUnqualifiedDesugaredType matcher:
|
|
/// \code
|
|
/// varDecl(hasType(hasUnqualifiedDesugaredType(
|
|
/// recordType(hasDeclaration(decl())))))
|
|
/// \endcode
|
|
/// In this matcher, the decl will match the CXXRecordDecl of class X.
|
|
///
|
|
/// Usable as: Matcher<AddrLabelExpr>, Matcher<CallExpr>,
|
|
/// Matcher<CXXConstructExpr>, Matcher<CXXNewExpr>, Matcher<DeclRefExpr>,
|
|
/// Matcher<EnumType>, Matcher<InjectedClassNameType>, Matcher<LabelStmt>,
|
|
/// Matcher<MemberExpr>, Matcher<QualType>, Matcher<RecordType>,
|
|
/// Matcher<TagType>, Matcher<TemplateSpecializationType>,
|
|
/// Matcher<TemplateTypeParmType>, Matcher<TypedefType>,
|
|
/// Matcher<UnresolvedUsingType>
|
|
inline internal::PolymorphicMatcher<
|
|
internal::HasDeclarationMatcher,
|
|
void(internal::HasDeclarationSupportedTypes), internal::Matcher<Decl>>
|
|
hasDeclaration(const internal::Matcher<Decl> &InnerMatcher) {
|
|
return internal::PolymorphicMatcher<
|
|
internal::HasDeclarationMatcher,
|
|
void(internal::HasDeclarationSupportedTypes), internal::Matcher<Decl>>(
|
|
InnerMatcher);
|
|
}
|
|
|
|
/// Matches a \c NamedDecl whose underlying declaration matches the given
|
|
/// matcher.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace N { template<class T> void f(T t); }
|
|
/// template <class T> void g() { using N::f; f(T()); }
|
|
/// \endcode
|
|
/// \c unresolvedLookupExpr(hasAnyDeclaration(
|
|
/// namedDecl(hasUnderlyingDecl(hasName("::N::f")))))
|
|
/// matches the use of \c f in \c g() .
|
|
AST_MATCHER_P(NamedDecl, hasUnderlyingDecl, internal::Matcher<NamedDecl>,
|
|
InnerMatcher) {
|
|
const NamedDecl *UnderlyingDecl = Node.getUnderlyingDecl();
|
|
|
|
return UnderlyingDecl != nullptr &&
|
|
InnerMatcher.matches(*UnderlyingDecl, Finder, Builder);
|
|
}
|
|
|
|
/// Matches on the implicit object argument of a member call expression, after
|
|
/// stripping off any parentheses or implicit casts.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// 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>,
|
|
InnerMatcher) {
|
|
const Expr *ExprNode = Node.getImplicitObjectArgument()
|
|
->IgnoreParenImpCasts();
|
|
return (ExprNode != nullptr &&
|
|
InnerMatcher.matches(*ExprNode, Finder, Builder));
|
|
}
|
|
|
|
|
|
/// Matches on the receiver of an ObjectiveC Message expression.
|
|
///
|
|
/// Example
|
|
/// matcher = objCMessageExpr(hasReceiverType(asString("UIWebView *")));
|
|
/// matches the [webView ...] message invocation.
|
|
/// \code
|
|
/// NSString *webViewJavaScript = ...
|
|
/// UIWebView *webView = ...
|
|
/// [webView stringByEvaluatingJavaScriptFromString:webViewJavascript];
|
|
/// \endcode
|
|
AST_MATCHER_P(ObjCMessageExpr, hasReceiverType, internal::Matcher<QualType>,
|
|
InnerMatcher) {
|
|
const QualType TypeDecl = Node.getReceiverType();
|
|
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 = objcMessageExpr(isInstanceMessage())
|
|
/// matches
|
|
/// \code
|
|
/// NSString *x = @"hello";
|
|
/// [x containsString:@"h"];
|
|
/// \endcode
|
|
/// but not
|
|
/// \code
|
|
/// [NSString stringWithFormat:@"format"];
|
|
/// \endcode
|
|
AST_MATCHER(ObjCMessageExpr, isInstanceMessage) {
|
|
return Node.isInstanceMessage();
|
|
}
|
|
|
|
/// Matches if the Objective-C message is sent to an instance,
|
|
/// and the inner matcher matches on that instance.
|
|
///
|
|
/// For example the method call in
|
|
/// \code
|
|
/// NSString *x = @"hello";
|
|
/// [x containsString:@"h"];
|
|
/// \endcode
|
|
/// is matched by
|
|
/// objcMessageExpr(hasReceiver(declRefExpr(to(varDecl(hasName("x"))))))
|
|
AST_MATCHER_P(ObjCMessageExpr, hasReceiver, internal::Matcher<Expr>,
|
|
InnerMatcher) {
|
|
const Expr *ReceiverNode = Node.getInstanceReceiver();
|
|
return (ReceiverNode != nullptr &&
|
|
InnerMatcher.matches(*ReceiverNode->IgnoreParenImpCasts(), Finder,
|
|
Builder));
|
|
}
|
|
|
|
/// Matches when BaseName == Selector.getAsString()
|
|
///
|
|
/// matcher = objCMessageExpr(hasSelector("loadHTMLString:baseURL:"));
|
|
/// matches the outer message expr in the code below, but NOT the message
|
|
/// invocation for self.bodyView.
|
|
/// \code
|
|
/// [self.bodyView loadHTMLString:html baseURL:NULL];
|
|
/// \endcode
|
|
AST_MATCHER_P(ObjCMessageExpr, hasSelector, std::string, BaseName) {
|
|
Selector Sel = Node.getSelector();
|
|
return BaseName == Sel.getAsString();
|
|
}
|
|
|
|
/// Matches when at least one of the supplied string equals to the
|
|
/// Selector.getAsString()
|
|
///
|
|
/// matcher = objCMessageExpr(hasSelector("methodA:", "methodB:"));
|
|
/// matches both of the expressions below:
|
|
/// \code
|
|
/// [myObj methodA:argA];
|
|
/// [myObj methodB:argB];
|
|
/// \endcode
|
|
extern const internal::VariadicFunction<internal::Matcher<ObjCMessageExpr>,
|
|
StringRef,
|
|
internal::hasAnySelectorFunc>
|
|
hasAnySelector;
|
|
|
|
/// Matches ObjC selectors whose name contains
|
|
/// a substring matched by the given RegExp.
|
|
/// matcher = objCMessageExpr(matchesSelector("loadHTMLString\:baseURL?"));
|
|
/// matches the outer message expr in the code below, but NOT the message
|
|
/// invocation for self.bodyView.
|
|
/// \code
|
|
/// [self.bodyView loadHTMLString:html baseURL:NULL];
|
|
/// \endcode
|
|
AST_MATCHER_REGEX(ObjCMessageExpr, matchesSelector, RegExp) {
|
|
std::string SelectorString = Node.getSelector().getAsString();
|
|
return RegExp->match(SelectorString);
|
|
}
|
|
|
|
/// Matches when the selector is the empty selector
|
|
///
|
|
/// Matches only when the selector of the objCMessageExpr is NULL. This may
|
|
/// represent an error condition in the tree!
|
|
AST_MATCHER(ObjCMessageExpr, hasNullSelector) {
|
|
return Node.getSelector().isNull();
|
|
}
|
|
|
|
/// Matches when the selector is a Unary Selector
|
|
///
|
|
/// matcher = objCMessageExpr(matchesSelector(hasUnarySelector());
|
|
/// matches self.bodyView in the code below, but NOT the outer message
|
|
/// invocation of "loadHTMLString:baseURL:".
|
|
/// \code
|
|
/// [self.bodyView loadHTMLString:html baseURL:NULL];
|
|
/// \endcode
|
|
AST_MATCHER(ObjCMessageExpr, hasUnarySelector) {
|
|
return Node.getSelector().isUnarySelector();
|
|
}
|
|
|
|
/// Matches when the selector is a keyword selector
|
|
///
|
|
/// objCMessageExpr(hasKeywordSelector()) matches the generated setFrame
|
|
/// message expression in
|
|
///
|
|
/// \code
|
|
/// UIWebView *webView = ...;
|
|
/// CGRect bodyFrame = webView.frame;
|
|
/// bodyFrame.size.height = self.bodyContentHeight;
|
|
/// webView.frame = bodyFrame;
|
|
/// // ^---- matches here
|
|
/// \endcode
|
|
AST_MATCHER(ObjCMessageExpr, hasKeywordSelector) {
|
|
return Node.getSelector().isKeywordSelector();
|
|
}
|
|
|
|
/// Matches when the selector has the specified number of arguments
|
|
///
|
|
/// matcher = objCMessageExpr(numSelectorArgs(0));
|
|
/// matches self.bodyView in the code below
|
|
///
|
|
/// matcher = objCMessageExpr(numSelectorArgs(2));
|
|
/// matches the invocation of "loadHTMLString:baseURL:" but not that
|
|
/// of self.bodyView
|
|
/// \code
|
|
/// [self.bodyView loadHTMLString:html baseURL:NULL];
|
|
/// \endcode
|
|
AST_MATCHER_P(ObjCMessageExpr, numSelectorArgs, unsigned, N) {
|
|
return Node.getSelector().getNumArgs() == N;
|
|
}
|
|
|
|
/// Matches if the call or fold expression's callee expression matches.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class Y { void x() { this->x(); x(); Y y; y.x(); } };
|
|
/// void f() { f(); }
|
|
/// \endcode
|
|
/// callExpr(callee(expr()))
|
|
/// matches this->x(), x(), y.x(), f()
|
|
/// with callee(...)
|
|
/// matching this->x, x, y.x, f respectively
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename... Args>
|
|
/// auto sum(Args... args) {
|
|
/// return (0 + ... + args);
|
|
/// }
|
|
///
|
|
/// template <typename... Args>
|
|
/// auto multiply(Args... args) {
|
|
/// return (args * ... * 1);
|
|
/// }
|
|
/// \endcode
|
|
/// cxxFoldExpr(callee(expr()))
|
|
/// matches (args * ... * 1)
|
|
/// with callee(...)
|
|
/// matching *
|
|
///
|
|
/// Note: Callee cannot take the more general internal::Matcher<Expr>
|
|
/// because this introduces ambiguous overloads with calls to Callee taking a
|
|
/// internal::Matcher<Decl>, as the matcher hierarchy is purely
|
|
/// implemented in terms of implicit casts.
|
|
AST_POLYMORPHIC_MATCHER_P_OVERLOAD(callee,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(CallExpr,
|
|
CXXFoldExpr),
|
|
internal::Matcher<Stmt>, InnerMatcher, 0) {
|
|
const auto *ExprNode = Node.getCallee();
|
|
return (ExprNode != nullptr &&
|
|
InnerMatcher.matches(*ExprNode, Finder, Builder));
|
|
}
|
|
|
|
/// Matches 1) if the call expression's callee's declaration matches the
|
|
/// given matcher; or 2) if the Obj-C message expression's callee's method
|
|
/// declaration matches the given matcher.
|
|
///
|
|
/// Example matches y.x() (matcher = callExpr(callee(
|
|
/// cxxMethodDecl(hasName("x")))))
|
|
/// \code
|
|
/// class Y { public: void x(); };
|
|
/// void z() { Y y; y.x(); }
|
|
/// \endcode
|
|
///
|
|
/// Example 2. Matches [I foo] with
|
|
/// objcMessageExpr(callee(objcMethodDecl(hasName("foo"))))
|
|
///
|
|
/// \code
|
|
/// @interface I: NSObject
|
|
/// +(void)foo;
|
|
/// @end
|
|
/// ...
|
|
/// [I foo]
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
|
|
callee, AST_POLYMORPHIC_SUPPORTED_TYPES(ObjCMessageExpr, CallExpr),
|
|
internal::Matcher<Decl>, InnerMatcher, 1) {
|
|
if (isa<CallExpr>(&Node))
|
|
return callExpr(hasDeclaration(InnerMatcher))
|
|
.matches(Node, Finder, Builder);
|
|
else {
|
|
// The dynamic cast below is guaranteed to succeed as there are only 2
|
|
// supported return types.
|
|
const auto *MsgNode = cast<ObjCMessageExpr>(&Node);
|
|
const Decl *DeclNode = MsgNode->getMethodDecl();
|
|
return (DeclNode != nullptr &&
|
|
InnerMatcher.matches(*DeclNode, Finder, Builder));
|
|
}
|
|
}
|
|
|
|
/// Matches if the expression's or declaration's type matches a type
|
|
/// matcher.
|
|
///
|
|
/// Example matches x (matcher = expr(hasType(cxxRecordDecl(hasName("X")))))
|
|
/// and z (matcher = varDecl(hasType(cxxRecordDecl(hasName("X")))))
|
|
/// and U (matcher = typedefDecl(hasType(asString("int")))
|
|
/// and friend class X (matcher = friendDecl(hasType("X"))
|
|
/// and public virtual X (matcher = cxxBaseSpecifier(hasType(
|
|
/// asString("class X")))
|
|
/// \code
|
|
/// class X {};
|
|
/// void y(X &x) { x; X z; }
|
|
/// typedef int U;
|
|
/// class Y { friend class X; };
|
|
/// class Z : public virtual X {};
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
|
|
hasType,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(Expr, FriendDecl, TypedefNameDecl,
|
|
ValueDecl, CXXBaseSpecifier),
|
|
internal::Matcher<QualType>, InnerMatcher, 0) {
|
|
QualType QT = internal::getUnderlyingType(Node);
|
|
if (!QT.isNull())
|
|
return InnerMatcher.matches(QT, Finder, Builder);
|
|
return false;
|
|
}
|
|
|
|
/// Overloaded to match the declaration of the expression's or value
|
|
/// declaration's type.
|
|
///
|
|
/// In case of a value declaration (for example a variable declaration),
|
|
/// this resolves one layer of indirection. For example, in the value
|
|
/// declaration "X x;", cxxRecordDecl(hasName("X")) matches the declaration of
|
|
/// X, while varDecl(hasType(cxxRecordDecl(hasName("X")))) matches the
|
|
/// declaration of x.
|
|
///
|
|
/// Example matches x (matcher = expr(hasType(cxxRecordDecl(hasName("X")))))
|
|
/// and z (matcher = varDecl(hasType(cxxRecordDecl(hasName("X")))))
|
|
/// and friend class X (matcher = friendDecl(hasType("X"))
|
|
/// and public virtual X (matcher = cxxBaseSpecifier(hasType(
|
|
/// cxxRecordDecl(hasName("X"))))
|
|
/// \code
|
|
/// class X {};
|
|
/// void y(X &x) { x; X z; }
|
|
/// class Y { friend class X; };
|
|
/// class Z : public virtual X {};
|
|
/// \endcode
|
|
///
|
|
/// Example matches class Derived
|
|
/// (matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))
|
|
/// \code
|
|
/// class Base {};
|
|
/// class Derived : Base {};
|
|
/// \endcode
|
|
///
|
|
/// Usable as: Matcher<Expr>, Matcher<FriendDecl>, Matcher<ValueDecl>,
|
|
/// Matcher<CXXBaseSpecifier>
|
|
AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
|
|
hasType,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(Expr, FriendDecl, ValueDecl,
|
|
CXXBaseSpecifier, ObjCInterfaceDecl),
|
|
internal::Matcher<Decl>, InnerMatcher, 1) {
|
|
QualType QT = internal::getUnderlyingType(Node);
|
|
if (!QT.isNull())
|
|
return qualType(hasDeclaration(InnerMatcher)).matches(QT, Finder, Builder);
|
|
return false;
|
|
}
|
|
|
|
/// Matches if the type location of a node matches the inner matcher.
|
|
///
|
|
/// Examples:
|
|
/// \code
|
|
/// int x;
|
|
/// \endcode
|
|
/// declaratorDecl(hasTypeLoc(loc(asString("int"))))
|
|
/// matches int x
|
|
///
|
|
/// \code
|
|
/// auto x = int(3);
|
|
/// \endcode
|
|
/// cxxTemporaryObjectExpr(hasTypeLoc(loc(asString("int"))))
|
|
/// matches int(3)
|
|
///
|
|
/// \code
|
|
/// struct Foo { Foo(int, int); };
|
|
/// auto x = Foo(1, 2);
|
|
/// \endcode
|
|
/// cxxFunctionalCastExpr(hasTypeLoc(loc(asString("struct Foo"))))
|
|
/// matches Foo(1, 2)
|
|
///
|
|
/// Usable as: Matcher<BlockDecl>, Matcher<CXXBaseSpecifier>,
|
|
/// Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>,
|
|
/// Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>,
|
|
/// Matcher<CXXUnresolvedConstructExpr>,
|
|
/// Matcher<CompoundLiteralExpr>,
|
|
/// Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>,
|
|
/// Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>,
|
|
/// Matcher<TypedefNameDecl>
|
|
AST_POLYMORPHIC_MATCHER_P(
|
|
hasTypeLoc,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(
|
|
BlockDecl, CXXBaseSpecifier, CXXCtorInitializer, CXXFunctionalCastExpr,
|
|
CXXNewExpr, CXXTemporaryObjectExpr, CXXUnresolvedConstructExpr,
|
|
CompoundLiteralExpr, DeclaratorDecl, ExplicitCastExpr, ObjCPropertyDecl,
|
|
TemplateArgumentLoc, TypedefNameDecl),
|
|
internal::Matcher<TypeLoc>, Inner) {
|
|
TypeSourceInfo *source = internal::GetTypeSourceInfo(Node);
|
|
if (source == nullptr) {
|
|
// This happens for example for implicit destructors.
|
|
return false;
|
|
}
|
|
return Inner.matches(source->getTypeLoc(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches if the matched type is represented by the given string.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class Y { public: void x(); };
|
|
/// void z() { Y* y; y->x(); }
|
|
/// \endcode
|
|
/// cxxMemberCallExpr(on(hasType(asString("class Y *"))))
|
|
/// matches y->x()
|
|
AST_MATCHER_P(QualType, asString, std::string, Name) {
|
|
return Name == Node.getAsString();
|
|
}
|
|
|
|
/// Matches if the matched type is a pointer type and the pointee type
|
|
/// matches the specified matcher.
|
|
///
|
|
/// Example matches y->x()
|
|
/// (matcher = cxxMemberCallExpr(on(hasType(pointsTo
|
|
/// cxxRecordDecl(hasName("Y")))))))
|
|
/// \code
|
|
/// class Y { public: void x(); };
|
|
/// void z() { Y *y; y->x(); }
|
|
/// \endcode
|
|
AST_MATCHER_P(
|
|
QualType, pointsTo, internal::Matcher<QualType>,
|
|
InnerMatcher) {
|
|
return (!Node.isNull() && Node->isAnyPointerType() &&
|
|
InnerMatcher.matches(Node->getPointeeType(), Finder, Builder));
|
|
}
|
|
|
|
/// Overloaded to match the pointee type's declaration.
|
|
AST_MATCHER_P_OVERLOAD(QualType, pointsTo, internal::Matcher<Decl>,
|
|
InnerMatcher, 1) {
|
|
return pointsTo(qualType(hasDeclaration(InnerMatcher)))
|
|
.matches(Node, Finder, Builder);
|
|
}
|
|
|
|
/// Matches if the matched type matches the unqualified desugared
|
|
/// type of the matched node.
|
|
///
|
|
/// For example, in:
|
|
/// \code
|
|
/// class A {};
|
|
/// using B = A;
|
|
/// \endcode
|
|
/// The matcher type(hasUnqualifiedDesugaredType(recordType())) matches
|
|
/// both B and A.
|
|
AST_MATCHER_P(Type, hasUnqualifiedDesugaredType, internal::Matcher<Type>,
|
|
InnerMatcher) {
|
|
return InnerMatcher.matches(*Node.getUnqualifiedDesugaredType(), Finder,
|
|
Builder);
|
|
}
|
|
|
|
/// Matches if the matched type is a reference type and the referenced
|
|
/// type matches the specified matcher.
|
|
///
|
|
/// Example matches X &x and const X &y
|
|
/// (matcher = varDecl(hasType(references(cxxRecordDecl(hasName("X"))))))
|
|
/// \code
|
|
/// class X {
|
|
/// void a(X b) {
|
|
/// X &x = b;
|
|
/// const X &y = b;
|
|
/// }
|
|
/// };
|
|
/// \endcode
|
|
AST_MATCHER_P(QualType, references, internal::Matcher<QualType>,
|
|
InnerMatcher) {
|
|
return (!Node.isNull() && Node->isReferenceType() &&
|
|
InnerMatcher.matches(Node->getPointeeType(), Finder, Builder));
|
|
}
|
|
|
|
/// Matches QualTypes whose canonical type matches InnerMatcher.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// typedef int &int_ref;
|
|
/// int a;
|
|
/// int_ref b = a;
|
|
/// \endcode
|
|
///
|
|
/// \c varDecl(hasType(qualType(referenceType()))))) will not match the
|
|
/// declaration of b but \c
|
|
/// varDecl(hasType(qualType(hasCanonicalType(referenceType())))))) does.
|
|
AST_MATCHER_P(QualType, hasCanonicalType, internal::Matcher<QualType>,
|
|
InnerMatcher) {
|
|
if (Node.isNull())
|
|
return false;
|
|
return InnerMatcher.matches(Node.getCanonicalType(), Finder, Builder);
|
|
}
|
|
|
|
/// Overloaded to match the referenced type's declaration.
|
|
AST_MATCHER_P_OVERLOAD(QualType, references, internal::Matcher<Decl>,
|
|
InnerMatcher, 1) {
|
|
return references(qualType(hasDeclaration(InnerMatcher)))
|
|
.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()))
|
|
/// only matches `(g()).m()` (the parens are ignored).
|
|
///
|
|
/// FIXME: Overload to allow directly matching types?
|
|
AST_MATCHER_P(CXXMemberCallExpr, onImplicitObjectArgument,
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
const Expr *ExprNode = Node.getImplicitObjectArgument();
|
|
return (ExprNode != nullptr &&
|
|
InnerMatcher.matches(*ExprNode, Finder, Builder));
|
|
}
|
|
|
|
/// 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(
|
|
anyOf(hasType(InnerMatcher), hasType(pointsTo(InnerMatcher))))
|
|
.matches(Node, Finder, Builder);
|
|
}
|
|
|
|
/// Overloaded to match the type's declaration.
|
|
AST_MATCHER_P_OVERLOAD(CXXMemberCallExpr, thisPointerType,
|
|
internal::Matcher<Decl>, InnerMatcher, 1) {
|
|
return onImplicitObjectArgument(
|
|
anyOf(hasType(InnerMatcher), hasType(pointsTo(InnerMatcher))))
|
|
.matches(Node, Finder, Builder);
|
|
}
|
|
|
|
/// Matches a DeclRefExpr that refers to a declaration that matches the
|
|
/// specified matcher.
|
|
///
|
|
/// Example matches x in if(x)
|
|
/// (matcher = declRefExpr(to(varDecl(hasName("x")))))
|
|
/// \code
|
|
/// bool x;
|
|
/// if (x) {}
|
|
/// \endcode
|
|
AST_MATCHER_P(DeclRefExpr, to, internal::Matcher<Decl>,
|
|
InnerMatcher) {
|
|
const Decl *DeclNode = Node.getDecl();
|
|
return (DeclNode != nullptr &&
|
|
InnerMatcher.matches(*DeclNode, Finder, Builder));
|
|
}
|
|
|
|
/// Matches if a node refers to a declaration through a specific
|
|
/// using shadow declaration.
|
|
///
|
|
/// Examples:
|
|
/// \code
|
|
/// namespace a { int f(); }
|
|
/// using a::f;
|
|
/// int x = f();
|
|
/// \endcode
|
|
/// declRefExpr(throughUsingDecl(anything()))
|
|
/// matches \c f
|
|
///
|
|
/// \code
|
|
/// namespace a { class X{}; }
|
|
/// using a::X;
|
|
/// X x;
|
|
/// \endcode
|
|
/// typeLoc(loc(usingType(throughUsingDecl(anything()))))
|
|
/// matches \c X
|
|
///
|
|
/// Usable as: Matcher<DeclRefExpr>, Matcher<UsingType>
|
|
AST_POLYMORPHIC_MATCHER_P(throughUsingDecl,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr,
|
|
UsingType),
|
|
internal::Matcher<UsingShadowDecl>, Inner) {
|
|
const NamedDecl *FoundDecl = Node.getFoundDecl();
|
|
if (const UsingShadowDecl *UsingDecl = dyn_cast<UsingShadowDecl>(FoundDecl))
|
|
return Inner.matches(*UsingDecl, Finder, Builder);
|
|
return false;
|
|
}
|
|
|
|
/// Matches an \c OverloadExpr if any of the declarations in the set of
|
|
/// overloads matches the given matcher.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T> void foo(T);
|
|
/// template <typename T> void bar(T);
|
|
/// template <typename T> void baz(T t) {
|
|
/// foo(t);
|
|
/// bar(t);
|
|
/// }
|
|
/// \endcode
|
|
/// unresolvedLookupExpr(hasAnyDeclaration(
|
|
/// functionTemplateDecl(hasName("foo"))))
|
|
/// matches \c foo in \c foo(t); but not \c bar in \c bar(t);
|
|
AST_MATCHER_P(OverloadExpr, hasAnyDeclaration, internal::Matcher<Decl>,
|
|
InnerMatcher) {
|
|
return matchesFirstInPointerRange(InnerMatcher, Node.decls_begin(),
|
|
Node.decls_end(), Finder,
|
|
Builder) != Node.decls_end();
|
|
}
|
|
|
|
/// Matches the Decl of a DeclStmt which has a single declaration.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int a, b;
|
|
/// int c;
|
|
/// \endcode
|
|
/// declStmt(hasSingleDecl(anything()))
|
|
/// matches 'int c;' but not 'int a, b;'.
|
|
AST_MATCHER_P(DeclStmt, hasSingleDecl, internal::Matcher<Decl>, InnerMatcher) {
|
|
if (Node.isSingleDecl()) {
|
|
const Decl *FoundDecl = Node.getSingleDecl();
|
|
return InnerMatcher.matches(*FoundDecl, Finder, Builder);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Matches a variable declaration that has an initializer expression
|
|
/// that matches the given matcher.
|
|
///
|
|
/// Example matches x (matcher = varDecl(hasInitializer(callExpr())))
|
|
/// \code
|
|
/// bool y() { return true; }
|
|
/// bool x = y();
|
|
/// \endcode
|
|
AST_MATCHER_P(
|
|
VarDecl, hasInitializer, internal::Matcher<Expr>,
|
|
InnerMatcher) {
|
|
const Expr *Initializer = Node.getAnyInitializer();
|
|
return (Initializer != nullptr &&
|
|
InnerMatcher.matches(*Initializer, Finder, Builder));
|
|
}
|
|
|
|
/// Matches a variable serving as the implicit variable for a lambda init-
|
|
/// capture.
|
|
///
|
|
/// Example matches x (matcher = varDecl(isInitCapture()))
|
|
/// \code
|
|
/// auto f = [x=3]() { return x; };
|
|
/// \endcode
|
|
AST_MATCHER(VarDecl, isInitCapture) { return Node.isInitCapture(); }
|
|
|
|
/// Matches each lambda capture in a lambda expression.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int main() {
|
|
/// int x, y;
|
|
/// float z;
|
|
/// auto f = [=]() { return x + y + z; };
|
|
/// }
|
|
/// \endcode
|
|
/// lambdaExpr(forEachLambdaCapture(
|
|
/// lambdaCapture(capturesVar(varDecl(hasType(isInteger()))))))
|
|
/// will trigger two matches, binding for 'x' and 'y' respectively.
|
|
AST_MATCHER_P(LambdaExpr, forEachLambdaCapture,
|
|
internal::Matcher<LambdaCapture>, InnerMatcher) {
|
|
BoundNodesTreeBuilder Result;
|
|
bool Matched = false;
|
|
for (const auto &Capture : Node.captures()) {
|
|
if (Finder->isTraversalIgnoringImplicitNodes() && Capture.isImplicit())
|
|
continue;
|
|
BoundNodesTreeBuilder CaptureBuilder(*Builder);
|
|
if (InnerMatcher.matches(Capture, Finder, &CaptureBuilder)) {
|
|
Matched = true;
|
|
Result.addMatch(CaptureBuilder);
|
|
}
|
|
}
|
|
*Builder = std::move(Result);
|
|
return Matched;
|
|
}
|
|
|
|
/// \brief Matches a static variable with local scope.
|
|
///
|
|
/// Example matches y (matcher = varDecl(isStaticLocal()))
|
|
/// \code
|
|
/// void f() {
|
|
/// int x;
|
|
/// static int y;
|
|
/// }
|
|
/// static int z;
|
|
/// \endcode
|
|
AST_MATCHER(VarDecl, isStaticLocal) {
|
|
return Node.isStaticLocal();
|
|
}
|
|
|
|
/// Matches a variable declaration that has function scope and is a
|
|
/// non-static local variable.
|
|
///
|
|
/// Example matches x (matcher = varDecl(hasLocalStorage())
|
|
/// \code
|
|
/// void f() {
|
|
/// int x;
|
|
/// static int y;
|
|
/// }
|
|
/// int z;
|
|
/// \endcode
|
|
AST_MATCHER(VarDecl, hasLocalStorage) {
|
|
return Node.hasLocalStorage();
|
|
}
|
|
|
|
/// Matches a variable declaration that does not have local storage.
|
|
///
|
|
/// Example matches y and z (matcher = varDecl(hasGlobalStorage())
|
|
/// \code
|
|
/// void f() {
|
|
/// int x;
|
|
/// static int y;
|
|
/// }
|
|
/// int z;
|
|
/// \endcode
|
|
AST_MATCHER(VarDecl, hasGlobalStorage) {
|
|
return Node.hasGlobalStorage();
|
|
}
|
|
|
|
/// Matches a variable declaration that has automatic storage duration.
|
|
///
|
|
/// Example matches x, but not y, z, or a.
|
|
/// (matcher = varDecl(hasAutomaticStorageDuration())
|
|
/// \code
|
|
/// void f() {
|
|
/// int x;
|
|
/// static int y;
|
|
/// thread_local int z;
|
|
/// }
|
|
/// int a;
|
|
/// \endcode
|
|
AST_MATCHER(VarDecl, hasAutomaticStorageDuration) {
|
|
return Node.getStorageDuration() == SD_Automatic;
|
|
}
|
|
|
|
/// Matches a variable declaration that has static storage duration.
|
|
/// It includes the variable declared at namespace scope and those declared
|
|
/// with "static" and "extern" storage class specifiers.
|
|
///
|
|
/// \code
|
|
/// void f() {
|
|
/// int x;
|
|
/// static int y;
|
|
/// thread_local int z;
|
|
/// }
|
|
/// int a;
|
|
/// static int b;
|
|
/// extern int c;
|
|
/// varDecl(hasStaticStorageDuration())
|
|
/// matches the function declaration y, a, b and c.
|
|
/// \endcode
|
|
AST_MATCHER(VarDecl, hasStaticStorageDuration) {
|
|
return Node.getStorageDuration() == SD_Static;
|
|
}
|
|
|
|
/// Matches a variable declaration that has thread storage duration.
|
|
///
|
|
/// Example matches z, but not x, z, or a.
|
|
/// (matcher = varDecl(hasThreadStorageDuration())
|
|
/// \code
|
|
/// void f() {
|
|
/// int x;
|
|
/// static int y;
|
|
/// thread_local int z;
|
|
/// }
|
|
/// int a;
|
|
/// \endcode
|
|
AST_MATCHER(VarDecl, hasThreadStorageDuration) {
|
|
return Node.getStorageDuration() == SD_Thread;
|
|
}
|
|
|
|
/// Matches a variable declaration that is an exception variable from
|
|
/// a C++ catch block, or an Objective-C \@catch statement.
|
|
///
|
|
/// Example matches x (matcher = varDecl(isExceptionVariable())
|
|
/// \code
|
|
/// void f(int y) {
|
|
/// try {
|
|
/// } catch (int x) {
|
|
/// }
|
|
/// }
|
|
/// \endcode
|
|
AST_MATCHER(VarDecl, isExceptionVariable) {
|
|
return Node.isExceptionVariable();
|
|
}
|
|
|
|
/// Checks that a call expression or a constructor call expression has
|
|
/// a specific number of arguments (including absent default arguments).
|
|
///
|
|
/// Example matches f(0, 0) (matcher = callExpr(argumentCountIs(2)))
|
|
/// \code
|
|
/// void f(int x, int y);
|
|
/// f(0, 0);
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER_P(argumentCountIs,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(
|
|
CallExpr, CXXConstructExpr,
|
|
CXXUnresolvedConstructExpr, ObjCMessageExpr),
|
|
unsigned, N) {
|
|
unsigned NumArgs = Node.getNumArgs();
|
|
if (!Finder->isTraversalIgnoringImplicitNodes())
|
|
return NumArgs == N;
|
|
while (NumArgs) {
|
|
if (!isa<CXXDefaultArgExpr>(Node.getArg(NumArgs - 1)))
|
|
break;
|
|
--NumArgs;
|
|
}
|
|
return NumArgs == N;
|
|
}
|
|
|
|
/// Checks that a call expression or a constructor call expression has at least
|
|
/// the specified number of arguments (including absent default arguments).
|
|
///
|
|
/// Example matches f(0, 0) and g(0, 0, 0)
|
|
/// (matcher = callExpr(argumentCountAtLeast(2)))
|
|
/// \code
|
|
/// void f(int x, int y);
|
|
/// void g(int x, int y, int z);
|
|
/// f(0, 0);
|
|
/// g(0, 0, 0);
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER_P(argumentCountAtLeast,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(
|
|
CallExpr, CXXConstructExpr,
|
|
CXXUnresolvedConstructExpr, ObjCMessageExpr),
|
|
unsigned, N) {
|
|
unsigned NumArgs = Node.getNumArgs();
|
|
if (!Finder->isTraversalIgnoringImplicitNodes())
|
|
return NumArgs >= N;
|
|
while (NumArgs) {
|
|
if (!isa<CXXDefaultArgExpr>(Node.getArg(NumArgs - 1)))
|
|
break;
|
|
--NumArgs;
|
|
}
|
|
return NumArgs >= N;
|
|
}
|
|
|
|
/// Matches the n'th argument of a call expression or a constructor
|
|
/// call expression.
|
|
///
|
|
/// Example matches y in x(y)
|
|
/// (matcher = callExpr(hasArgument(0, declRefExpr())))
|
|
/// \code
|
|
/// void x(int) { int y; x(y); }
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER_P2(hasArgument,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(
|
|
CallExpr, CXXConstructExpr,
|
|
CXXUnresolvedConstructExpr, ObjCMessageExpr),
|
|
unsigned, N, internal::Matcher<Expr>, InnerMatcher) {
|
|
if (N >= Node.getNumArgs())
|
|
return false;
|
|
const Expr *Arg = Node.getArg(N);
|
|
if (Finder->isTraversalIgnoringImplicitNodes() && isa<CXXDefaultArgExpr>(Arg))
|
|
return false;
|
|
return InnerMatcher.matches(*Arg->IgnoreParenImpCasts(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches the operand that does not contain the parameter pack.
|
|
///
|
|
/// Example matches `(0 + ... + args)` and `(args * ... * 1)`
|
|
/// (matcher = cxxFoldExpr(hasFoldInit(expr())))
|
|
/// with hasFoldInit(...)
|
|
/// matching `0` and `1` respectively
|
|
/// \code
|
|
/// template <typename... Args>
|
|
/// auto sum(Args... args) {
|
|
/// return (0 + ... + args);
|
|
/// }
|
|
///
|
|
/// template <typename... Args>
|
|
/// auto multiply(Args... args) {
|
|
/// return (args * ... * 1);
|
|
/// }
|
|
/// \endcode
|
|
AST_MATCHER_P(CXXFoldExpr, hasFoldInit, internal::Matcher<Expr>, InnerMacher) {
|
|
const auto *const Init = Node.getInit();
|
|
return Init && InnerMacher.matches(*Init, Finder, Builder);
|
|
}
|
|
|
|
/// Matches the operand that contains the parameter pack.
|
|
///
|
|
/// Example matches `(0 + ... + args)`
|
|
/// (matcher = cxxFoldExpr(hasPattern(expr())))
|
|
/// with hasPattern(...)
|
|
/// matching `args`
|
|
/// \code
|
|
/// template <typename... Args>
|
|
/// auto sum(Args... args) {
|
|
/// return (0 + ... + args);
|
|
/// }
|
|
///
|
|
/// template <typename... Args>
|
|
/// auto multiply(Args... args) {
|
|
/// return (args * ... * 1);
|
|
/// }
|
|
/// \endcode
|
|
AST_MATCHER_P(CXXFoldExpr, hasPattern, internal::Matcher<Expr>, InnerMacher) {
|
|
const Expr *const Pattern = Node.getPattern();
|
|
return Pattern && InnerMacher.matches(*Pattern, Finder, Builder);
|
|
}
|
|
|
|
/// Matches right-folding fold expressions.
|
|
///
|
|
/// Example matches `(args * ... * 1)`
|
|
/// (matcher = cxxFoldExpr(isRightFold()))
|
|
/// \code
|
|
/// template <typename... Args>
|
|
/// auto sum(Args... args) {
|
|
/// return (0 + ... + args);
|
|
/// }
|
|
///
|
|
/// template <typename... Args>
|
|
/// auto multiply(Args... args) {
|
|
/// return (args * ... * 1);
|
|
/// }
|
|
/// \endcode
|
|
AST_MATCHER(CXXFoldExpr, isRightFold) { return Node.isRightFold(); }
|
|
|
|
/// Matches left-folding fold expressions.
|
|
///
|
|
/// Example matches `(0 + ... + args)`
|
|
/// (matcher = cxxFoldExpr(isLeftFold()))
|
|
/// \code
|
|
/// template <typename... Args>
|
|
/// auto sum(Args... args) {
|
|
/// return (0 + ... + args);
|
|
/// }
|
|
///
|
|
/// template <typename... Args>
|
|
/// auto multiply(Args... args) {
|
|
/// return (args * ... * 1);
|
|
/// }
|
|
/// \endcode
|
|
AST_MATCHER(CXXFoldExpr, isLeftFold) { return Node.isLeftFold(); }
|
|
|
|
/// Matches unary fold expressions, i.e. fold expressions without an
|
|
/// initializer.
|
|
///
|
|
/// Example matches `(args * ...)`
|
|
/// (matcher = cxxFoldExpr(isUnaryFold()))
|
|
/// \code
|
|
/// template <typename... Args>
|
|
/// auto sum(Args... args) {
|
|
/// return (0 + ... + args);
|
|
/// }
|
|
///
|
|
/// template <typename... Args>
|
|
/// auto multiply(Args... args) {
|
|
/// return (args * ...);
|
|
/// }
|
|
/// \endcode
|
|
AST_MATCHER(CXXFoldExpr, isUnaryFold) { return Node.getInit() == nullptr; }
|
|
|
|
/// Matches binary fold expressions, i.e. fold expressions with an initializer.
|
|
///
|
|
/// Example matches `(0 + ... + args)`
|
|
/// (matcher = cxxFoldExpr(isBinaryFold()))
|
|
/// \code
|
|
/// template <typename... Args>
|
|
/// auto sum(Args... args) {
|
|
/// return (0 + ... + args);
|
|
/// }
|
|
///
|
|
/// template <typename... Args>
|
|
/// auto multiply(Args... args) {
|
|
/// return (args * ...);
|
|
/// }
|
|
/// \endcode
|
|
AST_MATCHER(CXXFoldExpr, isBinaryFold) { return Node.getInit() != nullptr; }
|
|
|
|
/// Matches the n'th item of an initializer list expression.
|
|
///
|
|
/// Example matches y.
|
|
/// (matcher = initListExpr(hasInit(0, expr())))
|
|
/// \code
|
|
/// int x{y}.
|
|
/// \endcode
|
|
AST_MATCHER_P2(InitListExpr, hasInit, unsigned, N, internal::Matcher<Expr>,
|
|
InnerMatcher) {
|
|
return N < Node.getNumInits() &&
|
|
InnerMatcher.matches(*Node.getInit(N), Finder, Builder);
|
|
}
|
|
|
|
/// Matches declaration statements that contain a specific number of
|
|
/// declarations.
|
|
///
|
|
/// Example: Given
|
|
/// \code
|
|
/// int a, b;
|
|
/// int c;
|
|
/// int d = 2, e;
|
|
/// \endcode
|
|
/// declCountIs(2)
|
|
/// matches 'int a, b;' and 'int d = 2, e;', but not 'int c;'.
|
|
AST_MATCHER_P(DeclStmt, declCountIs, unsigned, N) {
|
|
return std::distance(Node.decl_begin(), Node.decl_end()) == (ptrdiff_t)N;
|
|
}
|
|
|
|
/// Matches the n'th declaration of a declaration statement.
|
|
///
|
|
/// Note that this does not work for global declarations because the AST
|
|
/// breaks up multiple-declaration DeclStmt's into multiple single-declaration
|
|
/// DeclStmt's.
|
|
/// Example: Given non-global declarations
|
|
/// \code
|
|
/// int a, b = 0;
|
|
/// int c;
|
|
/// int d = 2, e;
|
|
/// \endcode
|
|
/// declStmt(containsDeclaration(
|
|
/// 0, varDecl(hasInitializer(anything()))))
|
|
/// matches only 'int d = 2, e;', and
|
|
/// declStmt(containsDeclaration(1, varDecl()))
|
|
/// \code
|
|
/// matches 'int a, b = 0' as well as 'int d = 2, e;'
|
|
/// but 'int c;' is not matched.
|
|
/// \endcode
|
|
AST_MATCHER_P2(DeclStmt, containsDeclaration, unsigned, N,
|
|
internal::Matcher<Decl>, InnerMatcher) {
|
|
const unsigned NumDecls = std::distance(Node.decl_begin(), Node.decl_end());
|
|
if (N >= NumDecls)
|
|
return false;
|
|
DeclStmt::const_decl_iterator Iterator = Node.decl_begin();
|
|
std::advance(Iterator, N);
|
|
return InnerMatcher.matches(**Iterator, Finder, Builder);
|
|
}
|
|
|
|
/// Matches a C++ catch statement that has a catch-all handler.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// try {
|
|
/// // ...
|
|
/// } catch (int) {
|
|
/// // ...
|
|
/// } catch (...) {
|
|
/// // ...
|
|
/// }
|
|
/// \endcode
|
|
/// cxxCatchStmt(isCatchAll()) matches catch(...) but not catch(int).
|
|
AST_MATCHER(CXXCatchStmt, isCatchAll) {
|
|
return Node.getExceptionDecl() == nullptr;
|
|
}
|
|
|
|
/// Matches a constructor initializer.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct Foo {
|
|
/// Foo() : foo_(1) { }
|
|
/// int foo_;
|
|
/// };
|
|
/// \endcode
|
|
/// cxxRecordDecl(has(cxxConstructorDecl(
|
|
/// hasAnyConstructorInitializer(anything())
|
|
/// )))
|
|
/// record matches Foo, hasAnyConstructorInitializer matches foo_(1)
|
|
AST_MATCHER_P(CXXConstructorDecl, hasAnyConstructorInitializer,
|
|
internal::Matcher<CXXCtorInitializer>, InnerMatcher) {
|
|
auto MatchIt = matchesFirstInPointerRange(InnerMatcher, Node.init_begin(),
|
|
Node.init_end(), Finder, Builder);
|
|
if (MatchIt == Node.init_end())
|
|
return false;
|
|
return (*MatchIt)->isWritten() || !Finder->isTraversalIgnoringImplicitNodes();
|
|
}
|
|
|
|
/// Matches the field declaration of a constructor initializer.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct Foo {
|
|
/// Foo() : foo_(1) { }
|
|
/// int foo_;
|
|
/// };
|
|
/// \endcode
|
|
/// cxxRecordDecl(has(cxxConstructorDecl(hasAnyConstructorInitializer(
|
|
/// forField(hasName("foo_"))))))
|
|
/// matches Foo
|
|
/// with forField matching foo_
|
|
AST_MATCHER_P(CXXCtorInitializer, forField,
|
|
internal::Matcher<FieldDecl>, InnerMatcher) {
|
|
const FieldDecl *NodeAsDecl = Node.getAnyMember();
|
|
return (NodeAsDecl != nullptr &&
|
|
InnerMatcher.matches(*NodeAsDecl, Finder, Builder));
|
|
}
|
|
|
|
/// Matches the initializer expression of a constructor initializer.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct Foo {
|
|
/// Foo() : foo_(1) { }
|
|
/// int foo_;
|
|
/// };
|
|
/// \endcode
|
|
/// cxxRecordDecl(has(cxxConstructorDecl(hasAnyConstructorInitializer(
|
|
/// withInitializer(integerLiteral(equals(1)))))))
|
|
/// matches Foo
|
|
/// with withInitializer matching (1)
|
|
AST_MATCHER_P(CXXCtorInitializer, withInitializer,
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
const Expr* NodeAsExpr = Node.getInit();
|
|
return (NodeAsExpr != nullptr &&
|
|
InnerMatcher.matches(*NodeAsExpr, Finder, Builder));
|
|
}
|
|
|
|
/// Matches a constructor initializer if it is explicitly written in
|
|
/// code (as opposed to implicitly added by the compiler).
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct Foo {
|
|
/// Foo() { }
|
|
/// Foo(int) : foo_("A") { }
|
|
/// string foo_;
|
|
/// };
|
|
/// \endcode
|
|
/// cxxConstructorDecl(hasAnyConstructorInitializer(isWritten()))
|
|
/// will match Foo(int), but not Foo()
|
|
AST_MATCHER(CXXCtorInitializer, isWritten) {
|
|
return Node.isWritten();
|
|
}
|
|
|
|
/// Matches a constructor initializer if it is initializing a base, as
|
|
/// opposed to a member.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct B {};
|
|
/// struct D : B {
|
|
/// int I;
|
|
/// D(int i) : I(i) {}
|
|
/// };
|
|
/// struct E : B {
|
|
/// E() : B() {}
|
|
/// };
|
|
/// \endcode
|
|
/// cxxConstructorDecl(hasAnyConstructorInitializer(isBaseInitializer()))
|
|
/// will match E(), but not match D(int).
|
|
AST_MATCHER(CXXCtorInitializer, isBaseInitializer) {
|
|
return Node.isBaseInitializer();
|
|
}
|
|
|
|
/// Matches a constructor initializer if it is initializing a member, as
|
|
/// opposed to a base.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct B {};
|
|
/// struct D : B {
|
|
/// int I;
|
|
/// D(int i) : I(i) {}
|
|
/// };
|
|
/// struct E : B {
|
|
/// E() : B() {}
|
|
/// };
|
|
/// \endcode
|
|
/// cxxConstructorDecl(hasAnyConstructorInitializer(isMemberInitializer()))
|
|
/// will match D(int), but not match E().
|
|
AST_MATCHER(CXXCtorInitializer, isMemberInitializer) {
|
|
return Node.isMemberInitializer();
|
|
}
|
|
|
|
/// Matches any argument of a call expression or a constructor call
|
|
/// expression, or an ObjC-message-send expression.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void x(int, int, int) { int y; x(1, y, 42); }
|
|
/// \endcode
|
|
/// callExpr(hasAnyArgument(declRefExpr()))
|
|
/// matches x(1, y, 42)
|
|
/// with hasAnyArgument(...)
|
|
/// matching y
|
|
///
|
|
/// For ObjectiveC, given
|
|
/// \code
|
|
/// @interface I - (void) f:(int) y; @end
|
|
/// void foo(I *i) { [i f:12]; }
|
|
/// \endcode
|
|
/// objcMessageExpr(hasAnyArgument(integerLiteral(equals(12))))
|
|
/// matches [i f:12]
|
|
AST_POLYMORPHIC_MATCHER_P(hasAnyArgument,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(
|
|
CallExpr, CXXConstructExpr,
|
|
CXXUnresolvedConstructExpr, ObjCMessageExpr),
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
for (const Expr *Arg : Node.arguments()) {
|
|
if (Finder->isTraversalIgnoringImplicitNodes() &&
|
|
isa<CXXDefaultArgExpr>(Arg))
|
|
break;
|
|
BoundNodesTreeBuilder Result(*Builder);
|
|
if (InnerMatcher.matches(*Arg, Finder, &Result)) {
|
|
*Builder = std::move(Result);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Matches lambda captures.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int main() {
|
|
/// int x;
|
|
/// auto f = [x](){};
|
|
/// auto g = [x = 1](){};
|
|
/// }
|
|
/// \endcode
|
|
/// In the matcher `lambdaExpr(hasAnyCapture(lambdaCapture()))`,
|
|
/// `lambdaCapture()` matches `x` and `x=1`.
|
|
extern const internal::VariadicAllOfMatcher<LambdaCapture> lambdaCapture;
|
|
|
|
/// Matches any capture in a lambda expression.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void foo() {
|
|
/// int t = 5;
|
|
/// auto f = [=](){ return t; };
|
|
/// }
|
|
/// \endcode
|
|
/// lambdaExpr(hasAnyCapture(lambdaCapture())) and
|
|
/// lambdaExpr(hasAnyCapture(lambdaCapture(refersToVarDecl(hasName("t")))))
|
|
/// both match `[=](){ return t; }`.
|
|
AST_MATCHER_P(LambdaExpr, hasAnyCapture, internal::Matcher<LambdaCapture>,
|
|
InnerMatcher) {
|
|
for (const LambdaCapture &Capture : Node.captures()) {
|
|
clang::ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
|
|
if (InnerMatcher.matches(Capture, Finder, &Result)) {
|
|
*Builder = std::move(Result);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Matches a `LambdaCapture` that refers to the specified `VarDecl`. The
|
|
/// `VarDecl` can be a separate variable that is captured by value or
|
|
/// reference, or a synthesized variable if the capture has an initializer.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void foo() {
|
|
/// int x;
|
|
/// auto f = [x](){};
|
|
/// auto g = [x = 1](){};
|
|
/// }
|
|
/// \endcode
|
|
/// In the matcher
|
|
/// lambdaExpr(hasAnyCapture(lambdaCapture(capturesVar(hasName("x")))),
|
|
/// capturesVar(hasName("x")) matches `x` and `x = 1`.
|
|
AST_MATCHER_P(LambdaCapture, capturesVar, internal::Matcher<ValueDecl>,
|
|
InnerMatcher) {
|
|
if (!Node.capturesVariable())
|
|
return false;
|
|
auto *capturedVar = Node.getCapturedVar();
|
|
return capturedVar && InnerMatcher.matches(*capturedVar, Finder, Builder);
|
|
}
|
|
|
|
/// Matches a `LambdaCapture` that refers to 'this'.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class C {
|
|
/// int cc;
|
|
/// int f() {
|
|
/// auto l = [this]() { return cc; };
|
|
/// return l();
|
|
/// }
|
|
/// };
|
|
/// \endcode
|
|
/// lambdaExpr(hasAnyCapture(lambdaCapture(capturesThis())))
|
|
/// matches `[this]() { return cc; }`.
|
|
AST_MATCHER(LambdaCapture, capturesThis) { return Node.capturesThis(); }
|
|
|
|
/// Matches a constructor call expression which uses list initialization.
|
|
AST_MATCHER(CXXConstructExpr, isListInitialization) {
|
|
return Node.isListInitialization();
|
|
}
|
|
|
|
/// Matches a constructor call expression which requires
|
|
/// zero initialization.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void foo() {
|
|
/// struct point { double x; double y; };
|
|
/// point pt[2] = { { 1.0, 2.0 } };
|
|
/// }
|
|
/// \endcode
|
|
/// initListExpr(has(cxxConstructExpr(requiresZeroInitialization()))
|
|
/// will match the implicit array filler for pt[1].
|
|
AST_MATCHER(CXXConstructExpr, requiresZeroInitialization) {
|
|
return Node.requiresZeroInitialization();
|
|
}
|
|
|
|
/// Matches the n'th parameter of a function or an ObjC method
|
|
/// declaration or a block.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class X { void f(int x) {} };
|
|
/// \endcode
|
|
/// cxxMethodDecl(hasParameter(0, hasType(varDecl())))
|
|
/// matches f(int x) {}
|
|
/// with hasParameter(...)
|
|
/// matching int x
|
|
///
|
|
/// For ObjectiveC, given
|
|
/// \code
|
|
/// @interface I - (void) f:(int) y; @end
|
|
/// \endcode
|
|
//
|
|
/// the matcher objcMethodDecl(hasParameter(0, hasName("y")))
|
|
/// matches the declaration of method f with hasParameter
|
|
/// matching y.
|
|
AST_POLYMORPHIC_MATCHER_P2(hasParameter,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
|
|
ObjCMethodDecl,
|
|
BlockDecl),
|
|
unsigned, N, internal::Matcher<ParmVarDecl>,
|
|
InnerMatcher) {
|
|
return (N < Node.parameters().size()
|
|
&& InnerMatcher.matches(*Node.parameters()[N], Finder, Builder));
|
|
}
|
|
|
|
/// Matches if the given method declaration declares a member function with an
|
|
/// explicit object parameter.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct A {
|
|
/// int operator-(this A, int);
|
|
/// void fun(this A &&self);
|
|
/// static int operator()(int);
|
|
/// int operator+(int);
|
|
/// };
|
|
/// \endcode
|
|
///
|
|
/// cxxMethodDecl(isExplicitObjectMemberFunction()) matches the first two
|
|
/// methods but not the last two.
|
|
AST_MATCHER(CXXMethodDecl, isExplicitObjectMemberFunction) {
|
|
return Node.isExplicitObjectMemberFunction();
|
|
}
|
|
|
|
/// Matches all arguments and their respective ParmVarDecl.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void f(int i);
|
|
/// int y;
|
|
/// f(y);
|
|
/// \endcode
|
|
/// callExpr(
|
|
/// forEachArgumentWithParam(
|
|
/// declRefExpr(to(varDecl(hasName("y")))),
|
|
/// parmVarDecl(hasType(isInteger()))
|
|
/// ))
|
|
/// matches f(y);
|
|
/// with declRefExpr(...)
|
|
/// matching int y
|
|
/// and parmVarDecl(...)
|
|
/// matching int i
|
|
AST_POLYMORPHIC_MATCHER_P2(forEachArgumentWithParam,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(CallExpr,
|
|
CXXConstructExpr),
|
|
internal::Matcher<Expr>, ArgMatcher,
|
|
internal::Matcher<ParmVarDecl>, ParamMatcher) {
|
|
BoundNodesTreeBuilder Result;
|
|
// The first argument of an overloaded member operator is the implicit object
|
|
// argument of the method which should not be matched against a parameter, so
|
|
// we skip over it here.
|
|
BoundNodesTreeBuilder Matches;
|
|
unsigned ArgIndex =
|
|
cxxOperatorCallExpr(
|
|
callee(cxxMethodDecl(unless(isExplicitObjectMemberFunction()))))
|
|
.matches(Node, Finder, &Matches)
|
|
? 1
|
|
: 0;
|
|
int ParamIndex = 0;
|
|
bool Matched = false;
|
|
for (; ArgIndex < Node.getNumArgs(); ++ArgIndex) {
|
|
BoundNodesTreeBuilder ArgMatches(*Builder);
|
|
if (ArgMatcher.matches(*(Node.getArg(ArgIndex)->IgnoreParenCasts()),
|
|
Finder, &ArgMatches)) {
|
|
BoundNodesTreeBuilder ParamMatches(ArgMatches);
|
|
if (expr(anyOf(cxxConstructExpr(hasDeclaration(cxxConstructorDecl(
|
|
hasParameter(ParamIndex, ParamMatcher)))),
|
|
callExpr(callee(functionDecl(
|
|
hasParameter(ParamIndex, ParamMatcher))))))
|
|
.matches(Node, Finder, &ParamMatches)) {
|
|
Result.addMatch(ParamMatches);
|
|
Matched = true;
|
|
}
|
|
}
|
|
++ParamIndex;
|
|
}
|
|
*Builder = std::move(Result);
|
|
return Matched;
|
|
}
|
|
|
|
/// Matches all arguments and their respective types for a \c CallExpr or
|
|
/// \c CXXConstructExpr. It is very similar to \c forEachArgumentWithParam but
|
|
/// it works on calls through function pointers as well.
|
|
///
|
|
/// The difference is, that function pointers do not provide access to a
|
|
/// \c ParmVarDecl, but only the \c QualType for each argument.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void f(int i);
|
|
/// int y;
|
|
/// f(y);
|
|
/// void (*f_ptr)(int) = f;
|
|
/// f_ptr(y);
|
|
/// \endcode
|
|
/// callExpr(
|
|
/// forEachArgumentWithParamType(
|
|
/// declRefExpr(to(varDecl(hasName("y")))),
|
|
/// qualType(isInteger()).bind("type)
|
|
/// ))
|
|
/// matches f(y) and f_ptr(y)
|
|
/// with declRefExpr(...)
|
|
/// matching int y
|
|
/// and qualType(...)
|
|
/// matching int
|
|
AST_POLYMORPHIC_MATCHER_P2(forEachArgumentWithParamType,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(CallExpr,
|
|
CXXConstructExpr),
|
|
internal::Matcher<Expr>, ArgMatcher,
|
|
internal::Matcher<QualType>, ParamMatcher) {
|
|
BoundNodesTreeBuilder Result;
|
|
// The first argument of an overloaded member operator is the implicit object
|
|
// argument of the method which should not be matched against a parameter, so
|
|
// we skip over it here.
|
|
BoundNodesTreeBuilder Matches;
|
|
unsigned ArgIndex =
|
|
cxxOperatorCallExpr(
|
|
callee(cxxMethodDecl(unless(isExplicitObjectMemberFunction()))))
|
|
.matches(Node, Finder, &Matches)
|
|
? 1
|
|
: 0;
|
|
const FunctionProtoType *FProto = nullptr;
|
|
|
|
if (const auto *Call = dyn_cast<CallExpr>(&Node)) {
|
|
if (const auto *Value =
|
|
dyn_cast_or_null<ValueDecl>(Call->getCalleeDecl())) {
|
|
QualType QT = Value->getType().getCanonicalType();
|
|
|
|
// This does not necessarily lead to a `FunctionProtoType`,
|
|
// e.g. K&R functions do not have a function prototype.
|
|
if (QT->isFunctionPointerType())
|
|
FProto = QT->getPointeeType()->getAs<FunctionProtoType>();
|
|
|
|
if (QT->isMemberFunctionPointerType()) {
|
|
const auto *MP = QT->getAs<MemberPointerType>();
|
|
assert(MP && "Must be member-pointer if its a memberfunctionpointer");
|
|
FProto = MP->getPointeeType()->getAs<FunctionProtoType>();
|
|
assert(FProto &&
|
|
"The call must have happened through a member function "
|
|
"pointer");
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned ParamIndex = 0;
|
|
bool Matched = false;
|
|
unsigned NumArgs = Node.getNumArgs();
|
|
if (FProto && FProto->isVariadic())
|
|
NumArgs = std::min(NumArgs, FProto->getNumParams());
|
|
|
|
for (; ArgIndex < NumArgs; ++ArgIndex, ++ParamIndex) {
|
|
BoundNodesTreeBuilder ArgMatches(*Builder);
|
|
if (ArgMatcher.matches(*(Node.getArg(ArgIndex)->IgnoreParenCasts()), Finder,
|
|
&ArgMatches)) {
|
|
BoundNodesTreeBuilder ParamMatches(ArgMatches);
|
|
|
|
// This test is cheaper compared to the big matcher in the next if.
|
|
// Therefore, please keep this order.
|
|
if (FProto && FProto->getNumParams() > ParamIndex) {
|
|
QualType ParamType = FProto->getParamType(ParamIndex);
|
|
if (ParamMatcher.matches(ParamType, Finder, &ParamMatches)) {
|
|
Result.addMatch(ParamMatches);
|
|
Matched = true;
|
|
continue;
|
|
}
|
|
}
|
|
if (expr(anyOf(cxxConstructExpr(hasDeclaration(cxxConstructorDecl(
|
|
hasParameter(ParamIndex, hasType(ParamMatcher))))),
|
|
callExpr(callee(functionDecl(
|
|
hasParameter(ParamIndex, hasType(ParamMatcher)))))))
|
|
.matches(Node, Finder, &ParamMatches)) {
|
|
Result.addMatch(ParamMatches);
|
|
Matched = true;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
*Builder = std::move(Result);
|
|
return Matched;
|
|
}
|
|
|
|
/// Matches the ParmVarDecl nodes that are at the N'th position in the parameter
|
|
/// list. The parameter list could be that of either a block, function, or
|
|
/// objc-method.
|
|
///
|
|
///
|
|
/// Given
|
|
///
|
|
/// \code
|
|
/// void f(int a, int b, int c) {
|
|
/// }
|
|
/// \endcode
|
|
///
|
|
/// ``parmVarDecl(isAtPosition(0))`` matches ``int a``.
|
|
///
|
|
/// ``parmVarDecl(isAtPosition(1))`` matches ``int b``.
|
|
AST_MATCHER_P(ParmVarDecl, isAtPosition, unsigned, N) {
|
|
const clang::DeclContext *Context = Node.getParentFunctionOrMethod();
|
|
|
|
if (const auto *Decl = dyn_cast_or_null<FunctionDecl>(Context))
|
|
return N < Decl->param_size() && Decl->getParamDecl(N) == &Node;
|
|
if (const auto *Decl = dyn_cast_or_null<BlockDecl>(Context))
|
|
return N < Decl->param_size() && Decl->getParamDecl(N) == &Node;
|
|
if (const auto *Decl = dyn_cast_or_null<ObjCMethodDecl>(Context))
|
|
return N < Decl->param_size() && Decl->getParamDecl(N) == &Node;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Matches any parameter of a function or an ObjC method declaration or a
|
|
/// block.
|
|
///
|
|
/// Does not match the 'this' parameter of a method.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class X { void f(int x, int y, int z) {} };
|
|
/// \endcode
|
|
/// cxxMethodDecl(hasAnyParameter(hasName("y")))
|
|
/// matches f(int x, int y, int z) {}
|
|
/// with hasAnyParameter(...)
|
|
/// matching int y
|
|
///
|
|
/// For ObjectiveC, given
|
|
/// \code
|
|
/// @interface I - (void) f:(int) y; @end
|
|
/// \endcode
|
|
//
|
|
/// the matcher objcMethodDecl(hasAnyParameter(hasName("y")))
|
|
/// matches the declaration of method f with hasParameter
|
|
/// matching y.
|
|
///
|
|
/// For blocks, given
|
|
/// \code
|
|
/// b = ^(int y) { printf("%d", y) };
|
|
/// \endcode
|
|
///
|
|
/// the matcher blockDecl(hasAnyParameter(hasName("y")))
|
|
/// matches the declaration of the block b with hasParameter
|
|
/// matching y.
|
|
AST_POLYMORPHIC_MATCHER_P(hasAnyParameter,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
|
|
ObjCMethodDecl,
|
|
BlockDecl),
|
|
internal::Matcher<ParmVarDecl>,
|
|
InnerMatcher) {
|
|
return matchesFirstInPointerRange(InnerMatcher, Node.param_begin(),
|
|
Node.param_end(), Finder,
|
|
Builder) != Node.param_end();
|
|
}
|
|
|
|
/// Matches \c FunctionDecls and \c FunctionProtoTypes that have a
|
|
/// specific parameter count.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void f(int i) {}
|
|
/// void g(int i, int j) {}
|
|
/// void h(int i, int j);
|
|
/// void j(int i);
|
|
/// void k(int x, int y, int z, ...);
|
|
/// \endcode
|
|
/// functionDecl(parameterCountIs(2))
|
|
/// matches \c g and \c h
|
|
/// functionProtoType(parameterCountIs(2))
|
|
/// matches \c g and \c h
|
|
/// functionProtoType(parameterCountIs(3))
|
|
/// matches \c k
|
|
AST_POLYMORPHIC_MATCHER_P(parameterCountIs,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
|
|
FunctionProtoType),
|
|
unsigned, N) {
|
|
return Node.getNumParams() == N;
|
|
}
|
|
|
|
/// Matches templateSpecializationType, class template specialization,
|
|
/// variable template specialization, and function template specialization
|
|
/// nodes where the template argument matches the inner matcher. This matcher
|
|
/// may produce multiple matches.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T, unsigned N, unsigned M>
|
|
/// struct Matrix {};
|
|
///
|
|
/// constexpr unsigned R = 2;
|
|
/// Matrix<int, R * 2, R * 4> M;
|
|
///
|
|
/// template <typename T, typename U>
|
|
/// void f(T&& t, U&& u) {}
|
|
///
|
|
/// bool B = false;
|
|
/// f(R, B);
|
|
/// \endcode
|
|
/// templateSpecializationType(forEachTemplateArgument(isExpr(expr())))
|
|
/// matches twice, with expr() matching 'R * 2' and 'R * 4'
|
|
/// functionDecl(forEachTemplateArgument(refersToType(builtinType())))
|
|
/// matches the specialization f<unsigned, bool> twice, for 'unsigned'
|
|
/// and 'bool'
|
|
AST_POLYMORPHIC_MATCHER_P(
|
|
forEachTemplateArgument,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
|
|
VarTemplateSpecializationDecl, FunctionDecl,
|
|
TemplateSpecializationType),
|
|
internal::Matcher<TemplateArgument>, InnerMatcher) {
|
|
ArrayRef<TemplateArgument> TemplateArgs =
|
|
clang::ast_matchers::internal::getTemplateSpecializationArgs(Node);
|
|
clang::ast_matchers::internal::BoundNodesTreeBuilder Result;
|
|
bool Matched = false;
|
|
for (const auto &Arg : TemplateArgs) {
|
|
clang::ast_matchers::internal::BoundNodesTreeBuilder ArgBuilder(*Builder);
|
|
if (InnerMatcher.matches(Arg, Finder, &ArgBuilder)) {
|
|
Matched = true;
|
|
Result.addMatch(ArgBuilder);
|
|
}
|
|
}
|
|
*Builder = std::move(Result);
|
|
return Matched;
|
|
}
|
|
|
|
/// Matches \c FunctionDecls that have a noreturn attribute.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void nope();
|
|
/// [[noreturn]] void a();
|
|
/// __attribute__((noreturn)) void b();
|
|
/// struct c { [[noreturn]] c(); };
|
|
/// \endcode
|
|
/// functionDecl(isNoReturn())
|
|
/// matches all of those except
|
|
/// \code
|
|
/// void nope();
|
|
/// \endcode
|
|
AST_MATCHER(FunctionDecl, isNoReturn) { return Node.isNoReturn(); }
|
|
|
|
/// Matches the return type of a function declaration.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// class X { int f() { return 1; } };
|
|
/// \endcode
|
|
/// cxxMethodDecl(returns(asString("int")))
|
|
/// matches int f() { return 1; }
|
|
AST_MATCHER_P(FunctionDecl, returns,
|
|
internal::Matcher<QualType>, InnerMatcher) {
|
|
return InnerMatcher.matches(Node.getReturnType(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches extern "C" function or variable declarations.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// extern "C" void f() {}
|
|
/// extern "C" { void g() {} }
|
|
/// void h() {}
|
|
/// extern "C" int x = 1;
|
|
/// extern "C" int y = 2;
|
|
/// int z = 3;
|
|
/// \endcode
|
|
/// functionDecl(isExternC())
|
|
/// matches the declaration of f and g, but not the declaration of h.
|
|
/// varDecl(isExternC())
|
|
/// matches the declaration of x and y, but not the declaration of z.
|
|
AST_POLYMORPHIC_MATCHER(isExternC, AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
|
|
VarDecl)) {
|
|
return Node.isExternC();
|
|
}
|
|
|
|
/// Matches variable/function declarations that have "static" storage
|
|
/// class specifier ("static" keyword) written in the source.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// static void f() {}
|
|
/// static int i = 0;
|
|
/// extern int j;
|
|
/// int k;
|
|
/// \endcode
|
|
/// functionDecl(isStaticStorageClass())
|
|
/// matches the function declaration f.
|
|
/// varDecl(isStaticStorageClass())
|
|
/// matches the variable declaration i.
|
|
AST_POLYMORPHIC_MATCHER(isStaticStorageClass,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
|
|
VarDecl)) {
|
|
return Node.getStorageClass() == SC_Static;
|
|
}
|
|
|
|
/// Matches deleted function declarations.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// void Func();
|
|
/// void DeletedFunc() = delete;
|
|
/// \endcode
|
|
/// functionDecl(isDeleted())
|
|
/// matches the declaration of DeletedFunc, but not Func.
|
|
AST_MATCHER(FunctionDecl, isDeleted) {
|
|
return Node.isDeleted();
|
|
}
|
|
|
|
/// Matches defaulted function declarations.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// class A { ~A(); };
|
|
/// class B { ~B() = default; };
|
|
/// \endcode
|
|
/// functionDecl(isDefaulted())
|
|
/// matches the declaration of ~B, but not ~A.
|
|
AST_MATCHER(FunctionDecl, isDefaulted) {
|
|
return Node.isDefaulted();
|
|
}
|
|
|
|
/// Matches weak function declarations.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// void foo() __attribute__((__weakref__("__foo")));
|
|
/// void bar();
|
|
/// \endcode
|
|
/// functionDecl(isWeak())
|
|
/// matches the weak declaration "foo", but not "bar".
|
|
AST_MATCHER(FunctionDecl, isWeak) { return Node.isWeak(); }
|
|
|
|
/// Matches functions that have a dynamic exception specification.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// void f();
|
|
/// void g() noexcept;
|
|
/// void h() noexcept(true);
|
|
/// void i() noexcept(false);
|
|
/// void j() throw();
|
|
/// void k() throw(int);
|
|
/// void l() throw(...);
|
|
/// \endcode
|
|
/// functionDecl(hasDynamicExceptionSpec()) and
|
|
/// functionProtoType(hasDynamicExceptionSpec())
|
|
/// match the declarations of j, k, and l, but not f, g, h, or i.
|
|
AST_POLYMORPHIC_MATCHER(hasDynamicExceptionSpec,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
|
|
FunctionProtoType)) {
|
|
if (const FunctionProtoType *FnTy = internal::getFunctionProtoType(Node))
|
|
return FnTy->hasDynamicExceptionSpec();
|
|
return false;
|
|
}
|
|
|
|
/// Matches functions that have a non-throwing exception specification.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// void f();
|
|
/// void g() noexcept;
|
|
/// void h() throw();
|
|
/// void i() throw(int);
|
|
/// void j() noexcept(false);
|
|
/// \endcode
|
|
/// functionDecl(isNoThrow()) and functionProtoType(isNoThrow())
|
|
/// match the declarations of g, and h, but not f, i or j.
|
|
AST_POLYMORPHIC_MATCHER(isNoThrow,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
|
|
FunctionProtoType)) {
|
|
const FunctionProtoType *FnTy = internal::getFunctionProtoType(Node);
|
|
|
|
// If the function does not have a prototype, then it is assumed to be a
|
|
// throwing function (as it would if the function did not have any exception
|
|
// specification).
|
|
if (!FnTy)
|
|
return false;
|
|
|
|
// Assume the best for any unresolved exception specification.
|
|
if (isUnresolvedExceptionSpec(FnTy->getExceptionSpecType()))
|
|
return true;
|
|
|
|
return FnTy->isNothrow();
|
|
}
|
|
|
|
/// Matches consteval function declarations and if consteval/if ! consteval
|
|
/// statements.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// consteval int a();
|
|
/// void b() { if consteval {} }
|
|
/// void c() { if ! consteval {} }
|
|
/// void d() { if ! consteval {} else {} }
|
|
/// \endcode
|
|
/// functionDecl(isConsteval())
|
|
/// matches the declaration of "int a()".
|
|
/// ifStmt(isConsteval())
|
|
/// matches the if statement in "void b()", "void c()", "void d()".
|
|
AST_POLYMORPHIC_MATCHER(isConsteval,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, IfStmt)) {
|
|
return Node.isConsteval();
|
|
}
|
|
|
|
/// Matches constexpr variable and function declarations,
|
|
/// and if constexpr.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// constexpr int foo = 42;
|
|
/// constexpr int bar();
|
|
/// void baz() { if constexpr(1 > 0) {} }
|
|
/// \endcode
|
|
/// varDecl(isConstexpr())
|
|
/// matches the declaration of foo.
|
|
/// functionDecl(isConstexpr())
|
|
/// matches the declaration of bar.
|
|
/// ifStmt(isConstexpr())
|
|
/// matches the if statement in baz.
|
|
AST_POLYMORPHIC_MATCHER(isConstexpr,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(VarDecl,
|
|
FunctionDecl,
|
|
IfStmt)) {
|
|
return Node.isConstexpr();
|
|
}
|
|
|
|
/// Matches constinit variable declarations.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// constinit int foo = 42;
|
|
/// constinit const char* bar = "bar";
|
|
/// int baz = 42;
|
|
/// [[clang::require_constant_initialization]] int xyz = 42;
|
|
/// \endcode
|
|
/// varDecl(isConstinit())
|
|
/// matches the declaration of `foo` and `bar`, but not `baz` and `xyz`.
|
|
AST_MATCHER(VarDecl, isConstinit) {
|
|
if (const auto *CIA = Node.getAttr<ConstInitAttr>())
|
|
return CIA->isConstinit();
|
|
return false;
|
|
}
|
|
|
|
/// Matches selection statements with initializer.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// void foo() {
|
|
/// if (int i = foobar(); i > 0) {}
|
|
/// switch (int i = foobar(); i) {}
|
|
/// for (auto& a = get_range(); auto& x : a) {}
|
|
/// }
|
|
/// void bar() {
|
|
/// if (foobar() > 0) {}
|
|
/// switch (foobar()) {}
|
|
/// for (auto& x : get_range()) {}
|
|
/// }
|
|
/// \endcode
|
|
/// ifStmt(hasInitStatement(anything()))
|
|
/// matches the if statement in foo but not in bar.
|
|
/// switchStmt(hasInitStatement(anything()))
|
|
/// matches the switch statement in foo but not in bar.
|
|
/// cxxForRangeStmt(hasInitStatement(anything()))
|
|
/// matches the range for statement in foo but not in bar.
|
|
AST_POLYMORPHIC_MATCHER_P(hasInitStatement,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(IfStmt, SwitchStmt,
|
|
CXXForRangeStmt),
|
|
internal::Matcher<Stmt>, InnerMatcher) {
|
|
const Stmt *Init = Node.getInit();
|
|
return Init != nullptr && InnerMatcher.matches(*Init, Finder, Builder);
|
|
}
|
|
|
|
/// Matches the condition expression of an if statement, for loop,
|
|
/// switch statement or conditional operator.
|
|
///
|
|
/// Example matches true (matcher = hasCondition(cxxBoolLiteral(equals(true))))
|
|
/// \code
|
|
/// if (true) {}
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER_P(
|
|
hasCondition,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(IfStmt, ForStmt, WhileStmt, DoStmt,
|
|
SwitchStmt, AbstractConditionalOperator),
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
const Expr *const Condition = Node.getCond();
|
|
return (Condition != nullptr &&
|
|
InnerMatcher.matches(*Condition, Finder, Builder));
|
|
}
|
|
|
|
/// Matches the then-statement of an if statement.
|
|
///
|
|
/// Examples matches the if statement
|
|
/// (matcher = ifStmt(hasThen(cxxBoolLiteral(equals(true)))))
|
|
/// \code
|
|
/// if (false) true; else false;
|
|
/// \endcode
|
|
AST_MATCHER_P(IfStmt, hasThen, internal::Matcher<Stmt>, InnerMatcher) {
|
|
const Stmt *const Then = Node.getThen();
|
|
return (Then != nullptr && InnerMatcher.matches(*Then, Finder, Builder));
|
|
}
|
|
|
|
/// Matches the else-statement of an if statement.
|
|
///
|
|
/// Examples matches the if statement
|
|
/// (matcher = ifStmt(hasElse(cxxBoolLiteral(equals(true)))))
|
|
/// \code
|
|
/// if (false) false; else true;
|
|
/// \endcode
|
|
AST_MATCHER_P(IfStmt, hasElse, internal::Matcher<Stmt>, InnerMatcher) {
|
|
const Stmt *const Else = Node.getElse();
|
|
return (Else != nullptr && InnerMatcher.matches(*Else, Finder, Builder));
|
|
}
|
|
|
|
/// Matches if a node equals a previously bound node.
|
|
///
|
|
/// Matches a node if it equals the node previously bound to \p ID.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class X { int a; int b; };
|
|
/// \endcode
|
|
/// cxxRecordDecl(
|
|
/// has(fieldDecl(hasName("a"), hasType(type().bind("t")))),
|
|
/// has(fieldDecl(hasName("b"), hasType(type(equalsBoundNode("t"))))))
|
|
/// matches the class \c X, as \c a and \c b have the same type.
|
|
///
|
|
/// Note that when multiple matches are involved via \c forEach* matchers,
|
|
/// \c equalsBoundNodes acts as a filter.
|
|
/// For example:
|
|
/// compoundStmt(
|
|
/// forEachDescendant(varDecl().bind("d")),
|
|
/// forEachDescendant(declRefExpr(to(decl(equalsBoundNode("d"))))))
|
|
/// will trigger a match for each combination of variable declaration
|
|
/// and reference to that variable declaration within a compound statement.
|
|
AST_POLYMORPHIC_MATCHER_P(equalsBoundNode,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(Stmt, Decl, Type,
|
|
QualType),
|
|
std::string, ID) {
|
|
// FIXME: Figure out whether it makes sense to allow this
|
|
// on any other node types.
|
|
// For *Loc it probably does not make sense, as those seem
|
|
// unique. For NestedNameSepcifier it might make sense, as
|
|
// those also have pointer identity, but I'm not sure whether
|
|
// they're ever reused.
|
|
internal::NotEqualsBoundNodePredicate Predicate;
|
|
Predicate.ID = ID;
|
|
Predicate.Node = DynTypedNode::create(Node);
|
|
return Builder->removeBindings(Predicate);
|
|
}
|
|
|
|
/// Matches the condition variable statement in an if statement.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// if (A* a = GetAPointer()) {}
|
|
/// \endcode
|
|
/// hasConditionVariableStatement(...)
|
|
/// matches 'A* a = GetAPointer()'.
|
|
AST_MATCHER_P(IfStmt, hasConditionVariableStatement,
|
|
internal::Matcher<DeclStmt>, InnerMatcher) {
|
|
const DeclStmt* const DeclarationStatement =
|
|
Node.getConditionVariableDeclStmt();
|
|
return DeclarationStatement != nullptr &&
|
|
InnerMatcher.matches(*DeclarationStatement, Finder, Builder);
|
|
}
|
|
|
|
/// Matches the index expression of an array subscript expression.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int i[5];
|
|
/// void f() { i[1] = 42; }
|
|
/// \endcode
|
|
/// arraySubscriptExpression(hasIndex(integerLiteral()))
|
|
/// matches \c i[1] with the \c integerLiteral() matching \c 1
|
|
AST_MATCHER_P(ArraySubscriptExpr, hasIndex,
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
if (const Expr* Expression = Node.getIdx())
|
|
return InnerMatcher.matches(*Expression, Finder, Builder);
|
|
return false;
|
|
}
|
|
|
|
/// Matches the base expression of an array subscript expression.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int i[5];
|
|
/// void f() { i[1] = 42; }
|
|
/// \endcode
|
|
/// arraySubscriptExpression(hasBase(implicitCastExpr(
|
|
/// hasSourceExpression(declRefExpr()))))
|
|
/// matches \c i[1] with the \c declRefExpr() matching \c i
|
|
AST_MATCHER_P(ArraySubscriptExpr, hasBase,
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
if (const Expr* Expression = Node.getBase())
|
|
return InnerMatcher.matches(*Expression, Finder, Builder);
|
|
return false;
|
|
}
|
|
|
|
/// Matches a 'for', 'while', 'while' statement or a function or coroutine
|
|
/// definition that has a given body. Note that in case of functions or
|
|
/// coroutines this matcher only matches the definition itself and not the
|
|
/// other declarations of the same function or coroutine.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// for (;;) {}
|
|
/// \endcode
|
|
/// forStmt(hasBody(compoundStmt()))
|
|
/// matches 'for (;;) {}'
|
|
/// with compoundStmt()
|
|
/// matching '{}'
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void f();
|
|
/// void f() {}
|
|
/// \endcode
|
|
/// functionDecl(hasBody(compoundStmt()))
|
|
/// matches 'void f() {}'
|
|
/// with compoundStmt()
|
|
/// matching '{}'
|
|
/// but does not match 'void f();'
|
|
AST_POLYMORPHIC_MATCHER_P(
|
|
hasBody,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(DoStmt, ForStmt, WhileStmt, CXXForRangeStmt,
|
|
FunctionDecl, CoroutineBodyStmt),
|
|
internal::Matcher<Stmt>, InnerMatcher) {
|
|
if (Finder->isTraversalIgnoringImplicitNodes() && isDefaultedHelper(&Node))
|
|
return false;
|
|
const Stmt *const Statement = internal::GetBodyMatcher<NodeType>::get(Node);
|
|
return (Statement != nullptr &&
|
|
InnerMatcher.matches(*Statement, Finder, Builder));
|
|
}
|
|
|
|
/// Matches a function declaration that has a given body present in the AST.
|
|
/// Note that this matcher matches all the declarations of a function whose
|
|
/// body is present in the AST.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void f();
|
|
/// void f() {}
|
|
/// void g();
|
|
/// \endcode
|
|
/// functionDecl(hasAnyBody(compoundStmt()))
|
|
/// matches both 'void f();'
|
|
/// and 'void f() {}'
|
|
/// with compoundStmt()
|
|
/// matching '{}'
|
|
/// but does not match 'void g();'
|
|
AST_MATCHER_P(FunctionDecl, hasAnyBody,
|
|
internal::Matcher<Stmt>, InnerMatcher) {
|
|
const Stmt *const Statement = Node.getBody();
|
|
return (Statement != nullptr &&
|
|
InnerMatcher.matches(*Statement, Finder, Builder));
|
|
}
|
|
|
|
|
|
/// Matches compound statements where at least one substatement matches
|
|
/// a given matcher. Also matches StmtExprs that have CompoundStmt as children.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// { {}; 1+2; }
|
|
/// \endcode
|
|
/// hasAnySubstatement(compoundStmt())
|
|
/// matches '{ {}; 1+2; }'
|
|
/// with compoundStmt()
|
|
/// matching '{}'
|
|
AST_POLYMORPHIC_MATCHER_P(hasAnySubstatement,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(CompoundStmt,
|
|
StmtExpr),
|
|
internal::Matcher<Stmt>, InnerMatcher) {
|
|
const CompoundStmt *CS = CompoundStmtMatcher<NodeType>::get(Node);
|
|
return CS && matchesFirstInPointerRange(InnerMatcher, CS->body_begin(),
|
|
CS->body_end(), Finder,
|
|
Builder) != CS->body_end();
|
|
}
|
|
|
|
/// Checks that a compound statement contains a specific number of
|
|
/// child statements.
|
|
///
|
|
/// Example: Given
|
|
/// \code
|
|
/// { for (;;) {} }
|
|
/// \endcode
|
|
/// compoundStmt(statementCountIs(0)))
|
|
/// matches '{}'
|
|
/// but does not match the outer compound statement.
|
|
AST_MATCHER_P(CompoundStmt, statementCountIs, unsigned, N) {
|
|
return Node.size() == N;
|
|
}
|
|
|
|
/// Matches literals that are equal to the given value of type ValueT.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// f('\0', false, 3.14, 42);
|
|
/// \endcode
|
|
/// characterLiteral(equals(0))
|
|
/// matches '\0'
|
|
/// cxxBoolLiteral(equals(false)) and cxxBoolLiteral(equals(0))
|
|
/// match false
|
|
/// floatLiteral(equals(3.14)) and floatLiteral(equals(314e-2))
|
|
/// match 3.14
|
|
/// integerLiteral(equals(42))
|
|
/// matches 42
|
|
///
|
|
/// Note that you cannot directly match a negative numeric literal because the
|
|
/// minus sign is not part of the literal: It is a unary operator whose operand
|
|
/// is the positive numeric literal. Instead, you must use a unaryOperator()
|
|
/// matcher to match the minus sign:
|
|
///
|
|
/// unaryOperator(hasOperatorName("-"),
|
|
/// hasUnaryOperand(integerLiteral(equals(13))))
|
|
///
|
|
/// Usable as: Matcher<CharacterLiteral>, Matcher<CXXBoolLiteralExpr>,
|
|
/// Matcher<FloatingLiteral>, Matcher<IntegerLiteral>
|
|
template <typename ValueT>
|
|
internal::PolymorphicMatcher<internal::ValueEqualsMatcher,
|
|
void(internal::AllNodeBaseTypes), ValueT>
|
|
equals(const ValueT &Value) {
|
|
return internal::PolymorphicMatcher<internal::ValueEqualsMatcher,
|
|
void(internal::AllNodeBaseTypes), ValueT>(
|
|
Value);
|
|
}
|
|
|
|
AST_POLYMORPHIC_MATCHER_P_OVERLOAD(equals,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(CharacterLiteral,
|
|
CXXBoolLiteralExpr,
|
|
IntegerLiteral),
|
|
bool, Value, 0) {
|
|
return internal::ValueEqualsMatcher<NodeType, ParamT>(Value)
|
|
.matchesNode(Node);
|
|
}
|
|
|
|
AST_POLYMORPHIC_MATCHER_P_OVERLOAD(equals,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(CharacterLiteral,
|
|
CXXBoolLiteralExpr,
|
|
IntegerLiteral),
|
|
unsigned, Value, 1) {
|
|
return internal::ValueEqualsMatcher<NodeType, ParamT>(Value)
|
|
.matchesNode(Node);
|
|
}
|
|
|
|
AST_POLYMORPHIC_MATCHER_P_OVERLOAD(equals,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(CharacterLiteral,
|
|
CXXBoolLiteralExpr,
|
|
FloatingLiteral,
|
|
IntegerLiteral),
|
|
double, Value, 2) {
|
|
return internal::ValueEqualsMatcher<NodeType, ParamT>(Value)
|
|
.matchesNode(Node);
|
|
}
|
|
|
|
/// Matches the operator Name of operator expressions and fold expressions
|
|
/// (binary or unary).
|
|
///
|
|
/// Example matches a || b (matcher = binaryOperator(hasOperatorName("||")))
|
|
/// \code
|
|
/// !(a || b)
|
|
/// \endcode
|
|
///
|
|
/// Example matches `(0 + ... + args)`
|
|
/// (matcher = cxxFoldExpr(hasOperatorName("+")))
|
|
/// \code
|
|
/// template <typename... Args>
|
|
/// auto sum(Args... args) {
|
|
/// return (0 + ... + args);
|
|
/// }
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER_P(
|
|
hasOperatorName,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
|
|
CXXRewrittenBinaryOperator, CXXFoldExpr,
|
|
UnaryOperator),
|
|
std::string, Name) {
|
|
if (std::optional<StringRef> OpName = internal::getOpName(Node))
|
|
return *OpName == Name;
|
|
return false;
|
|
}
|
|
|
|
/// Matches operator expressions (binary or unary) that have any of the
|
|
/// specified names.
|
|
///
|
|
/// hasAnyOperatorName("+", "-")
|
|
/// Is equivalent to
|
|
/// anyOf(hasOperatorName("+"), hasOperatorName("-"))
|
|
extern const internal::VariadicFunction<
|
|
internal::PolymorphicMatcher<internal::HasAnyOperatorNameMatcher,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(
|
|
BinaryOperator, CXXOperatorCallExpr,
|
|
CXXRewrittenBinaryOperator, UnaryOperator),
|
|
std::vector<std::string>>,
|
|
StringRef, internal::hasAnyOperatorNameFunc>
|
|
hasAnyOperatorName;
|
|
|
|
/// Matches all kinds of assignment operators.
|
|
///
|
|
/// Example 1: matches a += b (matcher = binaryOperator(isAssignmentOperator()))
|
|
/// \code
|
|
/// if (a == b)
|
|
/// a += b;
|
|
/// \endcode
|
|
///
|
|
/// Example 2: matches s1 = s2
|
|
/// (matcher = cxxOperatorCallExpr(isAssignmentOperator()))
|
|
/// \code
|
|
/// struct S { S& operator=(const S&); };
|
|
/// void x() { S s1, s2; s1 = s2; }
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER(
|
|
isAssignmentOperator,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
|
|
CXXRewrittenBinaryOperator)) {
|
|
return Node.isAssignmentOp();
|
|
}
|
|
|
|
/// Matches comparison operators.
|
|
///
|
|
/// Example 1: matches a == b (matcher = binaryOperator(isComparisonOperator()))
|
|
/// \code
|
|
/// if (a == b)
|
|
/// a += b;
|
|
/// \endcode
|
|
///
|
|
/// Example 2: matches s1 < s2
|
|
/// (matcher = cxxOperatorCallExpr(isComparisonOperator()))
|
|
/// \code
|
|
/// struct S { bool operator<(const S& other); };
|
|
/// void x(S s1, S s2) { bool b1 = s1 < s2; }
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER(
|
|
isComparisonOperator,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
|
|
CXXRewrittenBinaryOperator)) {
|
|
return Node.isComparisonOp();
|
|
}
|
|
|
|
/// Matches the left hand side of binary operator expressions.
|
|
///
|
|
/// Example matches a (matcher = binaryOperator(hasLHS()))
|
|
/// \code
|
|
/// a || b
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER_P(
|
|
hasLHS,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
|
|
CXXRewrittenBinaryOperator,
|
|
ArraySubscriptExpr, CXXFoldExpr),
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
const Expr *LeftHandSide = internal::getLHS(Node);
|
|
return (LeftHandSide != nullptr &&
|
|
InnerMatcher.matches(*LeftHandSide, Finder, Builder));
|
|
}
|
|
|
|
/// Matches the right hand side of binary operator expressions.
|
|
///
|
|
/// Example matches b (matcher = binaryOperator(hasRHS()))
|
|
/// \code
|
|
/// a || b
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER_P(
|
|
hasRHS,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
|
|
CXXRewrittenBinaryOperator,
|
|
ArraySubscriptExpr, CXXFoldExpr),
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
const Expr *RightHandSide = internal::getRHS(Node);
|
|
return (RightHandSide != nullptr &&
|
|
InnerMatcher.matches(*RightHandSide, Finder, Builder));
|
|
}
|
|
|
|
/// Matches if either the left hand side or the right hand side of a
|
|
/// binary operator or fold expression matches.
|
|
AST_POLYMORPHIC_MATCHER_P(
|
|
hasEitherOperand,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
|
|
CXXFoldExpr, CXXRewrittenBinaryOperator),
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()(
|
|
anyOf(hasLHS(InnerMatcher), hasRHS(InnerMatcher)))
|
|
.matches(Node, Finder, Builder);
|
|
}
|
|
|
|
/// Matches if both matchers match with opposite sides of the binary operator
|
|
/// or fold expression.
|
|
///
|
|
/// Example matcher = binaryOperator(hasOperands(integerLiteral(equals(1),
|
|
/// integerLiteral(equals(2)))
|
|
/// \code
|
|
/// 1 + 2 // Match
|
|
/// 2 + 1 // Match
|
|
/// 1 + 1 // No match
|
|
/// 2 + 2 // No match
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER_P2(
|
|
hasOperands,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
|
|
CXXFoldExpr, CXXRewrittenBinaryOperator),
|
|
internal::Matcher<Expr>, Matcher1, internal::Matcher<Expr>, Matcher2) {
|
|
return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()(
|
|
anyOf(allOf(hasLHS(Matcher1), hasRHS(Matcher2)),
|
|
allOf(hasRHS(Matcher1), hasLHS(Matcher2))))
|
|
.matches(Node, Finder, Builder);
|
|
}
|
|
|
|
/// Matches if the operand of a unary operator matches.
|
|
///
|
|
/// Example matches true (matcher = hasUnaryOperand(
|
|
/// cxxBoolLiteral(equals(true))))
|
|
/// \code
|
|
/// !true
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER_P(hasUnaryOperand,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(UnaryOperator,
|
|
CXXOperatorCallExpr),
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
const Expr *const Operand = internal::getSubExpr(Node);
|
|
return (Operand != nullptr &&
|
|
InnerMatcher.matches(*Operand, Finder, Builder));
|
|
}
|
|
|
|
/// Matches if the cast's source expression
|
|
/// or opaque value's source expression matches the given matcher.
|
|
///
|
|
/// Example 1: matches "a string"
|
|
/// (matcher = castExpr(hasSourceExpression(cxxConstructExpr())))
|
|
/// \code
|
|
/// class URL { URL(string); };
|
|
/// URL url = "a string";
|
|
/// \endcode
|
|
///
|
|
/// Example 2: matches 'b' (matcher =
|
|
/// opaqueValueExpr(hasSourceExpression(implicitCastExpr(declRefExpr())))
|
|
/// \code
|
|
/// int a = b ?: 1;
|
|
/// \endcode
|
|
AST_POLYMORPHIC_MATCHER_P(hasSourceExpression,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(CastExpr,
|
|
OpaqueValueExpr),
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
const Expr *const SubExpression =
|
|
internal::GetSourceExpressionMatcher<NodeType>::get(Node);
|
|
return (SubExpression != nullptr &&
|
|
InnerMatcher.matches(*SubExpression, Finder, Builder));
|
|
}
|
|
|
|
/// Matches casts that has a given cast kind.
|
|
///
|
|
/// Example: matches the implicit cast around \c 0
|
|
/// (matcher = castExpr(hasCastKind(CK_NullToPointer)))
|
|
/// \code
|
|
/// int *p = 0;
|
|
/// \endcode
|
|
///
|
|
/// If the matcher is use from clang-query, CastKind parameter
|
|
/// should be passed as a quoted string. e.g., hasCastKind("CK_NullToPointer").
|
|
AST_MATCHER_P(CastExpr, hasCastKind, CastKind, Kind) {
|
|
return Node.getCastKind() == Kind;
|
|
}
|
|
|
|
/// Matches casts whose destination type matches a given matcher.
|
|
///
|
|
/// (Note: Clang's AST refers to other conversions as "casts" too, and calls
|
|
/// actual casts "explicit" casts.)
|
|
AST_MATCHER_P(ExplicitCastExpr, hasDestinationType,
|
|
internal::Matcher<QualType>, InnerMatcher) {
|
|
const QualType NodeType = Node.getTypeAsWritten();
|
|
return InnerMatcher.matches(NodeType, Finder, Builder);
|
|
}
|
|
|
|
/// Matches implicit casts whose destination type matches a given
|
|
/// matcher.
|
|
AST_MATCHER_P(ImplicitCastExpr, hasImplicitDestinationType,
|
|
internal::Matcher<QualType>, InnerMatcher) {
|
|
return InnerMatcher.matches(Node.getType(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches TagDecl object that are spelled with "struct."
|
|
///
|
|
/// Example matches S, but not C, U or E.
|
|
/// \code
|
|
/// struct S {};
|
|
/// class C {};
|
|
/// union U {};
|
|
/// enum E {};
|
|
/// \endcode
|
|
AST_MATCHER(TagDecl, isStruct) {
|
|
return Node.isStruct();
|
|
}
|
|
|
|
/// Matches TagDecl object that are spelled with "union."
|
|
///
|
|
/// Example matches U, but not C, S or E.
|
|
/// \code
|
|
/// struct S {};
|
|
/// class C {};
|
|
/// union U {};
|
|
/// enum E {};
|
|
/// \endcode
|
|
AST_MATCHER(TagDecl, isUnion) {
|
|
return Node.isUnion();
|
|
}
|
|
|
|
/// Matches TagDecl object that are spelled with "class."
|
|
///
|
|
/// Example matches C, but not S, U or E.
|
|
/// \code
|
|
/// struct S {};
|
|
/// class C {};
|
|
/// union U {};
|
|
/// enum E {};
|
|
/// \endcode
|
|
AST_MATCHER(TagDecl, isClass) {
|
|
return Node.isClass();
|
|
}
|
|
|
|
/// Matches TagDecl object that are spelled with "enum."
|
|
///
|
|
/// Example matches E, but not C, S or U.
|
|
/// \code
|
|
/// struct S {};
|
|
/// class C {};
|
|
/// union U {};
|
|
/// enum E {};
|
|
/// \endcode
|
|
AST_MATCHER(TagDecl, isEnum) {
|
|
return Node.isEnum();
|
|
}
|
|
|
|
/// Matches the true branch expression of a conditional operator.
|
|
///
|
|
/// Example 1 (conditional ternary operator): matches a
|
|
/// \code
|
|
/// condition ? a : b
|
|
/// \endcode
|
|
///
|
|
/// Example 2 (conditional binary operator): matches opaqueValueExpr(condition)
|
|
/// \code
|
|
/// condition ?: b
|
|
/// \endcode
|
|
AST_MATCHER_P(AbstractConditionalOperator, hasTrueExpression,
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
const Expr *Expression = Node.getTrueExpr();
|
|
return (Expression != nullptr &&
|
|
InnerMatcher.matches(*Expression, Finder, Builder));
|
|
}
|
|
|
|
/// Matches the false branch expression of a conditional operator
|
|
/// (binary or ternary).
|
|
///
|
|
/// Example matches b
|
|
/// \code
|
|
/// condition ? a : b
|
|
/// condition ?: b
|
|
/// \endcode
|
|
AST_MATCHER_P(AbstractConditionalOperator, hasFalseExpression,
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
const Expr *Expression = Node.getFalseExpr();
|
|
return (Expression != nullptr &&
|
|
InnerMatcher.matches(*Expression, Finder, Builder));
|
|
}
|
|
|
|
/// Matches if a declaration has a body attached.
|
|
///
|
|
/// Example matches A, va, fa
|
|
/// \code
|
|
/// class A {};
|
|
/// class B; // Doesn't match, as it has no body.
|
|
/// int va;
|
|
/// extern int vb; // Doesn't match, as it doesn't define the variable.
|
|
/// void fa() {}
|
|
/// void fb(); // Doesn't match, as it has no body.
|
|
/// @interface X
|
|
/// - (void)ma; // Doesn't match, interface is declaration.
|
|
/// @end
|
|
/// @implementation X
|
|
/// - (void)ma {}
|
|
/// @end
|
|
/// \endcode
|
|
///
|
|
/// Usable as: Matcher<TagDecl>, Matcher<VarDecl>, Matcher<FunctionDecl>,
|
|
/// Matcher<ObjCMethodDecl>
|
|
AST_POLYMORPHIC_MATCHER(isDefinition,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(TagDecl, VarDecl,
|
|
ObjCMethodDecl,
|
|
FunctionDecl)) {
|
|
return Node.isThisDeclarationADefinition();
|
|
}
|
|
|
|
/// Matches if a function declaration is variadic.
|
|
///
|
|
/// Example matches f, but not g or h. The function i will not match, even when
|
|
/// compiled in C mode.
|
|
/// \code
|
|
/// void f(...);
|
|
/// void g(int);
|
|
/// template <typename... Ts> void h(Ts...);
|
|
/// void i();
|
|
/// \endcode
|
|
AST_MATCHER(FunctionDecl, isVariadic) {
|
|
return Node.isVariadic();
|
|
}
|
|
|
|
/// Matches the class declaration that the given method declaration
|
|
/// belongs to.
|
|
///
|
|
/// FIXME: Generalize this for other kinds of declarations.
|
|
/// FIXME: What other kind of declarations would we need to generalize
|
|
/// this to?
|
|
///
|
|
/// Example matches A() in the last line
|
|
/// (matcher = cxxConstructExpr(hasDeclaration(cxxMethodDecl(
|
|
/// ofClass(hasName("A"))))))
|
|
/// \code
|
|
/// class A {
|
|
/// public:
|
|
/// A();
|
|
/// };
|
|
/// A a = A();
|
|
/// \endcode
|
|
AST_MATCHER_P(CXXMethodDecl, ofClass,
|
|
internal::Matcher<CXXRecordDecl>, InnerMatcher) {
|
|
|
|
ASTChildrenNotSpelledInSourceScope RAII(Finder, false);
|
|
|
|
const CXXRecordDecl *Parent = Node.getParent();
|
|
return (Parent != nullptr &&
|
|
InnerMatcher.matches(*Parent, Finder, Builder));
|
|
}
|
|
|
|
/// Matches each method overridden by the given method. This matcher may
|
|
/// produce multiple matches.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class A { virtual void f(); };
|
|
/// class B : public A { void f(); };
|
|
/// class C : public B { void f(); };
|
|
/// \endcode
|
|
/// cxxMethodDecl(ofClass(hasName("C")),
|
|
/// forEachOverridden(cxxMethodDecl().bind("b"))).bind("d")
|
|
/// matches once, with "b" binding "A::f" and "d" binding "C::f" (Note
|
|
/// that B::f is not overridden by C::f).
|
|
///
|
|
/// The check can produce multiple matches in case of multiple inheritance, e.g.
|
|
/// \code
|
|
/// class A1 { virtual void f(); };
|
|
/// class A2 { virtual void f(); };
|
|
/// class C : public A1, public A2 { void f(); };
|
|
/// \endcode
|
|
/// cxxMethodDecl(ofClass(hasName("C")),
|
|
/// forEachOverridden(cxxMethodDecl().bind("b"))).bind("d")
|
|
/// matches twice, once with "b" binding "A1::f" and "d" binding "C::f", and
|
|
/// once with "b" binding "A2::f" and "d" binding "C::f".
|
|
AST_MATCHER_P(CXXMethodDecl, forEachOverridden,
|
|
internal::Matcher<CXXMethodDecl>, InnerMatcher) {
|
|
BoundNodesTreeBuilder Result;
|
|
bool Matched = false;
|
|
for (const auto *Overridden : Node.overridden_methods()) {
|
|
BoundNodesTreeBuilder OverriddenBuilder(*Builder);
|
|
const bool OverriddenMatched =
|
|
InnerMatcher.matches(*Overridden, Finder, &OverriddenBuilder);
|
|
if (OverriddenMatched) {
|
|
Matched = true;
|
|
Result.addMatch(OverriddenBuilder);
|
|
}
|
|
}
|
|
*Builder = std::move(Result);
|
|
return Matched;
|
|
}
|
|
|
|
/// Matches declarations of virtual methods and C++ base specifers that specify
|
|
/// virtual inheritance.
|
|
///
|
|
/// Example:
|
|
/// \code
|
|
/// class A {
|
|
/// public:
|
|
/// virtual void x(); // matches x
|
|
/// };
|
|
/// \endcode
|
|
///
|
|
/// Example:
|
|
/// \code
|
|
/// class Base {};
|
|
/// class DirectlyDerived : virtual Base {}; // matches Base
|
|
/// class IndirectlyDerived : DirectlyDerived, Base {}; // matches Base
|
|
/// \endcode
|
|
///
|
|
/// Usable as: Matcher<CXXMethodDecl>, Matcher<CXXBaseSpecifier>
|
|
AST_POLYMORPHIC_MATCHER(isVirtual,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(CXXMethodDecl,
|
|
CXXBaseSpecifier)) {
|
|
return Node.isVirtual();
|
|
}
|
|
|
|
/// Matches if the given method declaration has an explicit "virtual".
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class A {
|
|
/// public:
|
|
/// virtual void x();
|
|
/// };
|
|
/// class B : public A {
|
|
/// public:
|
|
/// void x();
|
|
/// };
|
|
/// \endcode
|
|
/// matches A::x but not B::x
|
|
AST_MATCHER(CXXMethodDecl, isVirtualAsWritten) {
|
|
return Node.isVirtualAsWritten();
|
|
}
|
|
|
|
AST_MATCHER(CXXConstructorDecl, isInheritingConstructor) {
|
|
return Node.isInheritingConstructor();
|
|
}
|
|
|
|
/// Matches if the given method or class declaration is final.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// class A final {};
|
|
///
|
|
/// struct B {
|
|
/// virtual void f();
|
|
/// };
|
|
///
|
|
/// struct C : B {
|
|
/// void f() final;
|
|
/// };
|
|
/// \endcode
|
|
/// matches A and C::f, but not B, C, or B::f
|
|
AST_POLYMORPHIC_MATCHER(isFinal,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl,
|
|
CXXMethodDecl)) {
|
|
return Node.template hasAttr<FinalAttr>();
|
|
}
|
|
|
|
/// Matches if the given method declaration is pure.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class A {
|
|
/// public:
|
|
/// virtual void x() = 0;
|
|
/// };
|
|
/// \endcode
|
|
/// matches A::x
|
|
AST_MATCHER(CXXMethodDecl, isPure) { return Node.isPureVirtual(); }
|
|
|
|
/// Matches if the given method declaration is const.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct A {
|
|
/// void foo() const;
|
|
/// void bar();
|
|
/// };
|
|
/// \endcode
|
|
///
|
|
/// cxxMethodDecl(isConst()) matches A::foo() but not A::bar()
|
|
AST_MATCHER(CXXMethodDecl, isConst) {
|
|
return Node.isConst();
|
|
}
|
|
|
|
/// Matches if the given method declaration declares a copy assignment
|
|
/// operator.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct A {
|
|
/// A &operator=(const A &);
|
|
/// A &operator=(A &&);
|
|
/// };
|
|
/// \endcode
|
|
///
|
|
/// cxxMethodDecl(isCopyAssignmentOperator()) matches the first method but not
|
|
/// the second one.
|
|
AST_MATCHER(CXXMethodDecl, isCopyAssignmentOperator) {
|
|
return Node.isCopyAssignmentOperator();
|
|
}
|
|
|
|
/// Matches if the given method declaration declares a move assignment
|
|
/// operator.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct A {
|
|
/// A &operator=(const A &);
|
|
/// A &operator=(A &&);
|
|
/// };
|
|
/// \endcode
|
|
///
|
|
/// cxxMethodDecl(isMoveAssignmentOperator()) matches the second method but not
|
|
/// the first one.
|
|
AST_MATCHER(CXXMethodDecl, isMoveAssignmentOperator) {
|
|
return Node.isMoveAssignmentOperator();
|
|
}
|
|
|
|
/// Matches if the given method declaration overrides another method.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class A {
|
|
/// public:
|
|
/// virtual void x();
|
|
/// };
|
|
/// class B : public A {
|
|
/// public:
|
|
/// virtual void x();
|
|
/// };
|
|
/// \endcode
|
|
/// matches B::x
|
|
AST_MATCHER(CXXMethodDecl, isOverride) {
|
|
return Node.size_overridden_methods() > 0 || Node.hasAttr<OverrideAttr>();
|
|
}
|
|
|
|
/// Matches method declarations that are user-provided.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct S {
|
|
/// S(); // #1
|
|
/// S(const S &) = default; // #2
|
|
/// S(S &&) = delete; // #3
|
|
/// };
|
|
/// \endcode
|
|
/// cxxConstructorDecl(isUserProvided()) will match #1, but not #2 or #3.
|
|
AST_MATCHER(CXXMethodDecl, isUserProvided) {
|
|
return Node.isUserProvided();
|
|
}
|
|
|
|
/// Matches member expressions that are called with '->' as opposed
|
|
/// to '.'.
|
|
///
|
|
/// Member calls on the implicit this pointer match as called with '->'.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class Y {
|
|
/// void x() { this->x(); x(); Y y; y.x(); a; this->b; Y::b; }
|
|
/// template <class T> void f() { this->f<T>(); f<T>(); }
|
|
/// int a;
|
|
/// static int b;
|
|
/// };
|
|
/// template <class T>
|
|
/// class Z {
|
|
/// void x() { this->m; }
|
|
/// };
|
|
/// \endcode
|
|
/// memberExpr(isArrow())
|
|
/// matches this->x, x, y.x, a, this->b
|
|
/// cxxDependentScopeMemberExpr(isArrow())
|
|
/// matches this->m
|
|
/// unresolvedMemberExpr(isArrow())
|
|
/// matches this->f<T>, f<T>
|
|
AST_POLYMORPHIC_MATCHER(
|
|
isArrow, AST_POLYMORPHIC_SUPPORTED_TYPES(MemberExpr, UnresolvedMemberExpr,
|
|
CXXDependentScopeMemberExpr)) {
|
|
return Node.isArrow();
|
|
}
|
|
|
|
/// Matches QualType nodes that are of integer type.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void a(int);
|
|
/// void b(long);
|
|
/// void c(double);
|
|
/// \endcode
|
|
/// functionDecl(hasAnyParameter(hasType(isInteger())))
|
|
/// matches "a(int)", "b(long)", but not "c(double)".
|
|
AST_MATCHER(QualType, isInteger) {
|
|
return Node->isIntegerType();
|
|
}
|
|
|
|
/// Matches QualType nodes that are of unsigned integer type.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void a(int);
|
|
/// void b(unsigned long);
|
|
/// void c(double);
|
|
/// \endcode
|
|
/// functionDecl(hasAnyParameter(hasType(isUnsignedInteger())))
|
|
/// matches "b(unsigned long)", but not "a(int)" and "c(double)".
|
|
AST_MATCHER(QualType, isUnsignedInteger) {
|
|
return Node->isUnsignedIntegerType();
|
|
}
|
|
|
|
/// Matches QualType nodes that are of signed integer type.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void a(int);
|
|
/// void b(unsigned long);
|
|
/// void c(double);
|
|
/// \endcode
|
|
/// functionDecl(hasAnyParameter(hasType(isSignedInteger())))
|
|
/// matches "a(int)", but not "b(unsigned long)" and "c(double)".
|
|
AST_MATCHER(QualType, isSignedInteger) {
|
|
return Node->isSignedIntegerType();
|
|
}
|
|
|
|
/// Matches QualType nodes that are of character type.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void a(char);
|
|
/// void b(wchar_t);
|
|
/// void c(double);
|
|
/// \endcode
|
|
/// functionDecl(hasAnyParameter(hasType(isAnyCharacter())))
|
|
/// matches "a(char)", "b(wchar_t)", but not "c(double)".
|
|
AST_MATCHER(QualType, isAnyCharacter) {
|
|
return Node->isAnyCharacterType();
|
|
}
|
|
|
|
/// Matches QualType nodes that are of any pointer type; this includes
|
|
/// the Objective-C object pointer type, which is different despite being
|
|
/// syntactically similar.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int *i = nullptr;
|
|
///
|
|
/// @interface Foo
|
|
/// @end
|
|
/// Foo *f;
|
|
///
|
|
/// int j;
|
|
/// \endcode
|
|
/// varDecl(hasType(isAnyPointer()))
|
|
/// matches "int *i" and "Foo *f", but not "int j".
|
|
AST_MATCHER(QualType, isAnyPointer) {
|
|
return Node->isAnyPointerType();
|
|
}
|
|
|
|
/// Matches QualType nodes that are const-qualified, i.e., that
|
|
/// include "top-level" const.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void a(int);
|
|
/// void b(int const);
|
|
/// void c(const int);
|
|
/// void d(const int*);
|
|
/// void e(int const) {};
|
|
/// \endcode
|
|
/// functionDecl(hasAnyParameter(hasType(isConstQualified())))
|
|
/// matches "void b(int const)", "void c(const int)" and
|
|
/// "void e(int const) {}". It does not match d as there
|
|
/// is no top-level const on the parameter type "const int *".
|
|
AST_MATCHER(QualType, isConstQualified) {
|
|
return Node.isConstQualified();
|
|
}
|
|
|
|
/// Matches QualType nodes that are volatile-qualified, i.e., that
|
|
/// include "top-level" volatile.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void a(int);
|
|
/// void b(int volatile);
|
|
/// void c(volatile int);
|
|
/// void d(volatile int*);
|
|
/// void e(int volatile) {};
|
|
/// \endcode
|
|
/// functionDecl(hasAnyParameter(hasType(isVolatileQualified())))
|
|
/// matches "void b(int volatile)", "void c(volatile int)" and
|
|
/// "void e(int volatile) {}". It does not match d as there
|
|
/// is no top-level volatile on the parameter type "volatile int *".
|
|
AST_MATCHER(QualType, isVolatileQualified) {
|
|
return Node.isVolatileQualified();
|
|
}
|
|
|
|
/// Matches QualType nodes that have local CV-qualifiers attached to
|
|
/// the node, not hidden within a typedef.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// typedef const int const_int;
|
|
/// const_int i;
|
|
/// int *const j;
|
|
/// int *volatile k;
|
|
/// int m;
|
|
/// \endcode
|
|
/// \c varDecl(hasType(hasLocalQualifiers())) matches only \c j and \c k.
|
|
/// \c i is const-qualified but the qualifier is not local.
|
|
AST_MATCHER(QualType, hasLocalQualifiers) {
|
|
return Node.hasLocalQualifiers();
|
|
}
|
|
|
|
/// Matches a member expression where the member is matched by a
|
|
/// given matcher.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct { int first, second; } first, second;
|
|
/// int i(second.first);
|
|
/// int j(first.second);
|
|
/// \endcode
|
|
/// memberExpr(member(hasName("first")))
|
|
/// matches second.first
|
|
/// but not first.second (because the member name there is "second").
|
|
AST_MATCHER_P(MemberExpr, member,
|
|
internal::Matcher<ValueDecl>, InnerMatcher) {
|
|
return InnerMatcher.matches(*Node.getMemberDecl(), Finder, Builder);
|
|
}
|
|
|
|
/// 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;
|
|
/// int f(X x) { x.m; return m; }
|
|
/// };
|
|
/// \endcode
|
|
/// 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,
|
|
CXXDependentScopeMemberExpr),
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
if (const auto *E = dyn_cast<UnresolvedMemberExpr>(&Node))
|
|
if (E->isImplicitAccess())
|
|
return false;
|
|
if (const auto *E = dyn_cast<CXXDependentScopeMemberExpr>(&Node))
|
|
if (E->isImplicitAccess())
|
|
return false;
|
|
return InnerMatcher.matches(*Node.getBase(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches any using shadow declaration.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace X { void b(); }
|
|
/// using X::b;
|
|
/// \endcode
|
|
/// usingDecl(hasAnyUsingShadowDecl(hasName("b"))))
|
|
/// matches \code using X::b \endcode
|
|
AST_MATCHER_P(BaseUsingDecl, hasAnyUsingShadowDecl,
|
|
internal::Matcher<UsingShadowDecl>, InnerMatcher) {
|
|
return matchesFirstInPointerRange(InnerMatcher, Node.shadow_begin(),
|
|
Node.shadow_end(), Finder,
|
|
Builder) != Node.shadow_end();
|
|
}
|
|
|
|
/// Matches a using shadow declaration where the target declaration is
|
|
/// matched by the given matcher.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace X { int a; void b(); }
|
|
/// using X::a;
|
|
/// using X::b;
|
|
/// \endcode
|
|
/// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(functionDecl())))
|
|
/// matches \code using X::b \endcode
|
|
/// but not \code using X::a \endcode
|
|
AST_MATCHER_P(UsingShadowDecl, hasTargetDecl,
|
|
internal::Matcher<NamedDecl>, InnerMatcher) {
|
|
return InnerMatcher.matches(*Node.getTargetDecl(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches template instantiations of function, class, or static
|
|
/// member variable template instantiations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T> class X {}; class A {}; X<A> x;
|
|
/// \endcode
|
|
/// or
|
|
/// \code
|
|
/// template <typename T> class X {}; class A {}; template class X<A>;
|
|
/// \endcode
|
|
/// or
|
|
/// \code
|
|
/// template <typename T> class X {}; class A {}; extern template class X<A>;
|
|
/// \endcode
|
|
/// cxxRecordDecl(hasName("::X"), isTemplateInstantiation())
|
|
/// matches the template instantiation of X<A>.
|
|
///
|
|
/// But given
|
|
/// \code
|
|
/// template <typename T> class X {}; class A {};
|
|
/// template <> class X<A> {}; X<A> x;
|
|
/// \endcode
|
|
/// cxxRecordDecl(hasName("::X"), isTemplateInstantiation())
|
|
/// does not match, as X<A> is an explicit template specialization.
|
|
///
|
|
/// Usable as: Matcher<FunctionDecl>, Matcher<VarDecl>, Matcher<CXXRecordDecl>
|
|
AST_POLYMORPHIC_MATCHER(isTemplateInstantiation,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, VarDecl,
|
|
CXXRecordDecl)) {
|
|
return (Node.getTemplateSpecializationKind() == TSK_ImplicitInstantiation ||
|
|
Node.getTemplateSpecializationKind() ==
|
|
TSK_ExplicitInstantiationDefinition ||
|
|
Node.getTemplateSpecializationKind() ==
|
|
TSK_ExplicitInstantiationDeclaration);
|
|
}
|
|
|
|
/// Matches declarations that are template instantiations or are inside
|
|
/// template instantiations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<typename T> void A(T t) { T i; }
|
|
/// A(0);
|
|
/// A(0U);
|
|
/// \endcode
|
|
/// functionDecl(isInstantiated())
|
|
/// matches 'A(int) {...};' and 'A(unsigned) {...}'.
|
|
AST_MATCHER_FUNCTION(internal::Matcher<Decl>, isInstantiated) {
|
|
auto IsInstantiation = decl(anyOf(cxxRecordDecl(isTemplateInstantiation()),
|
|
functionDecl(isTemplateInstantiation()),
|
|
varDecl(isTemplateInstantiation())));
|
|
return decl(anyOf(IsInstantiation, hasAncestor(IsInstantiation)));
|
|
}
|
|
|
|
/// Matches statements inside of a template instantiation.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int j;
|
|
/// template<typename T> void A(T t) { T i; j += 42;}
|
|
/// A(0);
|
|
/// A(0U);
|
|
/// \endcode
|
|
/// declStmt(isInTemplateInstantiation())
|
|
/// matches 'int i;' and 'unsigned i'.
|
|
/// unless(stmt(isInTemplateInstantiation()))
|
|
/// will NOT match j += 42; as it's shared between the template definition and
|
|
/// instantiation.
|
|
AST_MATCHER_FUNCTION(internal::Matcher<Stmt>, isInTemplateInstantiation) {
|
|
return stmt(hasAncestor(decl(anyOf(cxxRecordDecl(isTemplateInstantiation()),
|
|
functionDecl(isTemplateInstantiation()),
|
|
varDecl(isTemplateInstantiation())))));
|
|
}
|
|
|
|
/// Matches explicit template specializations of function, class, or
|
|
/// static member variable template instantiations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<typename T> void A(T t) { }
|
|
/// template<> void A(int N) { }
|
|
/// \endcode
|
|
/// functionDecl(isExplicitTemplateSpecialization())
|
|
/// matches the specialization A<int>().
|
|
///
|
|
/// Usable as: Matcher<FunctionDecl>, Matcher<VarDecl>, Matcher<CXXRecordDecl>
|
|
AST_POLYMORPHIC_MATCHER(isExplicitTemplateSpecialization,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, VarDecl,
|
|
CXXRecordDecl)) {
|
|
return (Node.getTemplateSpecializationKind() == TSK_ExplicitSpecialization);
|
|
}
|
|
|
|
/// Matches \c TypeLocs for which the given inner
|
|
/// QualType-matcher matches.
|
|
AST_MATCHER_FUNCTION_P_OVERLOAD(internal::BindableMatcher<TypeLoc>, loc,
|
|
internal::Matcher<QualType>, InnerMatcher, 0) {
|
|
return internal::BindableMatcher<TypeLoc>(
|
|
new internal::TypeLocTypeMatcher(InnerMatcher));
|
|
}
|
|
|
|
/// Matches `QualifiedTypeLoc`s in the clang AST.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// const int x = 0;
|
|
/// \endcode
|
|
/// qualifiedTypeLoc()
|
|
/// matches `const int`.
|
|
extern const internal::VariadicDynCastAllOfMatcher<TypeLoc, QualifiedTypeLoc>
|
|
qualifiedTypeLoc;
|
|
|
|
/// Matches `QualifiedTypeLoc`s that have an unqualified `TypeLoc` matching
|
|
/// `InnerMatcher`.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int* const x;
|
|
/// const int y;
|
|
/// \endcode
|
|
/// qualifiedTypeLoc(hasUnqualifiedLoc(pointerTypeLoc()))
|
|
/// matches the `TypeLoc` of the variable declaration of `x`, but not `y`.
|
|
AST_MATCHER_P(QualifiedTypeLoc, hasUnqualifiedLoc, internal::Matcher<TypeLoc>,
|
|
InnerMatcher) {
|
|
return InnerMatcher.matches(Node.getUnqualifiedLoc(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches a function declared with the specified return `TypeLoc`.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int f() { return 5; }
|
|
/// void g() {}
|
|
/// \endcode
|
|
/// functionDecl(hasReturnTypeLoc(loc(asString("int"))))
|
|
/// matches the declaration of `f`, but not `g`.
|
|
AST_MATCHER_P(FunctionDecl, hasReturnTypeLoc, internal::Matcher<TypeLoc>,
|
|
ReturnMatcher) {
|
|
auto Loc = Node.getFunctionTypeLoc();
|
|
return Loc && ReturnMatcher.matches(Loc.getReturnLoc(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches pointer `TypeLoc`s.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int* x;
|
|
/// \endcode
|
|
/// pointerTypeLoc()
|
|
/// matches `int*`.
|
|
extern const internal::VariadicDynCastAllOfMatcher<TypeLoc, PointerTypeLoc>
|
|
pointerTypeLoc;
|
|
|
|
/// Matches pointer `TypeLoc`s that have a pointee `TypeLoc` matching
|
|
/// `PointeeMatcher`.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int* x;
|
|
/// \endcode
|
|
/// pointerTypeLoc(hasPointeeLoc(loc(asString("int"))))
|
|
/// matches `int*`.
|
|
AST_MATCHER_P(PointerTypeLoc, hasPointeeLoc, internal::Matcher<TypeLoc>,
|
|
PointeeMatcher) {
|
|
return PointeeMatcher.matches(Node.getPointeeLoc(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches reference `TypeLoc`s.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int x = 3;
|
|
/// int& l = x;
|
|
/// int&& r = 3;
|
|
/// \endcode
|
|
/// referenceTypeLoc()
|
|
/// matches `int&` and `int&&`.
|
|
extern const internal::VariadicDynCastAllOfMatcher<TypeLoc, ReferenceTypeLoc>
|
|
referenceTypeLoc;
|
|
|
|
/// Matches reference `TypeLoc`s that have a referent `TypeLoc` matching
|
|
/// `ReferentMatcher`.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int x = 3;
|
|
/// int& xx = x;
|
|
/// \endcode
|
|
/// referenceTypeLoc(hasReferentLoc(loc(asString("int"))))
|
|
/// matches `int&`.
|
|
AST_MATCHER_P(ReferenceTypeLoc, hasReferentLoc, internal::Matcher<TypeLoc>,
|
|
ReferentMatcher) {
|
|
return ReferentMatcher.matches(Node.getPointeeLoc(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches template specialization `TypeLoc`s.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T> class C {};
|
|
/// C<char> var;
|
|
/// \endcode
|
|
/// varDecl(hasTypeLoc(templateSpecializationTypeLoc(typeLoc())))
|
|
/// matches `C<char> var`.
|
|
extern const internal::VariadicDynCastAllOfMatcher<
|
|
TypeLoc, TemplateSpecializationTypeLoc>
|
|
templateSpecializationTypeLoc;
|
|
|
|
/// Matches template specialization `TypeLoc`s, class template specializations,
|
|
/// variable template specializations, and function template specializations
|
|
/// that have at least one `TemplateArgumentLoc` matching the given
|
|
/// `InnerMatcher`.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<typename T> class A {};
|
|
/// A<int> a;
|
|
/// \endcode
|
|
/// varDecl(hasTypeLoc(templateSpecializationTypeLoc(hasAnyTemplateArgumentLoc(
|
|
/// hasTypeLoc(loc(asString("int")))))))
|
|
/// matches `A<int> a`.
|
|
AST_POLYMORPHIC_MATCHER_P(
|
|
hasAnyTemplateArgumentLoc,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
|
|
VarTemplateSpecializationDecl, FunctionDecl,
|
|
DeclRefExpr, TemplateSpecializationTypeLoc),
|
|
internal::Matcher<TemplateArgumentLoc>, InnerMatcher) {
|
|
auto Args = internal::getTemplateArgsWritten(Node);
|
|
return matchesFirstInRange(InnerMatcher, Args.begin(), Args.end(), Finder,
|
|
Builder) != Args.end();
|
|
return false;
|
|
}
|
|
|
|
/// Matches template specialization `TypeLoc`s, class template specializations,
|
|
/// variable template specializations, and function template specializations
|
|
/// where the n'th `TemplateArgumentLoc` matches the given `InnerMatcher`.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<typename T, typename U> class A {};
|
|
/// A<double, int> b;
|
|
/// A<int, double> c;
|
|
/// \endcode
|
|
/// varDecl(hasTypeLoc(templateSpecializationTypeLoc(hasTemplateArgumentLoc(0,
|
|
/// hasTypeLoc(loc(asString("double")))))))
|
|
/// matches `A<double, int> b`, but not `A<int, double> c`.
|
|
AST_POLYMORPHIC_MATCHER_P2(
|
|
hasTemplateArgumentLoc,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
|
|
VarTemplateSpecializationDecl, FunctionDecl,
|
|
DeclRefExpr, TemplateSpecializationTypeLoc),
|
|
unsigned, Index, internal::Matcher<TemplateArgumentLoc>, InnerMatcher) {
|
|
auto Args = internal::getTemplateArgsWritten(Node);
|
|
return Index < Args.size() &&
|
|
InnerMatcher.matches(Args[Index], Finder, Builder);
|
|
}
|
|
|
|
/// Matches C or C++ elaborated `TypeLoc`s.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct s {};
|
|
/// struct s ss;
|
|
/// \endcode
|
|
/// elaboratedTypeLoc()
|
|
/// matches the `TypeLoc` of the variable declaration of `ss`.
|
|
extern const internal::VariadicDynCastAllOfMatcher<TypeLoc, ElaboratedTypeLoc>
|
|
elaboratedTypeLoc;
|
|
|
|
/// Matches elaborated `TypeLoc`s that have a named `TypeLoc` matching
|
|
/// `InnerMatcher`.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T>
|
|
/// class C {};
|
|
/// class C<int> c;
|
|
///
|
|
/// class D {};
|
|
/// class D d;
|
|
/// \endcode
|
|
/// elaboratedTypeLoc(hasNamedTypeLoc(templateSpecializationTypeLoc()));
|
|
/// matches the `TypeLoc` of the variable declaration of `c`, but not `d`.
|
|
AST_MATCHER_P(ElaboratedTypeLoc, hasNamedTypeLoc, internal::Matcher<TypeLoc>,
|
|
InnerMatcher) {
|
|
return InnerMatcher.matches(Node.getNamedTypeLoc(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches type \c bool.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct S { bool func(); };
|
|
/// \endcode
|
|
/// functionDecl(returns(booleanType()))
|
|
/// matches "bool func();"
|
|
AST_MATCHER(Type, booleanType) {
|
|
return Node.isBooleanType();
|
|
}
|
|
|
|
/// Matches type \c void.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct S { void func(); };
|
|
/// \endcode
|
|
/// functionDecl(returns(voidType()))
|
|
/// matches "void func();"
|
|
AST_MATCHER(Type, voidType) {
|
|
return Node.isVoidType();
|
|
}
|
|
|
|
template <typename NodeType>
|
|
using AstTypeMatcher = internal::VariadicDynCastAllOfMatcher<Type, NodeType>;
|
|
|
|
/// Matches builtin Types.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct A {};
|
|
/// A a;
|
|
/// int b;
|
|
/// float c;
|
|
/// bool d;
|
|
/// \endcode
|
|
/// builtinType()
|
|
/// matches "int b", "float c" and "bool d"
|
|
extern const AstTypeMatcher<BuiltinType> builtinType;
|
|
|
|
/// Matches all kinds of arrays.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int a[] = { 2, 3 };
|
|
/// int b[4];
|
|
/// void f() { int c[a[0]]; }
|
|
/// \endcode
|
|
/// arrayType()
|
|
/// matches "int a[]", "int b[4]" and "int c[a[0]]";
|
|
extern const AstTypeMatcher<ArrayType> arrayType;
|
|
|
|
/// Matches C99 complex types.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// _Complex float f;
|
|
/// \endcode
|
|
/// complexType()
|
|
/// matches "_Complex float f"
|
|
extern const AstTypeMatcher<ComplexType> complexType;
|
|
|
|
/// Matches any real floating-point type (float, double, long double).
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int i;
|
|
/// float f;
|
|
/// \endcode
|
|
/// realFloatingPointType()
|
|
/// matches "float f" but not "int i"
|
|
AST_MATCHER(Type, realFloatingPointType) {
|
|
return Node.isRealFloatingType();
|
|
}
|
|
|
|
/// Matches arrays and C99 complex types that have a specific element
|
|
/// type.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct A {};
|
|
/// A a[7];
|
|
/// int b[7];
|
|
/// \endcode
|
|
/// arrayType(hasElementType(builtinType()))
|
|
/// matches "int b[7]"
|
|
///
|
|
/// Usable as: Matcher<ArrayType>, Matcher<ComplexType>
|
|
AST_TYPELOC_TRAVERSE_MATCHER_DECL(hasElementType, getElement,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(ArrayType,
|
|
ComplexType));
|
|
|
|
/// Matches C arrays with a specified constant size.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void() {
|
|
/// int a[2];
|
|
/// int b[] = { 2, 3 };
|
|
/// int c[b[0]];
|
|
/// }
|
|
/// \endcode
|
|
/// constantArrayType()
|
|
/// matches "int a[2]"
|
|
extern const AstTypeMatcher<ConstantArrayType> constantArrayType;
|
|
|
|
/// Matches nodes that have the specified size.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int a[42];
|
|
/// int b[2 * 21];
|
|
/// int c[41], d[43];
|
|
/// char *s = "abcd";
|
|
/// wchar_t *ws = L"abcd";
|
|
/// char *w = "a";
|
|
/// \endcode
|
|
/// constantArrayType(hasSize(42))
|
|
/// matches "int a[42]" and "int b[2 * 21]"
|
|
/// stringLiteral(hasSize(4))
|
|
/// matches "abcd", L"abcd"
|
|
AST_POLYMORPHIC_MATCHER_P(hasSize,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(ConstantArrayType,
|
|
StringLiteral),
|
|
unsigned, N) {
|
|
return internal::HasSizeMatcher<NodeType>::hasSize(Node, N);
|
|
}
|
|
|
|
/// Matches C++ arrays whose size is a value-dependent expression.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<typename T, int Size>
|
|
/// class array {
|
|
/// T data[Size];
|
|
/// };
|
|
/// \endcode
|
|
/// dependentSizedArrayType()
|
|
/// matches "T data[Size]"
|
|
extern const AstTypeMatcher<DependentSizedArrayType> dependentSizedArrayType;
|
|
|
|
/// Matches C++ extended vector type where either the type or size is
|
|
/// dependent.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template<typename T, int Size>
|
|
/// class vector {
|
|
/// typedef T __attribute__((ext_vector_type(Size))) type;
|
|
/// };
|
|
/// \endcode
|
|
/// dependentSizedExtVectorType()
|
|
/// matches "T __attribute__((ext_vector_type(Size)))"
|
|
extern const AstTypeMatcher<DependentSizedExtVectorType>
|
|
dependentSizedExtVectorType;
|
|
|
|
/// Matches C arrays with unspecified size.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int a[] = { 2, 3 };
|
|
/// int b[42];
|
|
/// void f(int c[]) { int d[a[0]]; };
|
|
/// \endcode
|
|
/// incompleteArrayType()
|
|
/// matches "int a[]" and "int c[]"
|
|
extern const AstTypeMatcher<IncompleteArrayType> incompleteArrayType;
|
|
|
|
/// Matches C arrays with a specified size that is not an
|
|
/// integer-constant-expression.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void f() {
|
|
/// int a[] = { 2, 3 }
|
|
/// int b[42];
|
|
/// int c[a[0]];
|
|
/// }
|
|
/// \endcode
|
|
/// variableArrayType()
|
|
/// matches "int c[a[0]]"
|
|
extern const AstTypeMatcher<VariableArrayType> variableArrayType;
|
|
|
|
/// Matches \c VariableArrayType nodes that have a specific size
|
|
/// expression.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// void f(int b) {
|
|
/// int a[b];
|
|
/// }
|
|
/// \endcode
|
|
/// variableArrayType(hasSizeExpr(ignoringImpCasts(declRefExpr(to(
|
|
/// varDecl(hasName("b")))))))
|
|
/// matches "int a[b]"
|
|
AST_MATCHER_P(VariableArrayType, hasSizeExpr,
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
return InnerMatcher.matches(*Node.getSizeExpr(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches atomic types.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// _Atomic(int) i;
|
|
/// \endcode
|
|
/// atomicType()
|
|
/// matches "_Atomic(int) i"
|
|
extern const AstTypeMatcher<AtomicType> atomicType;
|
|
|
|
/// Matches atomic types with a specific value type.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// _Atomic(int) i;
|
|
/// _Atomic(float) f;
|
|
/// \endcode
|
|
/// atomicType(hasValueType(isInteger()))
|
|
/// matches "_Atomic(int) i"
|
|
///
|
|
/// Usable as: Matcher<AtomicType>
|
|
AST_TYPELOC_TRAVERSE_MATCHER_DECL(hasValueType, getValue,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(AtomicType));
|
|
|
|
/// Matches types nodes representing C++11 auto types.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// auto n = 4;
|
|
/// int v[] = { 2, 3 }
|
|
/// for (auto i : v) { }
|
|
/// \endcode
|
|
/// autoType()
|
|
/// matches "auto n" and "auto i"
|
|
extern const AstTypeMatcher<AutoType> autoType;
|
|
|
|
/// Matches types nodes representing C++11 decltype(<expr>) types.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// short i = 1;
|
|
/// int j = 42;
|
|
/// decltype(i + j) result = i + j;
|
|
/// \endcode
|
|
/// decltypeType()
|
|
/// matches "decltype(i + j)"
|
|
extern const AstTypeMatcher<DecltypeType> decltypeType;
|
|
|
|
/// Matches \c AutoType nodes where the deduced type is a specific type.
|
|
///
|
|
/// Note: There is no \c TypeLoc for the deduced type and thus no
|
|
/// \c getDeducedLoc() matcher.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// auto a = 1;
|
|
/// auto b = 2.0;
|
|
/// \endcode
|
|
/// autoType(hasDeducedType(isInteger()))
|
|
/// matches "auto a"
|
|
///
|
|
/// Usable as: Matcher<AutoType>
|
|
AST_TYPE_TRAVERSE_MATCHER(hasDeducedType, getDeducedType,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(AutoType));
|
|
|
|
/// Matches \c DecltypeType or \c UsingType nodes to find the underlying type.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// decltype(1) a = 1;
|
|
/// decltype(2.0) b = 2.0;
|
|
/// \endcode
|
|
/// decltypeType(hasUnderlyingType(isInteger()))
|
|
/// matches the type of "a"
|
|
///
|
|
/// Usable as: Matcher<DecltypeType>, Matcher<UsingType>
|
|
AST_TYPE_TRAVERSE_MATCHER(hasUnderlyingType, getUnderlyingType,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(DecltypeType,
|
|
UsingType));
|
|
|
|
/// Matches \c FunctionType nodes.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int (*f)(int);
|
|
/// void g();
|
|
/// \endcode
|
|
/// functionType()
|
|
/// matches "int (*f)(int)" and the type of "g".
|
|
extern const AstTypeMatcher<FunctionType> functionType;
|
|
|
|
/// Matches \c FunctionProtoType nodes.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int (*f)(int);
|
|
/// void g();
|
|
/// \endcode
|
|
/// functionProtoType()
|
|
/// matches "int (*f)(int)" and the type of "g" in C++ mode.
|
|
/// In C mode, "g" is not matched because it does not contain a prototype.
|
|
extern const AstTypeMatcher<FunctionProtoType> functionProtoType;
|
|
|
|
/// Matches \c ParenType nodes.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int (*ptr_to_array)[4];
|
|
/// int *array_of_ptrs[4];
|
|
/// \endcode
|
|
///
|
|
/// \c varDecl(hasType(pointsTo(parenType()))) matches \c ptr_to_array but not
|
|
/// \c array_of_ptrs.
|
|
extern const AstTypeMatcher<ParenType> parenType;
|
|
|
|
/// Matches \c ParenType nodes where the inner type is a specific type.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int (*ptr_to_array)[4];
|
|
/// int (*ptr_to_func)(int);
|
|
/// \endcode
|
|
///
|
|
/// \c varDecl(hasType(pointsTo(parenType(innerType(functionType()))))) matches
|
|
/// \c ptr_to_func but not \c ptr_to_array.
|
|
///
|
|
/// Usable as: Matcher<ParenType>
|
|
AST_TYPE_TRAVERSE_MATCHER(innerType, getInnerType,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(ParenType));
|
|
|
|
/// Matches block pointer types, i.e. types syntactically represented as
|
|
/// "void (^)(int)".
|
|
///
|
|
/// The \c pointee is always required to be a \c FunctionType.
|
|
extern const AstTypeMatcher<BlockPointerType> blockPointerType;
|
|
|
|
/// Matches member pointer types.
|
|
/// Given
|
|
/// \code
|
|
/// struct A { int i; }
|
|
/// A::* ptr = A::i;
|
|
/// \endcode
|
|
/// memberPointerType()
|
|
/// matches "A::* ptr"
|
|
extern const AstTypeMatcher<MemberPointerType> memberPointerType;
|
|
|
|
/// Matches pointer types, but does not match Objective-C object pointer
|
|
/// types.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int *a;
|
|
/// int &b = *a;
|
|
/// int c = 5;
|
|
///
|
|
/// @interface Foo
|
|
/// @end
|
|
/// Foo *f;
|
|
/// \endcode
|
|
/// pointerType()
|
|
/// matches "int *a", but does not match "Foo *f".
|
|
extern const AstTypeMatcher<PointerType> pointerType;
|
|
|
|
/// Matches an Objective-C object pointer type, which is different from
|
|
/// a pointer type, despite being syntactically similar.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int *a;
|
|
///
|
|
/// @interface Foo
|
|
/// @end
|
|
/// Foo *f;
|
|
/// \endcode
|
|
/// pointerType()
|
|
/// matches "Foo *f", but does not match "int *a".
|
|
extern const AstTypeMatcher<ObjCObjectPointerType> objcObjectPointerType;
|
|
|
|
/// Matches both lvalue and rvalue reference types.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int *a;
|
|
/// int &b = *a;
|
|
/// int &&c = 1;
|
|
/// auto &d = b;
|
|
/// auto &&e = c;
|
|
/// auto &&f = 2;
|
|
/// int g = 5;
|
|
/// \endcode
|
|
///
|
|
/// \c referenceType() matches the types of \c b, \c c, \c d, \c e, and \c f.
|
|
extern const AstTypeMatcher<ReferenceType> referenceType;
|
|
|
|
/// Matches lvalue reference types.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// int *a;
|
|
/// int &b = *a;
|
|
/// int &&c = 1;
|
|
/// auto &d = b;
|
|
/// auto &&e = c;
|
|
/// auto &&f = 2;
|
|
/// int g = 5;
|
|
/// \endcode
|
|
///
|
|
/// \c lValueReferenceType() matches the types of \c b, \c d, and \c e. \c e is
|
|
/// matched since the type is deduced as int& by reference collapsing rules.
|
|
extern const AstTypeMatcher<LValueReferenceType> lValueReferenceType;
|
|
|
|
/// Matches rvalue reference types.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// int *a;
|
|
/// int &b = *a;
|
|
/// int &&c = 1;
|
|
/// auto &d = b;
|
|
/// auto &&e = c;
|
|
/// auto &&f = 2;
|
|
/// int g = 5;
|
|
/// \endcode
|
|
///
|
|
/// \c rValueReferenceType() matches the types of \c c and \c f. \c e is not
|
|
/// matched as it is deduced to int& by reference collapsing rules.
|
|
extern const AstTypeMatcher<RValueReferenceType> rValueReferenceType;
|
|
|
|
/// Narrows PointerType (and similar) matchers to those where the
|
|
/// \c pointee matches a given matcher.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// int *a;
|
|
/// int const *b;
|
|
/// float const *f;
|
|
/// \endcode
|
|
/// pointerType(pointee(isConstQualified(), isInteger()))
|
|
/// matches "int const *b"
|
|
///
|
|
/// Usable as: Matcher<BlockPointerType>, Matcher<MemberPointerType>,
|
|
/// Matcher<PointerType>, Matcher<ReferenceType>,
|
|
/// Matcher<ObjCObjectPointerType>
|
|
AST_TYPELOC_TRAVERSE_MATCHER_DECL(
|
|
pointee, getPointee,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(BlockPointerType, MemberPointerType,
|
|
PointerType, ReferenceType,
|
|
ObjCObjectPointerType));
|
|
|
|
/// Matches typedef types.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// typedef int X;
|
|
/// \endcode
|
|
/// typedefType()
|
|
/// matches "typedef int X"
|
|
extern const AstTypeMatcher<TypedefType> typedefType;
|
|
|
|
/// Matches qualified types when the qualifier is applied via a macro.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// #define CDECL __attribute__((cdecl))
|
|
/// typedef void (CDECL *X)();
|
|
/// typedef void (__attribute__((cdecl)) *Y)();
|
|
/// \endcode
|
|
/// macroQualifiedType()
|
|
/// matches the type of the typedef declaration of \c X but not \c Y.
|
|
extern const AstTypeMatcher<MacroQualifiedType> macroQualifiedType;
|
|
|
|
/// Matches enum types.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// enum C { Green };
|
|
/// enum class S { Red };
|
|
///
|
|
/// C c;
|
|
/// S s;
|
|
/// \endcode
|
|
//
|
|
/// \c enumType() matches the type of the variable declarations of both \c c and
|
|
/// \c s.
|
|
extern const AstTypeMatcher<EnumType> enumType;
|
|
|
|
/// Matches template specialization types.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T>
|
|
/// class C { };
|
|
///
|
|
/// template class C<int>; // A
|
|
/// C<char> var; // B
|
|
/// \endcode
|
|
///
|
|
/// \c templateSpecializationType() matches the type of the explicit
|
|
/// instantiation in \c A and the type of the variable declaration in \c B.
|
|
extern const AstTypeMatcher<TemplateSpecializationType>
|
|
templateSpecializationType;
|
|
|
|
/// Matches C++17 deduced template specialization types, e.g. deduced class
|
|
/// template types.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T>
|
|
/// class C { public: C(T); };
|
|
///
|
|
/// C c(123);
|
|
/// \endcode
|
|
/// \c deducedTemplateSpecializationType() matches the type in the declaration
|
|
/// of the variable \c c.
|
|
extern const AstTypeMatcher<DeducedTemplateSpecializationType>
|
|
deducedTemplateSpecializationType;
|
|
|
|
/// Matches types nodes representing unary type transformations.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// typedef __underlying_type(T) type;
|
|
/// \endcode
|
|
/// unaryTransformType()
|
|
/// matches "__underlying_type(T)"
|
|
extern const AstTypeMatcher<UnaryTransformType> unaryTransformType;
|
|
|
|
/// Matches record types (e.g. structs, classes).
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class C {};
|
|
/// struct S {};
|
|
///
|
|
/// C c;
|
|
/// S s;
|
|
/// \endcode
|
|
///
|
|
/// \c recordType() matches the type of the variable declarations of both \c c
|
|
/// and \c s.
|
|
extern const AstTypeMatcher<RecordType> recordType;
|
|
|
|
/// Matches tag types (record and enum types).
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// enum E {};
|
|
/// class C {};
|
|
///
|
|
/// E e;
|
|
/// C c;
|
|
/// \endcode
|
|
///
|
|
/// \c tagType() matches the type of the variable declarations of both \c e
|
|
/// and \c c.
|
|
extern const AstTypeMatcher<TagType> tagType;
|
|
|
|
/// Matches types specified with an elaborated type keyword or with a
|
|
/// qualified name.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace N {
|
|
/// namespace M {
|
|
/// class D {};
|
|
/// }
|
|
/// }
|
|
/// class C {};
|
|
///
|
|
/// class C c;
|
|
/// N::M::D d;
|
|
/// \endcode
|
|
///
|
|
/// \c elaboratedType() matches the type of the variable declarations of both
|
|
/// \c c and \c d.
|
|
extern const AstTypeMatcher<ElaboratedType> elaboratedType;
|
|
|
|
/// Matches ElaboratedTypes whose qualifier, a NestedNameSpecifier,
|
|
/// matches \c InnerMatcher if the qualifier exists.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace N {
|
|
/// namespace M {
|
|
/// class D {};
|
|
/// }
|
|
/// }
|
|
/// N::M::D d;
|
|
/// \endcode
|
|
///
|
|
/// \c elaboratedType(hasQualifier(hasPrefix(specifiesNamespace(hasName("N"))))
|
|
/// matches the type of the variable declaration of \c d.
|
|
AST_MATCHER_P(ElaboratedType, hasQualifier,
|
|
internal::Matcher<NestedNameSpecifier>, InnerMatcher) {
|
|
if (const NestedNameSpecifier *Qualifier = Node.getQualifier())
|
|
return InnerMatcher.matches(*Qualifier, Finder, Builder);
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Matches ElaboratedTypes whose named type matches \c InnerMatcher.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace N {
|
|
/// namespace M {
|
|
/// class D {};
|
|
/// }
|
|
/// }
|
|
/// N::M::D d;
|
|
/// \endcode
|
|
///
|
|
/// \c elaboratedType(namesType(recordType(
|
|
/// hasDeclaration(namedDecl(hasName("D")))))) matches the type of the variable
|
|
/// declaration of \c d.
|
|
AST_MATCHER_P(ElaboratedType, namesType, internal::Matcher<QualType>,
|
|
InnerMatcher) {
|
|
return InnerMatcher.matches(Node.getNamedType(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches types specified through a using declaration.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace a { struct S {}; }
|
|
/// using a::S;
|
|
/// S s;
|
|
/// \endcode
|
|
///
|
|
/// \c usingType() matches the type of the variable declaration of \c s.
|
|
extern const AstTypeMatcher<UsingType> usingType;
|
|
|
|
/// Matches types that represent the result of substituting a type for a
|
|
/// template type parameter.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T>
|
|
/// void F(T t) {
|
|
/// int i = 1 + t;
|
|
/// }
|
|
/// \endcode
|
|
///
|
|
/// \c substTemplateTypeParmType() matches the type of 't' but not '1'
|
|
extern const AstTypeMatcher<SubstTemplateTypeParmType>
|
|
substTemplateTypeParmType;
|
|
|
|
/// Matches template type parameter substitutions that have a replacement
|
|
/// type that matches the provided matcher.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// template <typename T>
|
|
/// double F(T t);
|
|
/// int i;
|
|
/// double j = F(i);
|
|
/// \endcode
|
|
///
|
|
/// \c substTemplateTypeParmType(hasReplacementType(type())) matches int
|
|
AST_TYPE_TRAVERSE_MATCHER(
|
|
hasReplacementType, getReplacementType,
|
|
AST_POLYMORPHIC_SUPPORTED_TYPES(SubstTemplateTypeParmType));
|
|
|
|
/// Matches template type parameter types.
|
|
///
|
|
/// Example matches T, but not int.
|
|
/// (matcher = templateTypeParmType())
|
|
/// \code
|
|
/// template <typename T> void f(int i);
|
|
/// \endcode
|
|
extern const AstTypeMatcher<TemplateTypeParmType> templateTypeParmType;
|
|
|
|
/// Matches injected class name types.
|
|
///
|
|
/// Example matches S s, but not S<T> s.
|
|
/// (matcher = parmVarDecl(hasType(injectedClassNameType())))
|
|
/// \code
|
|
/// template <typename T> struct S {
|
|
/// void f(S s);
|
|
/// void g(S<T> s);
|
|
/// };
|
|
/// \endcode
|
|
extern const AstTypeMatcher<InjectedClassNameType> injectedClassNameType;
|
|
|
|
/// Matches decayed type
|
|
/// Example matches i[] in declaration of f.
|
|
/// (matcher = valueDecl(hasType(decayedType(hasDecayedType(pointerType())))))
|
|
/// Example matches i[1].
|
|
/// (matcher = expr(hasType(decayedType(hasDecayedType(pointerType())))))
|
|
/// \code
|
|
/// void f(int i[]) {
|
|
/// i[1] = 0;
|
|
/// }
|
|
/// \endcode
|
|
extern const AstTypeMatcher<DecayedType> decayedType;
|
|
|
|
/// Matches the decayed type, whoes decayed type matches \c InnerMatcher
|
|
AST_MATCHER_P(DecayedType, hasDecayedType, internal::Matcher<QualType>,
|
|
InnerType) {
|
|
return InnerType.matches(Node.getDecayedType(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches a dependent name type
|
|
///
|
|
/// Example matches T::type
|
|
/// \code
|
|
/// template <typename T> struct declToImport {
|
|
/// typedef typename T::type dependent_name;
|
|
/// };
|
|
/// \endcode
|
|
extern const AstTypeMatcher<DependentNameType> dependentNameType;
|
|
|
|
/// Matches a dependent template specialization type
|
|
///
|
|
/// Example matches A<T>::template B<T>
|
|
/// \code
|
|
/// template<typename T> struct A;
|
|
/// template<typename T> struct declToImport {
|
|
/// typename A<T>::template B<T> a;
|
|
/// };
|
|
/// \endcode
|
|
extern const AstTypeMatcher<DependentTemplateSpecializationType>
|
|
dependentTemplateSpecializationType;
|
|
|
|
/// Matches declarations whose declaration context, interpreted as a
|
|
/// Decl, matches \c InnerMatcher.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace N {
|
|
/// namespace M {
|
|
/// class D {};
|
|
/// }
|
|
/// }
|
|
/// \endcode
|
|
///
|
|
/// \c cxxRcordDecl(hasDeclContext(namedDecl(hasName("M")))) matches the
|
|
/// declaration of \c class \c D.
|
|
AST_MATCHER_P(Decl, hasDeclContext, internal::Matcher<Decl>, InnerMatcher) {
|
|
const DeclContext *DC = Node.getDeclContext();
|
|
if (!DC) return false;
|
|
return InnerMatcher.matches(*Decl::castFromDeclContext(DC), Finder, Builder);
|
|
}
|
|
|
|
/// Matches nested name specifiers.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace ns {
|
|
/// struct A { static void f(); };
|
|
/// void A::f() {}
|
|
/// void g() { A::f(); }
|
|
/// }
|
|
/// ns::A a;
|
|
/// \endcode
|
|
/// nestedNameSpecifier()
|
|
/// matches "ns::" and both "A::"
|
|
extern const internal::VariadicAllOfMatcher<NestedNameSpecifier>
|
|
nestedNameSpecifier;
|
|
|
|
/// Same as \c nestedNameSpecifier but matches \c NestedNameSpecifierLoc.
|
|
extern const internal::VariadicAllOfMatcher<NestedNameSpecifierLoc>
|
|
nestedNameSpecifierLoc;
|
|
|
|
/// Matches \c NestedNameSpecifierLocs for which the given inner
|
|
/// NestedNameSpecifier-matcher matches.
|
|
AST_MATCHER_FUNCTION_P_OVERLOAD(
|
|
internal::BindableMatcher<NestedNameSpecifierLoc>, loc,
|
|
internal::Matcher<NestedNameSpecifier>, InnerMatcher, 1) {
|
|
return internal::BindableMatcher<NestedNameSpecifierLoc>(
|
|
new internal::LocMatcher<NestedNameSpecifierLoc, NestedNameSpecifier>(
|
|
InnerMatcher));
|
|
}
|
|
|
|
/// Matches nested name specifiers that specify a type matching the
|
|
/// given \c QualType matcher without qualifiers.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct A { struct B { struct C {}; }; };
|
|
/// A::B::C c;
|
|
/// \endcode
|
|
/// nestedNameSpecifier(specifiesType(
|
|
/// hasDeclaration(cxxRecordDecl(hasName("A")))
|
|
/// ))
|
|
/// matches "A::"
|
|
AST_MATCHER_P(NestedNameSpecifier, specifiesType,
|
|
internal::Matcher<QualType>, InnerMatcher) {
|
|
if (!Node.getAsType())
|
|
return false;
|
|
return InnerMatcher.matches(QualType(Node.getAsType(), 0), Finder, Builder);
|
|
}
|
|
|
|
/// Matches nested name specifier locs that specify a type matching the
|
|
/// given \c TypeLoc.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct A { struct B { struct C {}; }; };
|
|
/// A::B::C c;
|
|
/// \endcode
|
|
/// nestedNameSpecifierLoc(specifiesTypeLoc(loc(type(
|
|
/// hasDeclaration(cxxRecordDecl(hasName("A")))))))
|
|
/// matches "A::"
|
|
AST_MATCHER_P(NestedNameSpecifierLoc, specifiesTypeLoc,
|
|
internal::Matcher<TypeLoc>, InnerMatcher) {
|
|
return Node && Node.getNestedNameSpecifier()->getAsType() &&
|
|
InnerMatcher.matches(Node.getTypeLoc(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches on the prefix of a \c NestedNameSpecifier.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct A { struct B { struct C {}; }; };
|
|
/// A::B::C c;
|
|
/// \endcode
|
|
/// nestedNameSpecifier(hasPrefix(specifiesType(asString("struct A")))) and
|
|
/// matches "A::"
|
|
AST_MATCHER_P_OVERLOAD(NestedNameSpecifier, hasPrefix,
|
|
internal::Matcher<NestedNameSpecifier>, InnerMatcher,
|
|
0) {
|
|
const NestedNameSpecifier *NextNode = Node.getPrefix();
|
|
if (!NextNode)
|
|
return false;
|
|
return InnerMatcher.matches(*NextNode, Finder, Builder);
|
|
}
|
|
|
|
/// Matches on the prefix of a \c NestedNameSpecifierLoc.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct A { struct B { struct C {}; }; };
|
|
/// A::B::C c;
|
|
/// \endcode
|
|
/// nestedNameSpecifierLoc(hasPrefix(loc(specifiesType(asString("struct A")))))
|
|
/// matches "A::"
|
|
AST_MATCHER_P_OVERLOAD(NestedNameSpecifierLoc, hasPrefix,
|
|
internal::Matcher<NestedNameSpecifierLoc>, InnerMatcher,
|
|
1) {
|
|
NestedNameSpecifierLoc NextNode = Node.getPrefix();
|
|
if (!NextNode)
|
|
return false;
|
|
return InnerMatcher.matches(NextNode, Finder, Builder);
|
|
}
|
|
|
|
/// Matches nested name specifiers that specify a namespace matching the
|
|
/// given namespace matcher.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace ns { struct A {}; }
|
|
/// ns::A a;
|
|
/// \endcode
|
|
/// nestedNameSpecifier(specifiesNamespace(hasName("ns")))
|
|
/// matches "ns::"
|
|
AST_MATCHER_P(NestedNameSpecifier, specifiesNamespace,
|
|
internal::Matcher<NamespaceDecl>, InnerMatcher) {
|
|
if (!Node.getAsNamespace())
|
|
return false;
|
|
return InnerMatcher.matches(*Node.getAsNamespace(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches attributes.
|
|
/// Attributes may be attached with a variety of different syntaxes (including
|
|
/// keywords, C++11 attributes, GNU ``__attribute``` and MSVC `__declspec``,
|
|
/// and ``#pragma``s). They may also be implicit.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct [[nodiscard]] Foo{};
|
|
/// void bar(int * __attribute__((nonnull)) );
|
|
/// __declspec(noinline) void baz();
|
|
///
|
|
/// #pragma omp declare simd
|
|
/// int min();
|
|
/// \endcode
|
|
/// attr()
|
|
/// matches "nodiscard", "nonnull", "noinline", and the whole "#pragma" line.
|
|
extern const internal::VariadicAllOfMatcher<Attr> attr;
|
|
|
|
/// Overloads for the \c equalsNode matcher.
|
|
/// FIXME: Implement for other node types.
|
|
/// @{
|
|
|
|
/// Matches if a node equals another node.
|
|
///
|
|
/// \c Decl has pointer identity in the AST.
|
|
AST_MATCHER_P_OVERLOAD(Decl, equalsNode, const Decl*, Other, 0) {
|
|
return &Node == Other;
|
|
}
|
|
/// Matches if a node equals another node.
|
|
///
|
|
/// \c Stmt has pointer identity in the AST.
|
|
AST_MATCHER_P_OVERLOAD(Stmt, equalsNode, const Stmt*, Other, 1) {
|
|
return &Node == Other;
|
|
}
|
|
/// Matches if a node equals another node.
|
|
///
|
|
/// \c Type has pointer identity in the AST.
|
|
AST_MATCHER_P_OVERLOAD(Type, equalsNode, const Type*, Other, 2) {
|
|
return &Node == Other;
|
|
}
|
|
|
|
/// @}
|
|
|
|
/// Matches each case or default statement belonging to the given switch
|
|
/// statement. This matcher may produce multiple matches.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// switch (1) { case 1: case 2: default: switch (2) { case 3: case 4: ; } }
|
|
/// \endcode
|
|
/// switchStmt(forEachSwitchCase(caseStmt().bind("c"))).bind("s")
|
|
/// matches four times, with "c" binding each of "case 1:", "case 2:",
|
|
/// "case 3:" and "case 4:", and "s" respectively binding "switch (1)",
|
|
/// "switch (1)", "switch (2)" and "switch (2)".
|
|
AST_MATCHER_P(SwitchStmt, forEachSwitchCase, internal::Matcher<SwitchCase>,
|
|
InnerMatcher) {
|
|
BoundNodesTreeBuilder Result;
|
|
// FIXME: getSwitchCaseList() does not necessarily guarantee a stable
|
|
// iteration order. We should use the more general iterating matchers once
|
|
// they are capable of expressing this matcher (for example, it should ignore
|
|
// case statements belonging to nested switch statements).
|
|
bool Matched = false;
|
|
for (const SwitchCase *SC = Node.getSwitchCaseList(); SC;
|
|
SC = SC->getNextSwitchCase()) {
|
|
BoundNodesTreeBuilder CaseBuilder(*Builder);
|
|
bool CaseMatched = InnerMatcher.matches(*SC, Finder, &CaseBuilder);
|
|
if (CaseMatched) {
|
|
Matched = true;
|
|
Result.addMatch(CaseBuilder);
|
|
}
|
|
}
|
|
*Builder = std::move(Result);
|
|
return Matched;
|
|
}
|
|
|
|
/// Matches each constructor initializer in a constructor definition.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class A { A() : i(42), j(42) {} int i; int j; };
|
|
/// \endcode
|
|
/// cxxConstructorDecl(forEachConstructorInitializer(
|
|
/// forField(decl().bind("x"))
|
|
/// ))
|
|
/// will trigger two matches, binding for 'i' and 'j' respectively.
|
|
AST_MATCHER_P(CXXConstructorDecl, forEachConstructorInitializer,
|
|
internal::Matcher<CXXCtorInitializer>, InnerMatcher) {
|
|
BoundNodesTreeBuilder Result;
|
|
bool Matched = false;
|
|
for (const auto *I : Node.inits()) {
|
|
if (Finder->isTraversalIgnoringImplicitNodes() && !I->isWritten())
|
|
continue;
|
|
BoundNodesTreeBuilder InitBuilder(*Builder);
|
|
if (InnerMatcher.matches(*I, Finder, &InitBuilder)) {
|
|
Matched = true;
|
|
Result.addMatch(InitBuilder);
|
|
}
|
|
}
|
|
*Builder = std::move(Result);
|
|
return Matched;
|
|
}
|
|
|
|
/// Matches constructor declarations that are copy constructors.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct S {
|
|
/// S(); // #1
|
|
/// S(const S &); // #2
|
|
/// S(S &&); // #3
|
|
/// };
|
|
/// \endcode
|
|
/// cxxConstructorDecl(isCopyConstructor()) will match #2, but not #1 or #3.
|
|
AST_MATCHER(CXXConstructorDecl, isCopyConstructor) {
|
|
return Node.isCopyConstructor();
|
|
}
|
|
|
|
/// Matches constructor declarations that are move constructors.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct S {
|
|
/// S(); // #1
|
|
/// S(const S &); // #2
|
|
/// S(S &&); // #3
|
|
/// };
|
|
/// \endcode
|
|
/// cxxConstructorDecl(isMoveConstructor()) will match #3, but not #1 or #2.
|
|
AST_MATCHER(CXXConstructorDecl, isMoveConstructor) {
|
|
return Node.isMoveConstructor();
|
|
}
|
|
|
|
/// Matches constructor declarations that are default constructors.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct S {
|
|
/// S(); // #1
|
|
/// S(const S &); // #2
|
|
/// S(S &&); // #3
|
|
/// };
|
|
/// \endcode
|
|
/// cxxConstructorDecl(isDefaultConstructor()) will match #1, but not #2 or #3.
|
|
AST_MATCHER(CXXConstructorDecl, isDefaultConstructor) {
|
|
return Node.isDefaultConstructor();
|
|
}
|
|
|
|
/// Matches constructors that delegate to another constructor.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// struct S {
|
|
/// S(); // #1
|
|
/// S(int) {} // #2
|
|
/// S(S &&) : S() {} // #3
|
|
/// };
|
|
/// S::S() : S(0) {} // #4
|
|
/// \endcode
|
|
/// cxxConstructorDecl(isDelegatingConstructor()) will match #3 and #4, but not
|
|
/// #1 or #2.
|
|
AST_MATCHER(CXXConstructorDecl, isDelegatingConstructor) {
|
|
return Node.isDelegatingConstructor();
|
|
}
|
|
|
|
/// 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 and #8, but not #1, #7 or #9.
|
|
/// cxxConversionDecl(isExplicit()) will match #4, but not #3.
|
|
/// 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;
|
|
|
|
ASTChildrenNotSpelledInSourceScope RAII(Finder, false);
|
|
|
|
return InnerMatcher.matches(*ES.getExpr(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches functions, variables and namespace declarations that are marked with
|
|
/// the inline keyword.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// inline void f();
|
|
/// void g();
|
|
/// namespace n {
|
|
/// inline namespace m {}
|
|
/// }
|
|
/// inline int Foo = 5;
|
|
/// \endcode
|
|
/// functionDecl(isInline()) will match ::f().
|
|
/// namespaceDecl(isInline()) will match n::m.
|
|
/// varDecl(isInline()) will match Foo;
|
|
AST_POLYMORPHIC_MATCHER(isInline, AST_POLYMORPHIC_SUPPORTED_TYPES(NamespaceDecl,
|
|
FunctionDecl,
|
|
VarDecl)) {
|
|
// This is required because the spelling of the function used to determine
|
|
// whether inline is specified or not differs between the polymorphic types.
|
|
if (const auto *FD = dyn_cast<FunctionDecl>(&Node))
|
|
return FD->isInlineSpecified();
|
|
if (const auto *NSD = dyn_cast<NamespaceDecl>(&Node))
|
|
return NSD->isInline();
|
|
if (const auto *VD = dyn_cast<VarDecl>(&Node))
|
|
return VD->isInline();
|
|
llvm_unreachable("Not a valid polymorphic type");
|
|
}
|
|
|
|
/// Matches anonymous namespace declarations.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// namespace n {
|
|
/// namespace {} // #1
|
|
/// }
|
|
/// \endcode
|
|
/// namespaceDecl(isAnonymous()) will match #1 but not ::n.
|
|
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(); }
|
|
|
|
/// Matches declarations in an anonymous namespace.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// class vector {};
|
|
/// namespace foo {
|
|
/// class vector {};
|
|
/// namespace {
|
|
/// class vector {}; // #1
|
|
/// }
|
|
/// }
|
|
/// namespace {
|
|
/// class vector {}; // #2
|
|
/// namespace foo {
|
|
/// class vector{}; // #3
|
|
/// }
|
|
/// }
|
|
/// \endcode
|
|
/// cxxRecordDecl(hasName("vector"), isInAnonymousNamespace()) will match
|
|
/// #1, #2 and #3.
|
|
AST_MATCHER(Decl, isInAnonymousNamespace) {
|
|
return Node.isInAnonymousNamespace();
|
|
}
|
|
|
|
/// If the given case statement does not use the GNU case range
|
|
/// extension, matches the constant given in the statement.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// switch (1) { case 1: case 1+1: case 3 ... 4: ; }
|
|
/// \endcode
|
|
/// caseStmt(hasCaseConstant(integerLiteral()))
|
|
/// matches "case 1:"
|
|
AST_MATCHER_P(CaseStmt, hasCaseConstant, internal::Matcher<Expr>,
|
|
InnerMatcher) {
|
|
if (Node.getRHS())
|
|
return false;
|
|
|
|
return InnerMatcher.matches(*Node.getLHS(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches declaration that has a given attribute.
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// __attribute__((device)) void f() { ... }
|
|
/// \endcode
|
|
/// decl(hasAttr(clang::attr::CUDADevice)) matches the function declaration of
|
|
/// 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()) {
|
|
if (Attr->getKind() == AttrKind)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Matches the return value expression of a return statement
|
|
///
|
|
/// Given
|
|
/// \code
|
|
/// return a + b;
|
|
/// \endcode
|
|
/// hasReturnValue(binaryOperator())
|
|
/// matches 'return a + b'
|
|
/// with binaryOperator()
|
|
/// matching 'a + b'
|
|
AST_MATCHER_P(ReturnStmt, hasReturnValue, internal::Matcher<Expr>,
|
|
InnerMatcher) {
|
|
if (const auto *RetValue = Node.getRetValue())
|
|
return InnerMatcher.matches(*RetValue, Finder, Builder);
|
|
return false;
|
|
}
|
|
|
|
/// Matches CUDA kernel call expression.
|
|
///
|
|
/// Example matches,
|
|
/// \code
|
|
/// kernel<<<i,j>>>();
|
|
/// \endcode
|
|
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CUDAKernelCallExpr>
|
|
cudaKernelCallExpr;
|
|
|
|
/// Matches expressions that resolve to a null pointer constant, such as
|
|
/// GNU's __null, C++11's nullptr, or C's NULL macro.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// void *v1 = NULL;
|
|
/// void *v2 = nullptr;
|
|
/// void *v3 = __null; // GNU extension
|
|
/// char *cp = (char *)0;
|
|
/// int *ip = 0;
|
|
/// int i = 0;
|
|
/// \endcode
|
|
/// 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())))));
|
|
}
|
|
|
|
/// Matches the DecompositionDecl the binding belongs to.
|
|
///
|
|
/// For example, in:
|
|
/// \code
|
|
/// void foo()
|
|
/// {
|
|
/// int arr[3];
|
|
/// auto &[f, s, t] = arr;
|
|
///
|
|
/// f = 42;
|
|
/// }
|
|
/// \endcode
|
|
/// The matcher:
|
|
/// \code
|
|
/// bindingDecl(hasName("f"),
|
|
/// forDecomposition(decompositionDecl())
|
|
/// \endcode
|
|
/// matches 'f' in 'auto &[f, s, t]'.
|
|
AST_MATCHER_P(BindingDecl, forDecomposition, internal::Matcher<ValueDecl>,
|
|
InnerMatcher) {
|
|
if (const ValueDecl *VD = Node.getDecomposedDecl())
|
|
return InnerMatcher.matches(*VD, Finder, Builder);
|
|
return false;
|
|
}
|
|
|
|
/// Matches the Nth binding of a DecompositionDecl.
|
|
///
|
|
/// For example, in:
|
|
/// \code
|
|
/// void foo()
|
|
/// {
|
|
/// int arr[3];
|
|
/// auto &[f, s, t] = arr;
|
|
///
|
|
/// f = 42;
|
|
/// }
|
|
/// \endcode
|
|
/// The matcher:
|
|
/// \code
|
|
/// decompositionDecl(hasBinding(0,
|
|
/// bindingDecl(hasName("f").bind("fBinding"))))
|
|
/// \endcode
|
|
/// matches the decomposition decl with 'f' bound to "fBinding".
|
|
AST_MATCHER_P2(DecompositionDecl, hasBinding, unsigned, N,
|
|
internal::Matcher<BindingDecl>, InnerMatcher) {
|
|
if (Node.bindings().size() <= N)
|
|
return false;
|
|
return InnerMatcher.matches(*Node.bindings()[N], Finder, Builder);
|
|
}
|
|
|
|
/// Matches any binding of a DecompositionDecl.
|
|
///
|
|
/// For example, in:
|
|
/// \code
|
|
/// void foo()
|
|
/// {
|
|
/// int arr[3];
|
|
/// auto &[f, s, t] = arr;
|
|
///
|
|
/// f = 42;
|
|
/// }
|
|
/// \endcode
|
|
/// The matcher:
|
|
/// \code
|
|
/// decompositionDecl(hasAnyBinding(bindingDecl(hasName("f").bind("fBinding"))))
|
|
/// \endcode
|
|
/// matches the decomposition decl with 'f' bound to "fBinding".
|
|
AST_MATCHER_P(DecompositionDecl, hasAnyBinding, internal::Matcher<BindingDecl>,
|
|
InnerMatcher) {
|
|
return llvm::any_of(Node.bindings(), [&](const auto *Binding) {
|
|
return InnerMatcher.matches(*Binding, Finder, Builder);
|
|
});
|
|
}
|
|
|
|
/// Matches declaration of the function the statement belongs to.
|
|
///
|
|
/// Deprecated. Use forCallable() to correctly handle the situation when
|
|
/// the declaration is not a function (but a block or an Objective-C method).
|
|
/// forFunction() not only fails to take non-functions into account but also
|
|
/// may match the wrong declaration in their presence.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// F& operator=(const F& o) {
|
|
/// std::copy_if(o.begin(), o.end(), begin(), [](V v) { return v > 0; });
|
|
/// return *this;
|
|
/// }
|
|
/// \endcode
|
|
/// returnStmt(forFunction(hasName("operator=")))
|
|
/// matches 'return *this'
|
|
/// but does not match 'return v > 0'
|
|
AST_MATCHER_P(Stmt, forFunction, internal::Matcher<FunctionDecl>,
|
|
InnerMatcher) {
|
|
const auto &Parents = Finder->getASTContext().getParents(Node);
|
|
|
|
llvm::SmallVector<DynTypedNode, 8> Stack(Parents.begin(), Parents.end());
|
|
while (!Stack.empty()) {
|
|
const auto &CurNode = Stack.back();
|
|
Stack.pop_back();
|
|
if (const auto *FuncDeclNode = CurNode.get<FunctionDecl>()) {
|
|
if (InnerMatcher.matches(*FuncDeclNode, Finder, Builder)) {
|
|
return true;
|
|
}
|
|
} else if (const auto *LambdaExprNode = CurNode.get<LambdaExpr>()) {
|
|
if (InnerMatcher.matches(*LambdaExprNode->getCallOperator(), Finder,
|
|
Builder)) {
|
|
return true;
|
|
}
|
|
} else {
|
|
llvm::append_range(Stack, Finder->getASTContext().getParents(CurNode));
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Matches declaration of the function, method, or block the statement
|
|
/// belongs to.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// F& operator=(const F& o) {
|
|
/// std::copy_if(o.begin(), o.end(), begin(), [](V v) { return v > 0; });
|
|
/// return *this;
|
|
/// }
|
|
/// \endcode
|
|
/// returnStmt(forCallable(functionDecl(hasName("operator="))))
|
|
/// matches 'return *this'
|
|
/// but does not match 'return v > 0'
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// -(void) foo {
|
|
/// int x = 1;
|
|
/// dispatch_sync(queue, ^{ int y = 2; });
|
|
/// }
|
|
/// \endcode
|
|
/// declStmt(forCallable(objcMethodDecl()))
|
|
/// matches 'int x = 1'
|
|
/// but does not match 'int y = 2'.
|
|
/// whereas declStmt(forCallable(blockDecl()))
|
|
/// matches 'int y = 2'
|
|
/// but does not match 'int x = 1'.
|
|
AST_MATCHER_P(Stmt, forCallable, internal::Matcher<Decl>, InnerMatcher) {
|
|
const auto &Parents = Finder->getASTContext().getParents(Node);
|
|
|
|
llvm::SmallVector<DynTypedNode, 8> Stack(Parents.begin(), Parents.end());
|
|
while (!Stack.empty()) {
|
|
const auto &CurNode = Stack.back();
|
|
Stack.pop_back();
|
|
if (const auto *FuncDeclNode = CurNode.get<FunctionDecl>()) {
|
|
BoundNodesTreeBuilder B = *Builder;
|
|
if (InnerMatcher.matches(*FuncDeclNode, Finder, &B)) {
|
|
*Builder = std::move(B);
|
|
return true;
|
|
}
|
|
} else if (const auto *LambdaExprNode = CurNode.get<LambdaExpr>()) {
|
|
BoundNodesTreeBuilder B = *Builder;
|
|
if (InnerMatcher.matches(*LambdaExprNode->getCallOperator(), Finder,
|
|
&B)) {
|
|
*Builder = std::move(B);
|
|
return true;
|
|
}
|
|
} else if (const auto *ObjCMethodDeclNode = CurNode.get<ObjCMethodDecl>()) {
|
|
BoundNodesTreeBuilder B = *Builder;
|
|
if (InnerMatcher.matches(*ObjCMethodDeclNode, Finder, &B)) {
|
|
*Builder = std::move(B);
|
|
return true;
|
|
}
|
|
} else if (const auto *BlockDeclNode = CurNode.get<BlockDecl>()) {
|
|
BoundNodesTreeBuilder B = *Builder;
|
|
if (InnerMatcher.matches(*BlockDeclNode, Finder, &B)) {
|
|
*Builder = std::move(B);
|
|
return true;
|
|
}
|
|
} else {
|
|
llvm::append_range(Stack, Finder->getASTContext().getParents(CurNode));
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Matches a declaration that has external formal linkage.
|
|
///
|
|
/// Example matches only z (matcher = varDecl(hasExternalFormalLinkage()))
|
|
/// \code
|
|
/// void f() {
|
|
/// int x;
|
|
/// static int y;
|
|
/// }
|
|
/// int z;
|
|
/// \endcode
|
|
///
|
|
/// Example matches f() because it has external formal linkage despite being
|
|
/// unique to the translation unit as though it has internal likage
|
|
/// (matcher = functionDecl(hasExternalFormalLinkage()))
|
|
///
|
|
/// \code
|
|
/// namespace {
|
|
/// void f() {}
|
|
/// }
|
|
/// \endcode
|
|
AST_MATCHER(NamedDecl, hasExternalFormalLinkage) {
|
|
return Node.hasExternalFormalLinkage();
|
|
}
|
|
|
|
/// Matches a declaration that has default arguments.
|
|
///
|
|
/// Example matches y (matcher = parmVarDecl(hasDefaultArgument()))
|
|
/// \code
|
|
/// void x(int val) {}
|
|
/// void y(int val = 0) {}
|
|
/// \endcode
|
|
///
|
|
/// Deprecated. Use hasInitializer() instead to be able to
|
|
/// match on the contents of the default argument. For example:
|
|
///
|
|
/// \code
|
|
/// void x(int val = 7) {}
|
|
/// void y(int val = 42) {}
|
|
/// \endcode
|
|
/// parmVarDecl(hasInitializer(integerLiteral(equals(42))))
|
|
/// matches the parameter of y
|
|
///
|
|
/// A matcher such as
|
|
/// parmVarDecl(hasInitializer(anything()))
|
|
/// is equivalent to parmVarDecl(hasDefaultArgument()).
|
|
AST_MATCHER(ParmVarDecl, hasDefaultArgument) {
|
|
return Node.hasDefaultArg();
|
|
}
|
|
|
|
/// Matches array new expressions.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// MyClass *p1 = new MyClass[10];
|
|
/// \endcode
|
|
/// cxxNewExpr(isArray())
|
|
/// matches the expression 'new MyClass[10]'.
|
|
AST_MATCHER(CXXNewExpr, isArray) {
|
|
return Node.isArray();
|
|
}
|
|
|
|
/// Matches placement new expression arguments.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// MyClass *p1 = new (Storage, 16) MyClass();
|
|
/// \endcode
|
|
/// cxxNewExpr(hasPlacementArg(1, integerLiteral(equals(16))))
|
|
/// matches the expression 'new (Storage, 16) MyClass()'.
|
|
AST_MATCHER_P2(CXXNewExpr, hasPlacementArg, unsigned, Index,
|
|
internal::Matcher<Expr>, InnerMatcher) {
|
|
return Node.getNumPlacementArgs() > Index &&
|
|
InnerMatcher.matches(*Node.getPlacementArg(Index), Finder, Builder);
|
|
}
|
|
|
|
/// Matches any placement new expression arguments.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// MyClass *p1 = new (Storage) MyClass();
|
|
/// \endcode
|
|
/// cxxNewExpr(hasAnyPlacementArg(anything()))
|
|
/// matches the expression 'new (Storage, 16) MyClass()'.
|
|
AST_MATCHER_P(CXXNewExpr, hasAnyPlacementArg, internal::Matcher<Expr>,
|
|
InnerMatcher) {
|
|
return llvm::any_of(Node.placement_arguments(), [&](const Expr *Arg) {
|
|
return InnerMatcher.matches(*Arg, Finder, Builder);
|
|
});
|
|
}
|
|
|
|
/// Matches array new expressions with a given array size.
|
|
///
|
|
/// Given:
|
|
/// \code
|
|
/// MyClass *p1 = new MyClass[10];
|
|
/// \endcode
|
|
/// cxxNewExpr(hasArraySize(integerLiteral(equals(10))))
|
|
/// matches the expression 'new MyClass[10]'.
|
|
AST_MATCHER_P(CXXNewExpr, hasArraySize, internal::Matcher<Expr>, InnerMatcher) {
|
|
return Node.isArray() && *Node.getArraySize() &&
|
|
InnerMatcher.matches(**Node.getArraySize(), Finder, Builder);
|
|
}
|
|
|
|
/// Matches a class declaration that is defined.
|
|
///
|
|
/// Example matches x (matcher = cxxRecordDecl(hasDefinition()))
|
|
/// \code
|
|
/// class x {};
|
|
/// class y;
|
|
/// \endcode
|
|
AST_MATCHER(CXXRecordDecl, hasDefinition) {
|
|
return Node.hasDefinition();
|
|
}
|
|
|
|
/// Matches C++11 scoped enum declaration.
|
|
///
|
|
/// Example matches Y (matcher = enumDecl(isScoped()))
|
|
/// \code
|
|
/// enum X {};
|
|
/// enum class Y {};
|
|
/// \endcode
|
|
AST_MATCHER(EnumDecl, isScoped) {
|
|
return Node.isScoped();
|
|
}
|
|
|
|
/// Matches a function declared with a trailing return type.
|
|
///
|
|
/// Example matches Y (matcher = functionDecl(hasTrailingReturn()))
|
|
/// \code
|
|
/// int X() {}
|
|
/// auto Y() -> int {}
|
|
/// \endcode
|
|
AST_MATCHER(FunctionDecl, hasTrailingReturn) {
|
|
if (const auto *F = Node.getType()->getAs<FunctionProtoType>())
|
|
return F->hasTrailingReturn();
|
|
return false;
|
|
}
|
|
|
|
/// Matches expressions that match InnerMatcher that are possibly wrapped in an
|
|
/// elidable constructor and other corresponding bookkeeping nodes.
|
|
///
|
|
/// 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
|
|
///
|
|
/// \code
|
|
/// struct H {};
|
|
/// H G();
|
|
/// void f() {
|
|
/// H D = G();
|
|
/// }
|
|
/// \endcode
|
|
///
|
|
/// ``varDecl(hasInitializer(ignoringElidableConstructorCall(callExpr())))``
|
|
/// matches ``H D = G()`` in C++11 through C++17 (and beyond).
|
|
AST_MATCHER_P(Expr, ignoringElidableConstructorCall, internal::Matcher<Expr>,
|
|
InnerMatcher) {
|
|
// 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))) {
|
|
return InnerMatcher.matches(*MaterializeTemp->getSubExpr(), 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 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) != Clauses.end();
|
|
}
|
|
|
|
/// Matches OpenMP ``default`` clause.
|
|
///
|
|
/// Given
|
|
///
|
|
/// \code
|
|
/// #pragma omp parallel default(none)
|
|
/// #pragma omp parallel default(shared)
|
|
/// #pragma omp parallel default(private)
|
|
/// #pragma omp parallel default(firstprivate)
|
|
/// #pragma omp parallel
|
|
/// \endcode
|
|
///
|
|
/// ``ompDefaultClause()`` matches ``default(none)``, ``default(shared)``,
|
|
/// `` default(private)`` and ``default(firstprivate)``
|
|
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)
|
|
/// #pragma omp parallel default(private)
|
|
/// #pragma omp parallel default(firstprivate)
|
|
/// \endcode
|
|
///
|
|
/// ``ompDefaultClause(isNoneKind())`` matches only ``default(none)``.
|
|
AST_MATCHER(OMPDefaultClause, isNoneKind) {
|
|
return Node.getDefaultKind() == llvm::omp::OMP_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)
|
|
/// #pragma omp parallel default(private)
|
|
/// #pragma omp parallel default(firstprivate)
|
|
/// \endcode
|
|
///
|
|
/// ``ompDefaultClause(isSharedKind())`` matches only ``default(shared)``.
|
|
AST_MATCHER(OMPDefaultClause, isSharedKind) {
|
|
return Node.getDefaultKind() == llvm::omp::OMP_DEFAULT_shared;
|
|
}
|
|
|
|
/// Matches if the OpenMP ``default`` clause has ``private`` kind
|
|
/// specified.
|
|
///
|
|
/// Given
|
|
///
|
|
/// \code
|
|
/// #pragma omp parallel
|
|
/// #pragma omp parallel default(none)
|
|
/// #pragma omp parallel default(shared)
|
|
/// #pragma omp parallel default(private)
|
|
/// #pragma omp parallel default(firstprivate)
|
|
/// \endcode
|
|
///
|
|
/// ``ompDefaultClause(isPrivateKind())`` matches only
|
|
/// ``default(private)``.
|
|
AST_MATCHER(OMPDefaultClause, isPrivateKind) {
|
|
return Node.getDefaultKind() == llvm::omp::OMP_DEFAULT_private;
|
|
}
|
|
|
|
/// Matches if the OpenMP ``default`` clause has ``firstprivate`` kind
|
|
/// specified.
|
|
///
|
|
/// Given
|
|
///
|
|
/// \code
|
|
/// #pragma omp parallel
|
|
/// #pragma omp parallel default(none)
|
|
/// #pragma omp parallel default(shared)
|
|
/// #pragma omp parallel default(private)
|
|
/// #pragma omp parallel default(firstprivate)
|
|
/// \endcode
|
|
///
|
|
/// ``ompDefaultClause(isFirstPrivateKind())`` matches only
|
|
/// ``default(firstprivate)``.
|
|
AST_MATCHER(OMPDefaultClause, isFirstPrivateKind) {
|
|
return Node.getDefaultKind() == llvm::omp::OMP_DEFAULT_firstprivate;
|
|
}
|
|
|
|
/// 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 llvm::omp::isAllowedClauseForDirective(
|
|
Node.getDirectiveKind(), CKind,
|
|
Finder->getASTContext().getLangOpts().OpenMP);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
// End OpenMP handling.
|
|
//----------------------------------------------------------------------------//
|
|
|
|
} // namespace ast_matchers
|
|
} // namespace clang
|
|
|
|
#endif // LLVM_CLANG_ASTMATCHERS_ASTMATCHERS_H
|