diff --git a/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp b/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp --- a/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp +++ b/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp @@ -442,7 +442,7 @@ hasDeclaration(DeclMatcher), unless(templateSpecializationType()))))), hasParent(nestedNameSpecifierLoc()), - hasAncestor(isImplicit()), + hasAncestor(decl(isImplicit())), hasAncestor(UsingShadowDeclInClass), hasAncestor(functionDecl(isDefaulted())))), hasAncestor(decl().bind("dc"))) @@ -466,7 +466,7 @@ hasAncestor(decl(IsInMovedNs).bind("dc")), loc(nestedNameSpecifier( specifiesType(hasDeclaration(DeclMatcher.bind("from_decl"))))), - unless(anyOf(hasAncestor(isImplicit()), + unless(anyOf(hasAncestor(decl(isImplicit())), hasAncestor(UsingShadowDeclInClass), hasAncestor(functionDecl(isDefaulted())), hasAncestor(typeLoc(loc(qualType(hasDeclaration( @@ -495,7 +495,7 @@ hasAncestor(cxxRecordDecl()))), hasParent(namespaceDecl())); Finder->addMatcher(expr(hasAncestor(decl().bind("dc")), IsInMovedNs, - unless(hasAncestor(isImplicit())), + unless(hasAncestor(decl(isImplicit()))), anyOf(callExpr(callee(FuncMatcher)).bind("call"), declRefExpr(to(FuncMatcher.bind("func_decl"))) .bind("func_ref"))), diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp @@ -38,12 +38,12 @@ void ProBoundsConstantArrayIndexCheck::registerMatchers(MatchFinder *Finder) { // Note: if a struct contains an array member, the compiler-generated // constructor has an arraySubscriptExpr. - Finder->addMatcher( - arraySubscriptExpr( - hasBase(ignoringImpCasts(hasType(constantArrayType().bind("type")))), - hasIndex(expr().bind("index")), unless(hasAncestor(isImplicit()))) - .bind("expr"), - this); + Finder->addMatcher(arraySubscriptExpr(hasBase(ignoringImpCasts(hasType( + constantArrayType().bind("type")))), + hasIndex(expr().bind("index")), + unless(hasAncestor(decl(isImplicit())))) + .bind("expr"), + this); Finder->addMatcher( cxxOperatorCallExpr( diff --git a/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp b/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp --- a/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp @@ -196,12 +196,14 @@ // Sadly, we can't check whether the literal has suffix or not. // E.g. i32 suffix still results in 'BuiltinType::Kind::Int'. // And such an info is not stored in the *Literal itself. - Finder->addMatcher(traverse(TK_AsIs, - stmt(eachOf(integerLiteral().bind(IntegerLiteralCheck::Name), - floatLiteral().bind(FloatingLiteralCheck::Name)), - unless(anyOf(hasParent(userDefinedLiteral()), - hasAncestor(isImplicit()), - hasAncestor(substNonTypeTemplateParmExpr()))))), + Finder->addMatcher( + traverse( + TK_AsIs, + stmt(eachOf(integerLiteral().bind(IntegerLiteralCheck::Name), + floatLiteral().bind(FloatingLiteralCheck::Name)), + unless(anyOf(hasParent(userDefinedLiteral()), + hasAncestor(decl(isImplicit())), + hasAncestor(substNonTypeTemplateParmExpr()))))), this); } diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -531,6 +531,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.
 
@@ -2599,6 +2617,12 @@
 
+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.
@@ -2975,7 +2999,7 @@
 
 
 Matcher<CXXDependentScopeMemberExpr>hasMemberNamestd::string N
-
Matches template-dependent, but known, member names
+
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.
@@ -3597,8 +3621,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. @@ -732,9 +733,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(); } @@ -3254,8 +3256,8 @@ /// Usable as: Any Matcher extern const internal::ArgumentAdaptingMatcherFunc< internal::HasParentMatcher, - internal::TypeList, - internal::TypeList> + internal::TypeList, + internal::TypeList> hasParent; /// Matches AST nodes that have an ancestor that matches the provided @@ -3271,8 +3273,8 @@ /// Usable as: Any Matcher extern const internal::ArgumentAdaptingMatcherFunc< internal::HasAncestorMatcher, - internal::TypeList, - internal::TypeList> + internal::TypeList, + internal::TypeList> hasAncestor; /// Matches if the provided matcher does not match. @@ -6833,6 +6835,24 @@ 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; + /// 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 @@ -686,7 +686,8 @@ std::is_base_of::value || std::is_base_of::value || std::is_base_of::value || - std::is_base_of::value, + std::is_base_of::value || + std::is_base_of::value, "unsupported type for recursive matching"); return matchesChildOf(DynTypedNode::create(Node), getASTContext(), Matcher, Builder, Traverse, Bind); @@ -700,7 +701,8 @@ std::is_base_of::value || std::is_base_of::value || std::is_base_of::value || - std::is_base_of::value, + std::is_base_of::value || + std::is_base_of::value, "unsupported type for recursive matching"); return matchesDescendantOf(DynTypedNode::create(Node), getASTContext(), Matcher, Builder, Bind); @@ -714,7 +716,8 @@ static_assert(std::is_base_of::value || std::is_base_of::value || std::is_base_of::value || - std::is_base_of::value, + std::is_base_of::value || + std::is_base_of::value, "type not allowed for recursive matching"); return matchesAncestorOf(DynTypedNode::create(Node), getASTContext(), Matcher, Builder, MatchMode); @@ -863,7 +866,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 @@ -1083,7 +1086,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; @@ -1126,7 +1130,7 @@ /// Useful for matchers like \c anything and \c unless. using AllNodeBaseTypes = TypeList; + Type, TypeLoc, CXXCtorInitializer, Attr>; /// Helper meta-function to extract the argument out of a function of /// type void(Arg). @@ -1141,7 +1145,7 @@ using AdaptativeDefaultFromTypes = AllNodeBaseTypes; using AdaptativeDefaultToTypes = TypeList; + QualType, Attr>; /// All types that are supported by HasDeclarationMatcher above. using HasDeclarationSupportedTypes = 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/AST/ParentMapContext.cpp b/clang/lib/AST/ParentMapContext.cpp --- a/clang/lib/AST/ParentMapContext.cpp +++ b/clang/lib/AST/ParentMapContext.cpp @@ -307,6 +307,11 @@ [&] { return VisitorBase::TraverseNestedNameSpecifierLoc(NNSLocNode); }, &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. bool dataTraverseStmtPre(Stmt *StmtNode) { 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 @@ -134,6 +134,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 @@ -241,6 +243,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->isTraversalIgnoringImplicitNodes()) return VisitorBase::TraverseLambdaExpr(Node); @@ -323,6 +334,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. @@ -468,6 +482,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, @@ -594,6 +609,8 @@ match(*N); } else if (auto *N = Node.get()) { match(*N); + } else if (auto *N = Node.get()) { + match(*N); } } @@ -773,6 +790,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. */ } /// @} @@ -1174,6 +1194,11 @@ return RecursiveASTVisitor::TraverseTemplateArgumentLoc(Loc); } +bool MatchASTVisitor::TraverseAttr(Attr *AttrNode) { + match(*AttrNode); + return RecursiveASTVisitor::TraverseAttr(AttrNode); +} + class MatchASTConsumer : public ASTConsumer { public: MatchASTConsumer(MatchFinder *Finder, @@ -1256,6 +1281,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()) { @@ -1282,6 +1313,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 @@ -970,19 +970,20 @@ forEachDescendant = {}; const internal::ArgumentAdaptingMatcherFunc< internal::HasParentMatcher, - internal::TypeList, - internal::TypeList> + internal::TypeList, + internal::TypeList> hasParent = {}; const internal::ArgumentAdaptingMatcherFunc< internal::HasAncestorMatcher, - internal::TypeList, - internal::TypeList> + internal::TypeList, + internal::TypeList> hasAncestor = {}; const internal::VariadicOperatorMatcherFunc<1, 1> unless = { internal::DynTypedMatcher::VO_UnaryNot}; 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,19 @@ nestedNameSpecifier())); } +TEST_P(ASTMatchersTest, Attr) { + if (GetParam().isCXX11OrLater()) { + EXPECT_TRUE(matches("struct [[clang::warn_unused_result]] F{};", attr())); + } + if (GetParam().isCXX17OrLater()) { + EXPECT_TRUE(matches("struct [[nodiscard]] F{};", attr())); + } + EXPECT_TRUE(matches("int x(int * __attribute__((nonnull)) );", attr())); + // 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())); diff --git a/clang/unittests/ASTMatchers/ASTMatchersTest.h b/clang/unittests/ASTMatchers/ASTMatchersTest.h --- a/clang/unittests/ASTMatchers/ASTMatchersTest.h +++ b/clang/unittests/ASTMatchers/ASTMatchersTest.h @@ -298,7 +298,7 @@ // 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 // avoid constructing a full system triple. - std::vector Args = {"-std=gnu++98", "-target", + std::vector Args = {"-std=gnu++11", "-target", "i386-unknown-unknown"}; if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) { return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "ASTMatchersTest.h" +#include "clang/AST/Attrs.inc" #include "clang/AST/PrettyPrinter.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" @@ -3959,6 +3960,24 @@ // Nested names: a, a::A and a::A::B. std::make_unique>("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>("x", 2))); +} + +TEST(Attr, ParentsOfAttrs) { + StringRef Fragment = + "namespace a { struct [[clang::warn_unused_result]] F{}; }"; + EXPECT_TRUE(matches(Fragment, attr(hasAncestor(namespaceDecl())))); +} + template class VerifyMatchOnNode : public BoundNodesCallback { public: VerifyMatchOnNode(StringRef Id, const internal::Matcher &InnerMatcher,