diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -100,6 +100,24 @@ Return typeNameParameters +Matcher<Attr>attrMatcher<Attr>... +
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.
+
+ + Matcher<CXXCtorInitializer>cxxCtorInitializerMatcher<CXXCtorInitializer>...
Matches constructor initializers.
 
@@ -2168,6 +2186,26 @@
 
+Matcher<Attr>hasAttrNamellvm::StringRef Name +
Matches Attr nodes that have the specified attribute name.
+
+The attribute name is the token in the source code naming the attribute.
+Synonyms are not supported and scope qualifiers are ignored.
+
+Given
+  alignas(16) int bar;
+  struct [[clang::warn_unused_result]] Foo{};
+matches hasAttrName("alignas") and "warn_unused_result"
+(but not "nodiscard" or "clang::warn_unused_result").
+
+ + +Matcher<Attr>isImplicit +
Matches an entity that has been implicitly added by the compiler (e.g.
+implicit default/copy constructors).
+
+ + Matcher<BinaryOperator>hasAnyOperatorNameStringRef, ..., StringRef
Matches operator expressions (binary or unary) that have any of the
 specified names.
@@ -3090,8 +3128,8 @@
 
 
 Matcher<Decl>isImplicit
-
Matches a declaration that has been implicitly added
-by the compiler (eg. implicit default/copy constructors).
+
Matches an entity that has been implicitly added by the compiler (e.g.
+implicit default/copy constructors).
 
