Support Attr in DynTypedNode and ASTMatchers.

Differential Revision: https://reviews.llvm.org/D89743
This commit is contained in:
Sam McCall 2020-10-19 23:36:50 +02:00
parent 9ed7416aaf
commit a4bdcdadc6
17 changed files with 208 additions and 36 deletions

View File

@ -442,7 +442,7 @@ void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
hasDeclaration(DeclMatcher), hasDeclaration(DeclMatcher),
unless(templateSpecializationType()))))), unless(templateSpecializationType()))))),
hasParent(nestedNameSpecifierLoc()), hasParent(nestedNameSpecifierLoc()),
hasAncestor(isImplicit()), hasAncestor(decl(isImplicit())),
hasAncestor(UsingShadowDeclInClass), hasAncestor(UsingShadowDeclInClass),
hasAncestor(functionDecl(isDefaulted())))), hasAncestor(functionDecl(isDefaulted())))),
hasAncestor(decl().bind("dc"))) hasAncestor(decl().bind("dc")))
@ -466,7 +466,7 @@ void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
hasAncestor(decl(IsInMovedNs).bind("dc")), hasAncestor(decl(IsInMovedNs).bind("dc")),
loc(nestedNameSpecifier( loc(nestedNameSpecifier(
specifiesType(hasDeclaration(DeclMatcher.bind("from_decl"))))), specifiesType(hasDeclaration(DeclMatcher.bind("from_decl"))))),
unless(anyOf(hasAncestor(isImplicit()), unless(anyOf(hasAncestor(decl(isImplicit())),
hasAncestor(UsingShadowDeclInClass), hasAncestor(UsingShadowDeclInClass),
hasAncestor(functionDecl(isDefaulted())), hasAncestor(functionDecl(isDefaulted())),
hasAncestor(typeLoc(loc(qualType(hasDeclaration( hasAncestor(typeLoc(loc(qualType(hasDeclaration(
@ -495,7 +495,7 @@ void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
hasAncestor(cxxRecordDecl()))), hasAncestor(cxxRecordDecl()))),
hasParent(namespaceDecl())); hasParent(namespaceDecl()));
Finder->addMatcher(expr(hasAncestor(decl().bind("dc")), IsInMovedNs, Finder->addMatcher(expr(hasAncestor(decl().bind("dc")), IsInMovedNs,
unless(hasAncestor(isImplicit())), unless(hasAncestor(decl(isImplicit()))),
anyOf(callExpr(callee(FuncMatcher)).bind("call"), anyOf(callExpr(callee(FuncMatcher)).bind("call"),
declRefExpr(to(FuncMatcher.bind("func_decl"))) declRefExpr(to(FuncMatcher.bind("func_decl")))
.bind("func_ref"))), .bind("func_ref"))),

View File

@ -38,12 +38,12 @@ void ProBoundsConstantArrayIndexCheck::registerPPCallbacks(
void ProBoundsConstantArrayIndexCheck::registerMatchers(MatchFinder *Finder) { void ProBoundsConstantArrayIndexCheck::registerMatchers(MatchFinder *Finder) {
// Note: if a struct contains an array member, the compiler-generated // Note: if a struct contains an array member, the compiler-generated
// constructor has an arraySubscriptExpr. // constructor has an arraySubscriptExpr.
Finder->addMatcher( Finder->addMatcher(arraySubscriptExpr(hasBase(ignoringImpCasts(hasType(
arraySubscriptExpr( constantArrayType().bind("type")))),
hasBase(ignoringImpCasts(hasType(constantArrayType().bind("type")))), hasIndex(expr().bind("index")),
hasIndex(expr().bind("index")), unless(hasAncestor(isImplicit()))) unless(hasAncestor(decl(isImplicit()))))
.bind("expr"), .bind("expr"),
this); this);
Finder->addMatcher( Finder->addMatcher(
cxxOperatorCallExpr( cxxOperatorCallExpr(

View File

@ -582,6 +582,24 @@ Examples matches public virtual B.
</pre></td></tr> </pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Attr.html">Attr</a>&gt;</td><td class="name" onclick="toggle('attr0')"><a name="attr0Anchor">attr</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Attr.html">Attr</a>&gt;...</td></tr>
<tr><td colspan="4" class="doc" id="attr0"><pre>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
struct [[nodiscard]] Foo{};
void bar(int * __attribute__((nonnull)) );
__declspec(noinline) void baz();
#pragma omp declare simd
int min();
attr()
matches "nodiscard", "nonnull", "noinline", and the whole "#pragma" line.
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXCtorInitializer.html">CXXCtorInitializer</a>&gt;</td><td class="name" onclick="toggle('cxxCtorInitializer0')"><a name="cxxCtorInitializer0Anchor">cxxCtorInitializer</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXCtorInitializer.html">CXXCtorInitializer</a>&gt;...</td></tr> <tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXCtorInitializer.html">CXXCtorInitializer</a>&gt;</td><td class="name" onclick="toggle('cxxCtorInitializer0')"><a name="cxxCtorInitializer0Anchor">cxxCtorInitializer</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXCtorInitializer.html">CXXCtorInitializer</a>&gt;...</td></tr>
<tr><td colspan="4" class="doc" id="cxxCtorInitializer0"><pre>Matches constructor initializers. <tr><td colspan="4" class="doc" id="cxxCtorInitializer0"><pre>Matches constructor initializers.
@ -2744,6 +2762,12 @@ Usable as: Any Matcher
</pre></td></tr> </pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Attr.html">Attr</a>&gt;</td><td class="name" onclick="toggle('isImplicit1')"><a name="isImplicit1Anchor">isImplicit</a></td><td></td></tr>
<tr><td colspan="4" class="doc" id="isImplicit1"><pre>Matches an entity that has been implicitly added by the compiler (e.g.
implicit default/copy constructors).
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1BinaryOperator.html">BinaryOperator</a>&gt;</td><td class="name" onclick="toggle('hasAnyOperatorName0')"><a name="hasAnyOperatorName0Anchor">hasAnyOperatorName</a></td><td>StringRef, ..., StringRef</td></tr> <tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1BinaryOperator.html">BinaryOperator</a>&gt;</td><td class="name" onclick="toggle('hasAnyOperatorName0')"><a name="hasAnyOperatorName0Anchor">hasAnyOperatorName</a></td><td>StringRef, ..., StringRef</td></tr>
<tr><td colspan="4" class="doc" id="hasAnyOperatorName0"><pre>Matches operator expressions (binary or unary) that have any of the <tr><td colspan="4" class="doc" id="hasAnyOperatorName0"><pre>Matches operator expressions (binary or unary) that have any of the
specified names. specified names.
@ -3808,8 +3832,8 @@ Usable as: Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>&gt;</td><td class="name" onclick="toggle('isImplicit0')"><a name="isImplicit0Anchor">isImplicit</a></td><td></td></tr> <tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>&gt;</td><td class="name" onclick="toggle('isImplicit0')"><a name="isImplicit0Anchor">isImplicit</a></td><td></td></tr>
<tr><td colspan="4" class="doc" id="isImplicit0"><pre>Matches a declaration that has been implicitly added <tr><td colspan="4" class="doc" id="isImplicit0"><pre>Matches an entity that has been implicitly added by the compiler (e.g.
by the compiler (eg. implicit default/copy constructors). implicit default/copy constructors).
</pre></td></tr> </pre></td></tr>

View File

@ -30,6 +30,9 @@ class OMPClause;
#define GEN_CLANG_CLAUSE_CLASS #define GEN_CLANG_CLAUSE_CLASS
#define CLAUSE_CLASS(Enum, Str, Class) class Class; #define CLAUSE_CLASS(Enum, Str, Class) class Class;
#include "llvm/Frontend/OpenMP/OMP.inc" #include "llvm/Frontend/OpenMP/OMP.inc"
class Attr;
#define ATTR(A) class A##Attr;
#include "clang/Basic/AttrList.inc"
} // end namespace clang } // end namespace clang

View File

@ -25,10 +25,8 @@
#include "llvm/Support/AlignOf.h" #include "llvm/Support/AlignOf.h"
namespace llvm { namespace llvm {
class raw_ostream; class raw_ostream;
} // namespace llvm
}
namespace clang { namespace clang {
@ -66,6 +64,7 @@ public:
static ASTNodeKind getFromNode(const Stmt &S); static ASTNodeKind getFromNode(const Stmt &S);
static ASTNodeKind getFromNode(const Type &T); static ASTNodeKind getFromNode(const Type &T);
static ASTNodeKind getFromNode(const OMPClause &C); static ASTNodeKind getFromNode(const OMPClause &C);
static ASTNodeKind getFromNode(const Attr &A);
/// \} /// \}
/// Returns \c true if \c this and \c Other represent the same kind. /// Returns \c true if \c this and \c Other represent the same kind.
@ -152,6 +151,9 @@ private:
#define GEN_CLANG_CLAUSE_CLASS #define GEN_CLANG_CLAUSE_CLASS
#define CLAUSE_CLASS(Enum, Str, Class) NKI_##Class, #define CLAUSE_CLASS(Enum, Str, Class) NKI_##Class,
#include "llvm/Frontend/OpenMP/OMP.inc" #include "llvm/Frontend/OpenMP/OMP.inc"
NKI_Attr,
#define ATTR(A) NKI_##A##Attr,
#include "clang/Basic/AttrList.inc"
NKI_NumberOfKinds NKI_NumberOfKinds
}; };
@ -201,6 +203,7 @@ KIND_TO_KIND_ID(Decl)
KIND_TO_KIND_ID(Stmt) KIND_TO_KIND_ID(Stmt)
KIND_TO_KIND_ID(Type) KIND_TO_KIND_ID(Type)
KIND_TO_KIND_ID(OMPClause) KIND_TO_KIND_ID(OMPClause)
KIND_TO_KIND_ID(Attr)
KIND_TO_KIND_ID(CXXBaseSpecifier) KIND_TO_KIND_ID(CXXBaseSpecifier)
#define DECL(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED##Decl) #define DECL(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED##Decl)
#include "clang/AST/DeclNodes.inc" #include "clang/AST/DeclNodes.inc"
@ -211,6 +214,8 @@ KIND_TO_KIND_ID(CXXBaseSpecifier)
#define GEN_CLANG_CLAUSE_CLASS #define GEN_CLANG_CLAUSE_CLASS
#define CLAUSE_CLASS(Enum, Str, Class) KIND_TO_KIND_ID(Class) #define CLAUSE_CLASS(Enum, Str, Class) KIND_TO_KIND_ID(Class)
#include "llvm/Frontend/OpenMP/OMP.inc" #include "llvm/Frontend/OpenMP/OMP.inc"
#define ATTR(A) KIND_TO_KIND_ID(A##Attr)
#include "clang/Basic/AttrList.inc"
#undef KIND_TO_KIND_ID #undef KIND_TO_KIND_ID
inline raw_ostream &operator<<(raw_ostream &OS, ASTNodeKind K) { inline raw_ostream &operator<<(raw_ostream &OS, ASTNodeKind K) {
@ -486,6 +491,11 @@ struct DynTypedNode::BaseConverter<
T, std::enable_if_t<std::is_base_of<OMPClause, T>::value>> T, std::enable_if_t<std::is_base_of<OMPClause, T>::value>>
: public DynCastPtrConverter<T, OMPClause> {}; : public DynCastPtrConverter<T, OMPClause> {};
template <typename T>
struct DynTypedNode::BaseConverter<
T, std::enable_if_t<std::is_base_of<Attr, T>::value>>
: public DynCastPtrConverter<T, Attr> {};
template <> template <>
struct DynTypedNode::BaseConverter< struct DynTypedNode::BaseConverter<
NestedNameSpecifier, void> : public PtrConverter<NestedNameSpecifier> {}; NestedNameSpecifier, void> : public PtrConverter<NestedNameSpecifier> {};

View File

@ -167,6 +167,7 @@ public:
MatchCallback *Action); MatchCallback *Action);
void addMatcher(const TemplateArgumentLocMatcher &NodeMatch, void addMatcher(const TemplateArgumentLocMatcher &NodeMatch,
MatchCallback *Action); MatchCallback *Action);
void addMatcher(const AttrMatcher &NodeMatch, MatchCallback *Action);
/// @} /// @}
/// Adds a matcher to execute when running over the AST. /// Adds a matcher to execute when running over the AST.
@ -219,6 +220,7 @@ public:
std::vector<std::pair<CXXCtorInitializerMatcher, MatchCallback *>> CtorInit; std::vector<std::pair<CXXCtorInitializerMatcher, MatchCallback *>> CtorInit;
std::vector<std::pair<TemplateArgumentLocMatcher, MatchCallback *>> std::vector<std::pair<TemplateArgumentLocMatcher, MatchCallback *>>
TemplateArgumentLoc; TemplateArgumentLoc;
std::vector<std::pair<AttrMatcher, MatchCallback *>> Attr;
/// All the callbacks in one container to simplify iteration. /// All the callbacks in one container to simplify iteration.
llvm::SmallPtrSet<MatchCallback *, 16> AllCallbacks; llvm::SmallPtrSet<MatchCallback *, 16> AllCallbacks;
}; };

View File

@ -148,6 +148,7 @@ using CXXBaseSpecifierMatcher = internal::Matcher<CXXBaseSpecifier>;
using CXXCtorInitializerMatcher = internal::Matcher<CXXCtorInitializer>; using CXXCtorInitializerMatcher = internal::Matcher<CXXCtorInitializer>;
using TemplateArgumentMatcher = internal::Matcher<TemplateArgument>; using TemplateArgumentMatcher = internal::Matcher<TemplateArgument>;
using TemplateArgumentLocMatcher = internal::Matcher<TemplateArgumentLoc>; using TemplateArgumentLocMatcher = internal::Matcher<TemplateArgumentLoc>;
using AttrMatcher = internal::Matcher<Attr>;
/// @} /// @}
/// Matches any node. /// Matches any node.
@ -752,9 +753,10 @@ AST_MATCHER_P(ClassTemplateSpecializationDecl, hasSpecializedTemplate,
InnerMatcher.matches(*Decl, Finder, Builder)); InnerMatcher.matches(*Decl, Finder, Builder));
} }
/// Matches a declaration that has been implicitly added /// Matches an entity that has been implicitly added by the compiler (e.g.
/// by the compiler (eg. implicit default/copy constructors). /// implicit default/copy constructors).
AST_MATCHER(Decl, isImplicit) { AST_POLYMORPHIC_MATCHER(isImplicit,
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Attr)) {
return Node.isImplicit(); return Node.isImplicit();
} }
@ -3489,8 +3491,8 @@ internal::Matcher<T> findAll(const internal::Matcher<T> &Matcher) {
/// Usable as: Any Matcher /// Usable as: Any Matcher
extern const internal::ArgumentAdaptingMatcherFunc< extern const internal::ArgumentAdaptingMatcherFunc<
internal::HasParentMatcher, internal::HasParentMatcher,
internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc>, internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc, Attr>,
internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc>> internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc, Attr>>
hasParent; hasParent;
/// Matches AST nodes that have an ancestor that matches the provided /// Matches AST nodes that have an ancestor that matches the provided
@ -3506,8 +3508,8 @@ extern const internal::ArgumentAdaptingMatcherFunc<
/// Usable as: Any Matcher /// Usable as: Any Matcher
extern const internal::ArgumentAdaptingMatcherFunc< extern const internal::ArgumentAdaptingMatcherFunc<
internal::HasAncestorMatcher, internal::HasAncestorMatcher,
internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc>, internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc, Attr>,
internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc>> internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc, Attr>>
hasAncestor; hasAncestor;
/// Matches if the provided matcher does not match. /// Matches if the provided matcher does not match.
@ -7133,6 +7135,24 @@ AST_MATCHER_P(NestedNameSpecifier, specifiesNamespace,
return InnerMatcher.matches(*Node.getAsNamespace(), Finder, Builder); 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. /// Overloads for the \c equalsNode matcher.
/// FIXME: Implement for other node types. /// FIXME: Implement for other node types.
/// @{ /// @{

View File

@ -757,7 +757,8 @@ public:
std::is_base_of<NestedNameSpecifier, T>::value || std::is_base_of<NestedNameSpecifier, T>::value ||
std::is_base_of<NestedNameSpecifierLoc, T>::value || std::is_base_of<NestedNameSpecifierLoc, T>::value ||
std::is_base_of<TypeLoc, T>::value || std::is_base_of<TypeLoc, T>::value ||
std::is_base_of<QualType, T>::value, std::is_base_of<QualType, T>::value ||
std::is_base_of<Attr, T>::value,
"unsupported type for recursive matching"); "unsupported type for recursive matching");
return matchesChildOf(DynTypedNode::create(Node), getASTContext(), Matcher, return matchesChildOf(DynTypedNode::create(Node), getASTContext(), Matcher,
Builder, Bind); Builder, Bind);
@ -771,7 +772,8 @@ public:
std::is_base_of<NestedNameSpecifier, T>::value || std::is_base_of<NestedNameSpecifier, T>::value ||
std::is_base_of<NestedNameSpecifierLoc, T>::value || std::is_base_of<NestedNameSpecifierLoc, T>::value ||
std::is_base_of<TypeLoc, T>::value || std::is_base_of<TypeLoc, T>::value ||
std::is_base_of<QualType, T>::value, std::is_base_of<QualType, T>::value ||
std::is_base_of<Attr, T>::value,
"unsupported type for recursive matching"); "unsupported type for recursive matching");
return matchesDescendantOf(DynTypedNode::create(Node), getASTContext(), return matchesDescendantOf(DynTypedNode::create(Node), getASTContext(),
Matcher, Builder, Bind); Matcher, Builder, Bind);
@ -785,7 +787,8 @@ public:
static_assert(std::is_base_of<Decl, T>::value || static_assert(std::is_base_of<Decl, T>::value ||
std::is_base_of<NestedNameSpecifierLoc, T>::value || std::is_base_of<NestedNameSpecifierLoc, T>::value ||
std::is_base_of<Stmt, T>::value || std::is_base_of<Stmt, T>::value ||
std::is_base_of<TypeLoc, T>::value, std::is_base_of<TypeLoc, T>::value ||
std::is_base_of<Attr, T>::value,
"type not allowed for recursive matching"); "type not allowed for recursive matching");
return matchesAncestorOf(DynTypedNode::create(Node), getASTContext(), return matchesAncestorOf(DynTypedNode::create(Node), getASTContext(),
Matcher, Builder, MatchMode); Matcher, Builder, MatchMode);
@ -954,7 +957,7 @@ class HasNameMatcher : public SingleNodeMatcherInterface<NamedDecl> {
bool matchesNode(const NamedDecl &Node) const override; bool matchesNode(const NamedDecl &Node) const override;
private: private:
/// Unqualified match routine. /// Unqualified match routine.
/// ///
/// It is much faster than the full match, but it only works for unqualified /// It is much faster than the full match, but it only works for unqualified
@ -1175,7 +1178,8 @@ struct IsBaseType {
std::is_same<T, NestedNameSpecifier>::value || std::is_same<T, NestedNameSpecifier>::value ||
std::is_same<T, NestedNameSpecifierLoc>::value || std::is_same<T, NestedNameSpecifierLoc>::value ||
std::is_same<T, CXXCtorInitializer>::value || std::is_same<T, CXXCtorInitializer>::value ||
std::is_same<T, TemplateArgumentLoc>::value; std::is_same<T, TemplateArgumentLoc>::value ||
std::is_same<T, Attr>::value;
}; };
template <typename T> template <typename T>
const bool IsBaseType<T>::value; const bool IsBaseType<T>::value;
@ -1185,7 +1189,7 @@ const bool IsBaseType<T>::value;
/// Useful for matchers like \c anything and \c unless. /// Useful for matchers like \c anything and \c unless.
using AllNodeBaseTypes = using AllNodeBaseTypes =
TypeList<Decl, Stmt, NestedNameSpecifier, NestedNameSpecifierLoc, QualType, TypeList<Decl, Stmt, NestedNameSpecifier, NestedNameSpecifierLoc, QualType,
Type, TypeLoc, CXXCtorInitializer>; Type, TypeLoc, CXXCtorInitializer, Attr>;
/// Helper meta-function to extract the argument out of a function of /// Helper meta-function to extract the argument out of a function of
/// type void(Arg). /// type void(Arg).
@ -1212,7 +1216,7 @@ template <class T, class Tuple> constexpr T *new_from_tuple(Tuple &&t) {
using AdaptativeDefaultFromTypes = AllNodeBaseTypes; using AdaptativeDefaultFromTypes = AllNodeBaseTypes;
using AdaptativeDefaultToTypes = using AdaptativeDefaultToTypes =
TypeList<Decl, Stmt, NestedNameSpecifier, NestedNameSpecifierLoc, TypeLoc, TypeList<Decl, Stmt, NestedNameSpecifier, NestedNameSpecifierLoc, TypeLoc,
QualType>; QualType, Attr>;
/// All types that are supported by HasDeclarationMatcher above. /// All types that are supported by HasDeclarationMatcher above.
using HasDeclarationSupportedTypes = using HasDeclarationSupportedTypes =

View File

@ -14,6 +14,7 @@
#include "clang/AST/ASTTypeTraits.h" #include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/ASTContext.h" #include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclCXX.h"
#include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/OpenMPClause.h" #include "clang/AST/OpenMPClause.h"
@ -44,6 +45,9 @@ const ASTNodeKind::KindInfo ASTNodeKind::AllKindInfo[] = {
#define GEN_CLANG_CLAUSE_CLASS #define GEN_CLANG_CLAUSE_CLASS
#define CLAUSE_CLASS(Enum, Str, Class) {NKI_OMPClause, #Class}, #define CLAUSE_CLASS(Enum, Str, Class) {NKI_OMPClause, #Class},
#include "llvm/Frontend/OpenMP/OMP.inc" #include "llvm/Frontend/OpenMP/OMP.inc"
{NKI_None, "Attr"},
#define ATTR(A) {NKI_Attr, #A "Attr"},
#include "clang/Basic/AttrList.inc"
}; };
bool ASTNodeKind::isBaseOf(ASTNodeKind Other, unsigned *Distance) const { bool ASTNodeKind::isBaseOf(ASTNodeKind Other, unsigned *Distance) const {
@ -134,7 +138,17 @@ ASTNodeKind ASTNodeKind::getFromNode(const OMPClause &C) {
llvm_unreachable("unexpected OpenMP clause kind"); llvm_unreachable("unexpected OpenMP clause kind");
#include "llvm/Frontend/OpenMP/OMP.inc" #include "llvm/Frontend/OpenMP/OMP.inc"
} }
llvm_unreachable("invalid stmt kind"); llvm_unreachable("invalid omp clause kind");
}
ASTNodeKind ASTNodeKind::getFromNode(const Attr &A) {
switch (A.getKind()) {
#define ATTR(A) \
case attr::A: \
return ASTNodeKind(NKI_##A##Attr);
#include "clang/Basic/AttrList.inc"
}
llvm_unreachable("invalid attr kind");
} }
void DynTypedNode::print(llvm::raw_ostream &OS, void DynTypedNode::print(llvm::raw_ostream &OS,
@ -162,6 +176,8 @@ void DynTypedNode::print(llvm::raw_ostream &OS,
S->printPretty(OS, nullptr, PP); S->printPretty(OS, nullptr, PP);
else if (const Type *T = get<Type>()) else if (const Type *T = get<Type>())
QualType(T, 0).print(OS, PP); QualType(T, 0).print(OS, PP);
else if (const Attr *A = get<Attr>())
A->printPretty(OS, PP);
else else
OS << "Unable to print values of type " << NodeKind.asStringRef() << "\n"; OS << "Unable to print values of type " << NodeKind.asStringRef() << "\n";
} }
@ -195,5 +211,7 @@ SourceRange DynTypedNode::getSourceRange() const {
return SourceRange(C->getBeginLoc(), C->getEndLoc()); return SourceRange(C->getBeginLoc(), C->getEndLoc());
if (const auto *CBS = get<CXXBaseSpecifier>()) if (const auto *CBS = get<CXXBaseSpecifier>())
return CBS->getSourceRange(); return CBS->getSourceRange();
if (const auto *A = get<Attr>())
return A->getRange();
return SourceRange(); return SourceRange();
} }

View File

@ -429,6 +429,11 @@ private:
[&] { return VisitorBase::TraverseNestedNameSpecifierLoc(NNSLocNode); }, [&] { return VisitorBase::TraverseNestedNameSpecifierLoc(NNSLocNode); },
&Map.OtherParents); &Map.OtherParents);
} }
bool TraverseAttr(Attr *AttrNode) {
return TraverseNode(
AttrNode, AttrNode, [&] { return VisitorBase::TraverseAttr(AttrNode); },
&Map.PointerParents);
}
// Using generic TraverseNode for Stmt would prevent data-recursion. // Using generic TraverseNode for Stmt would prevent data-recursion.
bool dataTraverseStmtPre(Stmt *StmtNode) { bool dataTraverseStmtPre(Stmt *StmtNode) {

View File

@ -133,6 +133,8 @@ public:
else if (const TemplateArgumentLoc *TALoc = else if (const TemplateArgumentLoc *TALoc =
DynNode.get<TemplateArgumentLoc>()) DynNode.get<TemplateArgumentLoc>())
traverse(*TALoc); traverse(*TALoc);
else if (const Attr *A = DynNode.get<Attr>())
traverse(*A);
// FIXME: Add other base types after adding tests. // FIXME: Add other base types after adding tests.
// It's OK to always overwrite the bound nodes, as if there was // It's OK to always overwrite the bound nodes, as if there was
@ -263,6 +265,15 @@ public:
return match(*Node->getLHS()) && match(*Node->getRHS()); return match(*Node->getLHS()) && match(*Node->getRHS());
} }
bool TraverseAttr(Attr *A) {
if (A == nullptr ||
(A->isImplicit() &&
Finder->getASTContext().getParentMapContext().getTraversalKind() ==
TK_IgnoreUnlessSpelledInSource))
return true;
ScopedIncrement ScopedDepth(&CurrentDepth);
return traverse(*A);
}
bool TraverseLambdaExpr(LambdaExpr *Node) { bool TraverseLambdaExpr(LambdaExpr *Node) {
if (!Finder->isTraversalIgnoringImplicitNodes()) if (!Finder->isTraversalIgnoringImplicitNodes())
return VisitorBase::TraverseLambdaExpr(Node); return VisitorBase::TraverseLambdaExpr(Node);
@ -345,6 +356,9 @@ private:
bool baseTraverse(TemplateArgumentLoc TAL) { bool baseTraverse(TemplateArgumentLoc TAL) {
return VisitorBase::TraverseTemplateArgumentLoc(TAL); return VisitorBase::TraverseTemplateArgumentLoc(TAL);
} }
bool baseTraverse(const Attr &AttrNode) {
return VisitorBase::TraverseAttr(const_cast<Attr *>(&AttrNode));
}
// Sets 'Matched' to true if 'Matcher' matches 'Node' and: // Sets 'Matched' to true if 'Matcher' matches 'Node' and:
// 0 < CurrentDepth <= MaxDepth. // 0 < CurrentDepth <= MaxDepth.
@ -489,6 +503,7 @@ public:
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS); bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS);
bool TraverseConstructorInitializer(CXXCtorInitializer *CtorInit); bool TraverseConstructorInitializer(CXXCtorInitializer *CtorInit);
bool TraverseTemplateArgumentLoc(TemplateArgumentLoc TAL); bool TraverseTemplateArgumentLoc(TemplateArgumentLoc TAL);
bool TraverseAttr(Attr *AttrNode);
bool dataTraverseNode(Stmt *S, DataRecursionQueue *Queue) { bool dataTraverseNode(Stmt *S, DataRecursionQueue *Queue) {
if (auto *RF = dyn_cast<CXXForRangeStmt>(S)) { if (auto *RF = dyn_cast<CXXForRangeStmt>(S)) {
@ -694,6 +709,8 @@ public:
match(*N); match(*N);
} else if (auto *N = Node.get<TemplateArgumentLoc>()) { } else if (auto *N = Node.get<TemplateArgumentLoc>()) {
match(*N); match(*N);
} else if (auto *N = Node.get<Attr>()) {
match(*N);
} }
} }
@ -894,6 +911,9 @@ private:
void matchDispatch(const TemplateArgumentLoc *Node) { void matchDispatch(const TemplateArgumentLoc *Node) {
matchWithoutFilter(*Node, Matchers->TemplateArgumentLoc); matchWithoutFilter(*Node, Matchers->TemplateArgumentLoc);
} }
void matchDispatch(const Attr *Node) {
matchWithoutFilter(*Node, Matchers->Attr);
}
void matchDispatch(const void *) { /* Do nothing. */ } void matchDispatch(const void *) { /* Do nothing. */ }
/// @} /// @}
@ -1300,6 +1320,11 @@ bool MatchASTVisitor::TraverseTemplateArgumentLoc(TemplateArgumentLoc Loc) {
return RecursiveASTVisitor<MatchASTVisitor>::TraverseTemplateArgumentLoc(Loc); return RecursiveASTVisitor<MatchASTVisitor>::TraverseTemplateArgumentLoc(Loc);
} }
bool MatchASTVisitor::TraverseAttr(Attr *AttrNode) {
match(*AttrNode);
return RecursiveASTVisitor<MatchASTVisitor>::TraverseAttr(AttrNode);
}
class MatchASTConsumer : public ASTConsumer { class MatchASTConsumer : public ASTConsumer {
public: public:
MatchASTConsumer(MatchFinder *Finder, MatchASTConsumer(MatchFinder *Finder,
@ -1394,6 +1419,12 @@ void MatchFinder::addMatcher(const TemplateArgumentLocMatcher &NodeMatch,
Matchers.AllCallbacks.insert(Action); Matchers.AllCallbacks.insert(Action);
} }
void MatchFinder::addMatcher(const AttrMatcher &AttrMatch,
MatchCallback *Action) {
Matchers.Attr.emplace_back(AttrMatch, Action);
Matchers.AllCallbacks.insert(Action);
}
bool MatchFinder::addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch, bool MatchFinder::addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch,
MatchCallback *Action) { MatchCallback *Action) {
if (NodeMatch.canConvertTo<Decl>()) { if (NodeMatch.canConvertTo<Decl>()) {
@ -1420,6 +1451,9 @@ bool MatchFinder::addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch,
} else if (NodeMatch.canConvertTo<TemplateArgumentLoc>()) { } else if (NodeMatch.canConvertTo<TemplateArgumentLoc>()) {
addMatcher(NodeMatch.convertTo<TemplateArgumentLoc>(), Action); addMatcher(NodeMatch.convertTo<TemplateArgumentLoc>(), Action);
return true; return true;
} else if (NodeMatch.canConvertTo<Attr>()) {
addMatcher(NodeMatch.convertTo<Attr>(), Action);
return true;
} }
return false; return false;
} }

View File

@ -1000,19 +1000,20 @@ const internal::ArgumentAdaptingMatcherFunc<internal::ForEachDescendantMatcher>
forEachDescendant = {}; forEachDescendant = {};
const internal::ArgumentAdaptingMatcherFunc< const internal::ArgumentAdaptingMatcherFunc<
internal::HasParentMatcher, internal::HasParentMatcher,
internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc>, internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc, Attr>,
internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc>> internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc, Attr>>
hasParent = {}; hasParent = {};
const internal::ArgumentAdaptingMatcherFunc< const internal::ArgumentAdaptingMatcherFunc<
internal::HasAncestorMatcher, internal::HasAncestorMatcher,
internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc>, internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc, Attr>,
internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc>> internal::TypeList<Decl, NestedNameSpecifierLoc, Stmt, TypeLoc, Attr>>
hasAncestor = {}; hasAncestor = {};
const internal::VariadicOperatorMatcherFunc<1, 1> unless = { const internal::VariadicOperatorMatcherFunc<1, 1> unless = {
internal::DynTypedMatcher::VO_UnaryNot}; internal::DynTypedMatcher::VO_UnaryNot};
const internal::VariadicAllOfMatcher<NestedNameSpecifier> nestedNameSpecifier; const internal::VariadicAllOfMatcher<NestedNameSpecifier> nestedNameSpecifier;
const internal::VariadicAllOfMatcher<NestedNameSpecifierLoc> const internal::VariadicAllOfMatcher<NestedNameSpecifierLoc>
nestedNameSpecifierLoc; nestedNameSpecifierLoc;
const internal::VariadicAllOfMatcher<Attr> attr;
const internal::VariadicDynCastAllOfMatcher<Stmt, CUDAKernelCallExpr> const internal::VariadicDynCastAllOfMatcher<Stmt, CUDAKernelCallExpr>
cudaKernelCallExpr; cudaKernelCallExpr;
const AstTypeMatcher<BuiltinType> builtinType; const AstTypeMatcher<BuiltinType> builtinType;

View File

@ -142,6 +142,7 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(asmStmt); REGISTER_MATCHER(asmStmt);
REGISTER_MATCHER(atomicExpr); REGISTER_MATCHER(atomicExpr);
REGISTER_MATCHER(atomicType); REGISTER_MATCHER(atomicType);
REGISTER_MATCHER(attr);
REGISTER_MATCHER(autoType); REGISTER_MATCHER(autoType);
REGISTER_MATCHER(autoreleasePoolStmt) REGISTER_MATCHER(autoreleasePoolStmt)
REGISTER_MATCHER(binaryConditionalOperator); REGISTER_MATCHER(binaryConditionalOperator);

View File

@ -132,6 +132,7 @@ TEST(ASTNodeKind, Name) {
VERIFY_NAME(CallExpr); VERIFY_NAME(CallExpr);
VERIFY_NAME(Type); VERIFY_NAME(Type);
VERIFY_NAME(ConstantArrayType); VERIFY_NAME(ConstantArrayType);
VERIFY_NAME(NonNullAttr);
#undef VERIFY_NAME #undef VERIFY_NAME
} }
@ -160,6 +161,13 @@ TEST(DynTypedNode, NNSLocSourceRange) {
nestedNameSpecifierLoc())); nestedNameSpecifierLoc()));
} }
TEST(DynTypedNode, AttrSourceRange) {
RangeVerifier<DynTypedNode> Verifier;
Verifier.expectRange(1, 31, 1, 31);
EXPECT_TRUE(Verifier.match("void x(char *y __attribute__((nonnull)) );",
ast_matchers::attr()));
}
TEST(DynTypedNode, DeclDump) { TEST(DynTypedNode, DeclDump) {
DumpVerifier Verifier; DumpVerifier Verifier;
Verifier.expectSubstring("FunctionDecl"); Verifier.expectSubstring("FunctionDecl");

View File

@ -1885,6 +1885,29 @@ TEST_P(ASTMatchersTest, NestedNameSpecifier) {
nestedNameSpecifier())); nestedNameSpecifier()));
} }
TEST_P(ASTMatchersTest, Attr) {
// Windows adds some implicit attributes.
bool AutomaticAttributes = StringRef(GetParam().Target).contains("win32");
if (GetParam().isCXX11OrLater()) {
EXPECT_TRUE(matches("struct [[clang::warn_unused_result]] F{};", attr()));
// Unknown attributes are not parsed into an AST node.
if (!AutomaticAttributes)
EXPECT_TRUE(notMatches("int x [[unknownattr]];", attr()));
}
}
if (GetParam().isCXX17OrLater()) {
EXPECT_TRUE(matches("struct [[nodiscard]] F{};", attr()));
}
EXPECT_TRUE(matches("int x(int * __attribute__((nonnull)) );", attr()));
if (!AutomaticAttributes) {
EXPECT_TRUE(notMatches("struct F{}; int x(int *);", attr()));
// Some known attributes are not parsed into an AST node.
EXPECT_TRUE(notMatches("typedef int x __attribute__((ext_vector_type(1)));",
attr()));
}
}
TEST_P(ASTMatchersTest, NullStmt) { TEST_P(ASTMatchersTest, NullStmt) {
EXPECT_TRUE(matches("void f() {int i;;}", nullStmt())); EXPECT_TRUE(matches("void f() {int i;;}", nullStmt()));
EXPECT_TRUE(notMatches("void f() {int i;}", nullStmt())); EXPECT_TRUE(notMatches("void f() {int i;}", nullStmt()));

View File

@ -298,7 +298,7 @@ testing::AssertionResult matchAndVerifyResultConditionally(
// Some tests use typeof, which is a gnu extension. Using an explicit // Some tests use typeof, which is a gnu extension. Using an explicit
// unknown-unknown triple is good for a large speedup, because it lets us // unknown-unknown triple is good for a large speedup, because it lets us
// avoid constructing a full system triple. // avoid constructing a full system triple.
std::vector<std::string> Args = {"-std=gnu++98", "-target", std::vector<std::string> Args = {"-std=gnu++11", "-target",
"i386-unknown-unknown"}; "i386-unknown-unknown"};
if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) { if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) {
return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "ASTMatchersTest.h" #include "ASTMatchersTest.h"
#include "clang/AST/Attrs.inc"
#include "clang/AST/PrettyPrinter.h" #include "clang/AST/PrettyPrinter.h"
#include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h" #include "clang/ASTMatchers/ASTMatchers.h"
@ -2820,7 +2821,7 @@ B func1() { return 42; }
auto M = expr(unless(integerLiteral(equals(24)))).bind("intLit"); auto M = expr(unless(integerLiteral(equals(24)))).bind("intLit");
EXPECT_TRUE(matchAndVerifyResultTrue( EXPECT_TRUE(matchAndVerifyResultTrue(
Code, traverse(TK_AsIs, M), Code, traverse(TK_AsIs, M),
std::make_unique<VerifyIdIsBoundTo<Expr>>("intLit", 7))); std::make_unique<VerifyIdIsBoundTo<Expr>>("intLit", 6)));
EXPECT_TRUE(matchAndVerifyResultTrue( EXPECT_TRUE(matchAndVerifyResultTrue(
Code, traverse(TK_IgnoreUnlessSpelledInSource, M), Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
std::make_unique<VerifyIdIsBoundTo<Expr>>("intLit", 1))); std::make_unique<VerifyIdIsBoundTo<Expr>>("intLit", 1)));
@ -2866,7 +2867,7 @@ B func1() { return 42; }
auto M = expr().bind("allExprs"); auto M = expr().bind("allExprs");
EXPECT_TRUE(matchAndVerifyResultTrue( EXPECT_TRUE(matchAndVerifyResultTrue(
Code, traverse(TK_AsIs, M), Code, traverse(TK_AsIs, M),
std::make_unique<VerifyIdIsBoundTo<Expr>>("allExprs", 7))); std::make_unique<VerifyIdIsBoundTo<Expr>>("allExprs", 6)));
EXPECT_TRUE(matchAndVerifyResultTrue( EXPECT_TRUE(matchAndVerifyResultTrue(
Code, traverse(TK_IgnoreUnlessSpelledInSource, M), Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
std::make_unique<VerifyIdIsBoundTo<Expr>>("allExprs", 1))); std::make_unique<VerifyIdIsBoundTo<Expr>>("allExprs", 1)));
@ -5446,6 +5447,24 @@ TEST(NNSLoc, NestedNameSpecifierLocsAsDescendants) {
// Nested names: a, a::A and a::A::B. // Nested names: a, a::A and a::A::B.
std::make_unique<VerifyIdIsBoundTo<NestedNameSpecifierLoc>>("x", 3))); std::make_unique<VerifyIdIsBoundTo<NestedNameSpecifierLoc>>("x", 3)));
} }
TEST(Attr, AttrsAsDescendants) {
StringRef Fragment = "namespace a { struct [[clang::warn_unused_result]] "
"F{}; [[noreturn]] void foo(); }";
EXPECT_TRUE(matches(Fragment, namespaceDecl(hasDescendant(attr()))));
EXPECT_TRUE(matchAndVerifyResultTrue(
Fragment,
namespaceDecl(hasName("a"),
forEachDescendant(attr(unless(isImplicit())).bind("x"))),
std::make_unique<VerifyIdIsBoundTo<Attr>>("x", 2)));
}
TEST(Attr, ParentsOfAttrs) {
StringRef Fragment =
"namespace a { struct [[clang::warn_unused_result]] F{}; }";
EXPECT_TRUE(matches(Fragment, attr(hasAncestor(namespaceDecl()))));
}
template <typename T> class VerifyMatchOnNode : public BoundNodesCallback { template <typename T> class VerifyMatchOnNode : public BoundNodesCallback {
public: public:
VerifyMatchOnNode(StringRef Id, const internal::Matcher<T> &InnerMatcher, VerifyMatchOnNode(StringRef Id, const internal::Matcher<T> &InnerMatcher,