diff --git a/clang/include/clang/AST/ASTFwd.h b/clang/include/clang/AST/ASTFwd.h --- a/clang/include/clang/AST/ASTFwd.h +++ b/clang/include/clang/AST/ASTFwd.h @@ -29,7 +29,9 @@ class OMPClause; #define OMP_CLAUSE_CLASS(Enum, Str, Class) class Class; #include "llvm/Frontend/OpenMP/OMPKinds.def" - +class Attr; +#define ATTR(A) class A##Attr; +#include "clang/Basic/AttrList.inc" } // end namespace clang diff --git a/clang/include/clang/AST/ASTTypeTraits.h b/clang/include/clang/AST/ASTTypeTraits.h --- a/clang/include/clang/AST/ASTTypeTraits.h +++ b/clang/include/clang/AST/ASTTypeTraits.h @@ -25,10 +25,8 @@ #include "llvm/Support/AlignOf.h" namespace llvm { - class raw_ostream; - -} +} // namespace llvm namespace clang { @@ -70,6 +68,7 @@ static ASTNodeKind getFromNode(const Stmt &S); static ASTNodeKind getFromNode(const Type &T); 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. @@ -153,6 +152,9 @@ NKI_OMPClause, #define OMP_CLAUSE_CLASS(Enum, Str, Class) NKI_##Class, #include "llvm/Frontend/OpenMP/OMPKinds.def" + NKI_Attr, +#define ATTR(A) NKI_##A##Attr, +#include "clang/Basic/AttrList.inc" NKI_NumberOfKinds }; @@ -202,6 +204,7 @@ KIND_TO_KIND_ID(Stmt) KIND_TO_KIND_ID(Type) KIND_TO_KIND_ID(OMPClause) +KIND_TO_KIND_ID(Attr) KIND_TO_KIND_ID(CXXBaseSpecifier) #define DECL(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED##Decl) #include "clang/AST/DeclNodes.inc" @@ -211,6 +214,8 @@ #include "clang/AST/TypeNodes.inc" #define OMP_CLAUSE_CLASS(Enum, Str, Class) KIND_TO_KIND_ID(Class) #include "llvm/Frontend/OpenMP/OMPKinds.def" +#define ATTR(A) KIND_TO_KIND_ID(A##Attr) +#include "clang/Basic/AttrList.inc" #undef KIND_TO_KIND_ID inline raw_ostream &operator<<(raw_ostream &OS, ASTNodeKind K) { @@ -487,6 +492,11 @@ T, std::enable_if_t::value>> : public DynCastPtrConverter {}; +template +struct DynTypedNode::BaseConverter< + T, std::enable_if_t::value>> + : public DynCastPtrConverter {}; + template <> struct DynTypedNode::BaseConverter< NestedNameSpecifier, void> : public PtrConverter {}; diff --git a/clang/include/clang/ASTMatchers/ASTMatchFinder.h b/clang/include/clang/ASTMatchers/ASTMatchFinder.h --- a/clang/include/clang/ASTMatchers/ASTMatchFinder.h +++ b/clang/include/clang/ASTMatchers/ASTMatchFinder.h @@ -161,6 +161,7 @@ MatchCallback *Action); void addMatcher(const TemplateArgumentLocMatcher &NodeMatch, MatchCallback *Action); + void addMatcher(const AttrMatcher &NodeMatch, MatchCallback *Action); /// @} /// Adds a matcher to execute when running over the AST. @@ -213,6 +214,7 @@ std::vector> CtorInit; std::vector> TemplateArgumentLoc; + std::vector> Attr; /// All the callbacks in one container to simplify iteration. llvm::SmallPtrSet AllCallbacks; }; diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -147,6 +147,7 @@ using CXXCtorInitializerMatcher = internal::Matcher; using TemplateArgumentMatcher = internal::Matcher; using TemplateArgumentLocMatcher = internal::Matcher; +using AttrMatcher = internal::Matcher; /// @} /// Matches any node. @@ -733,9 +734,10 @@ InnerMatcher.matches(*Decl, Finder, Builder)); } -/// Matches a declaration that has been implicitly added -/// by the compiler (eg. implicit default/copy constructors). -AST_MATCHER(Decl, isImplicit) { +/// 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)) { return Node.isImplicit(); } @@ -6729,6 +6731,44 @@ 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; + +/// Matches Attr nodes that have the specified attribute name. +/// +/// The attribute name is the token in the source code naming the attribute. +/// Synonyms are not supported and scope qualifiers are ignored. +/// +/// Deprecated. This should really be merged with \c hasName. +/// +/// Given +/// \code +/// alignas(16) int bar; +/// struct [[clang::warn_unused_result]] Foo{}; +/// \endcode +/// matches hasAttrName("alignas") and "warn_unused_result" +/// (but not "nodiscard" or "clang::warn_unused_result"). +AST_MATCHER_P(Attr, hasAttrName, std::string, Name) { + if (const IdentifierInfo *II = Node.getAttrName()) + return II->isStr(Name); + return false; +} + /// Overloads for the \c equalsNode matcher. /// FIXME: Implement for other node types. /// @{ diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -726,7 +726,7 @@ bool matchesNode(const NamedDecl &Node) const override; - private: +private: /// Unqualified match routine. /// /// It is much faster than the full match, but it only works for unqualified @@ -944,7 +944,8 @@ std::is_same::value || std::is_same::value || std::is_same::value || - std::is_same::value; + std::is_same::value || + std::is_same::value; }; template const bool IsBaseType::value; diff --git a/clang/lib/AST/ASTTypeTraits.cpp b/clang/lib/AST/ASTTypeTraits.cpp --- a/clang/lib/AST/ASTTypeTraits.cpp +++ b/clang/lib/AST/ASTTypeTraits.cpp @@ -14,6 +14,7 @@ #include "clang/AST/ASTTypeTraits.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/OpenMPClause.h" @@ -21,28 +22,31 @@ using namespace clang; const ASTNodeKind::KindInfo ASTNodeKind::AllKindInfo[] = { - { NKI_None, "" }, - { NKI_None, "TemplateArgument" }, - { NKI_None, "TemplateArgumentLoc" }, - { NKI_None, "TemplateName" }, - { NKI_None, "NestedNameSpecifierLoc" }, - { NKI_None, "QualType" }, - { NKI_None, "TypeLoc" }, - { NKI_None, "CXXBaseSpecifier" }, - { NKI_None, "CXXCtorInitializer" }, - { NKI_None, "NestedNameSpecifier" }, - { NKI_None, "Decl" }, + {NKI_None, ""}, + {NKI_None, "TemplateArgument"}, + {NKI_None, "TemplateArgumentLoc"}, + {NKI_None, "TemplateName"}, + {NKI_None, "NestedNameSpecifierLoc"}, + {NKI_None, "QualType"}, + {NKI_None, "TypeLoc"}, + {NKI_None, "CXXBaseSpecifier"}, + {NKI_None, "CXXCtorInitializer"}, + {NKI_None, "NestedNameSpecifier"}, + {NKI_None, "Decl"}, #define DECL(DERIVED, BASE) { NKI_##BASE, #DERIVED "Decl" }, #include "clang/AST/DeclNodes.inc" - { NKI_None, "Stmt" }, + {NKI_None, "Stmt"}, #define STMT(DERIVED, BASE) { NKI_##BASE, #DERIVED }, #include "clang/AST/StmtNodes.inc" - { NKI_None, "Type" }, + {NKI_None, "Type"}, #define TYPE(DERIVED, BASE) { NKI_##BASE, #DERIVED "Type" }, #include "clang/AST/TypeNodes.inc" - { NKI_None, "OMPClause" }, + {NKI_None, "OMPClause"}, #define OMP_CLAUSE_CLASS(Enum, Str, Class) {NKI_OMPClause, #Class}, #include "llvm/Frontend/OpenMP/OMPKinds.def" + {NKI_None, "Attr"}, +#define ATTR(A) {NKI_Attr, #A "Attr"}, +#include "clang/Basic/AttrList.inc" }; bool ASTNodeKind::isBaseOf(ASTNodeKind Other, unsigned *Distance) const { @@ -123,7 +127,17 @@ break; #include "llvm/Frontend/OpenMP/OMPKinds.def" } - 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, @@ -151,6 +165,8 @@ S->printPretty(OS, nullptr, PP); else if (const Type *T = get()) QualType(T, 0).print(OS, PP); + else if (const Attr *A = get()) + A->printPretty(OS, PP); else OS << "Unable to print values of type " << NodeKind.asStringRef() << "\n"; } @@ -182,5 +198,7 @@ return TAL->getSourceRange(); if (const auto *C = get()) return SourceRange(C->getBeginLoc(), C->getEndLoc()); + if (const auto *A = get()) + return A->getRange(); return SourceRange(); } diff --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp b/clang/lib/ASTMatchers/ASTMatchFinder.cpp --- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp @@ -131,6 +131,8 @@ else if (const TemplateArgumentLoc *TALoc = DynNode.get()) traverse(*TALoc); + else if (const Attr *A = DynNode.get()) + traverse(*A); // FIXME: Add other base types after adding tests. // It's OK to always overwrite the bound nodes, as if there was @@ -231,6 +233,15 @@ ScopedIncrement ScopedDepth(&CurrentDepth); return traverse(TAL); } + 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) { if (Finder->getASTContext().getParentMapContext().getTraversalKind() != TK_IgnoreUnlessSpelledInSource) @@ -314,6 +325,9 @@ bool baseTraverse(TemplateArgumentLoc TAL) { return VisitorBase::TraverseTemplateArgumentLoc(TAL); } + bool baseTraverse(const Attr &AttrNode) { + return VisitorBase::TraverseAttr(const_cast(&AttrNode)); + } // Sets 'Matched' to true if 'Matcher' matches 'Node' and: // 0 < CurrentDepth <= MaxDepth. @@ -458,6 +472,7 @@ bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS); bool TraverseConstructorInitializer(CXXCtorInitializer *CtorInit); bool TraverseTemplateArgumentLoc(TemplateArgumentLoc TAL); + bool TraverseAttr(Attr *AttrNode); // Matches children or descendants of 'Node' with 'BaseMatcher'. bool memoizedMatchesRecursively(const DynTypedNode &Node, ASTContext &Ctx, @@ -571,6 +586,8 @@ match(*N); } else if (auto *N = Node.get()) { match(*N); + } else if (auto *N = Node.get()) { + match(*N); } } @@ -697,6 +714,9 @@ void matchDispatch(const TemplateArgumentLoc *Node) { matchWithoutFilter(*Node, Matchers->TemplateArgumentLoc); } + void matchDispatch(const Attr *Node) { + matchWithoutFilter(*Node, Matchers->Attr); + } void matchDispatch(const void *) { /* Do nothing. */ } /// @} @@ -1068,6 +1088,11 @@ return RecursiveASTVisitor::TraverseTemplateArgumentLoc(Loc); } +bool MatchASTVisitor::TraverseAttr(Attr *AttrNode) { + match(*AttrNode); + return RecursiveASTVisitor::TraverseAttr(AttrNode); +} + class MatchASTConsumer : public ASTConsumer { public: MatchASTConsumer(MatchFinder *Finder, @@ -1150,6 +1175,12 @@ 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, MatchCallback *Action) { if (NodeMatch.canConvertTo()) { @@ -1176,6 +1207,9 @@ } else if (NodeMatch.canConvertTo()) { addMatcher(NodeMatch.convertTo(), Action); return true; + } else if (NodeMatch.canConvertTo()) { + addMatcher(NodeMatch.convertTo(), Action); + return true; } return false; } diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp --- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -973,6 +973,7 @@ const internal::VariadicAllOfMatcher nestedNameSpecifier; const internal::VariadicAllOfMatcher nestedNameSpecifierLoc; +const internal::VariadicAllOfMatcher attr; const internal::VariadicDynCastAllOfMatcher cudaKernelCallExpr; const AstTypeMatcher builtinType; diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -141,6 +141,7 @@ REGISTER_MATCHER(asmStmt); REGISTER_MATCHER(atomicExpr); REGISTER_MATCHER(atomicType); + REGISTER_MATCHER(attr); REGISTER_MATCHER(autoType); REGISTER_MATCHER(autoreleasePoolStmt) REGISTER_MATCHER(binaryConditionalOperator); diff --git a/clang/unittests/AST/ASTTypeTraitsTest.cpp b/clang/unittests/AST/ASTTypeTraitsTest.cpp --- a/clang/unittests/AST/ASTTypeTraitsTest.cpp +++ b/clang/unittests/AST/ASTTypeTraitsTest.cpp @@ -120,6 +120,7 @@ VERIFY_NAME(CallExpr); VERIFY_NAME(Type); VERIFY_NAME(ConstantArrayType); + VERIFY_NAME(NonNullAttr); #undef VERIFY_NAME } @@ -148,6 +149,13 @@ nestedNameSpecifierLoc())); } +TEST(DynTypedNode, AttrSourceRange) { + RangeVerifier Verifier; + Verifier.expectRange(1, 31, 1, 31); + EXPECT_TRUE(Verifier.match("void x(char *y __attribute__((nonnull)) );", + ast_matchers::attr())); +} + TEST(DynTypedNode, DeclDump) { DumpVerifier Verifier; Verifier.expectSubstring("FunctionDecl"); diff --git a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp @@ -1875,6 +1875,26 @@ nestedNameSpecifier())); } +TEST_P(ASTMatchersTest, Attr) { + if (GetParam().isCXX11OrLater()) { + EXPECT_TRUE(matches("struct [[clang::warn_unused_result]] F{};", + attr(hasAttrName("warn_unused_result")))); + EXPECT_TRUE(notMatches("struct [[clang::warn_unused_result]] F{};", + attr(hasAttrName("clang::warn_unused_result")))); + } + if (GetParam().isCXX17OrLater()) { + EXPECT_TRUE( + matches("struct [[nodiscard]] F{};", attr(hasAttrName("nodiscard")))); + EXPECT_TRUE(notMatches("struct [[nodiscard]] F{};", + attr(hasAttrName("warn_unused_result")))); + } + EXPECT_TRUE(matches("int x(int * __attribute__((nonnull)) );", + attr(hasAttrName("nonnull")))); + // On windows, some nodes have an implicit visibility attribute. + EXPECT_TRUE( + notMatches("struct F{}; int x(int *);", attr(unless(isImplicit())))); +} + TEST_P(ASTMatchersTest, NullStmt) { EXPECT_TRUE(matches("void f() {int i;;}", nullStmt())); EXPECT_TRUE(notMatches("void f() {int i;}", nullStmt()));