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/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -582,6 +582,24 @@ +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.
 
@@ -2744,6 +2762,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.
@@ -3808,8 +3832,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 @@ -30,6 +30,9 @@ #define GEN_CLANG_CLAUSE_CLASS #define CLAUSE_CLASS(Enum, Str, Class) class Class; #include "llvm/Frontend/OpenMP/OMP.inc" +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 { @@ -66,6 +64,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. @@ -152,6 +151,9 @@ #define GEN_CLANG_CLAUSE_CLASS #define CLAUSE_CLASS(Enum, Str, Class) NKI_##Class, #include "llvm/Frontend/OpenMP/OMP.inc" + NKI_Attr, +#define ATTR(A) NKI_##A##Attr, +#include "clang/Basic/AttrList.inc" NKI_NumberOfKinds }; @@ -201,6 +203,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 @@ #define GEN_CLANG_CLAUSE_CLASS #define CLAUSE_CLASS(Enum, Str, Class) KIND_TO_KIND_ID(Class) #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 inline raw_ostream &operator<<(raw_ostream &OS, ASTNodeKind K) { @@ -486,6 +491,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 @@ -167,6 +167,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. @@ -219,6 +220,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 @@ -148,6 +148,7 @@ using CXXCtorInitializerMatcher = internal::Matcher; using TemplateArgumentMatcher = internal::Matcher; using TemplateArgumentLocMatcher = internal::Matcher; +using AttrMatcher = internal::Matcher; /// @} /// Matches any node. @@ -752,9 +753,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(); } @@ -3489,8 +3491,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 @@ -3506,8 +3508,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. @@ -7133,6 +7135,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 @@ -757,7 +757,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, Bind); @@ -771,7 +772,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); @@ -785,7 +787,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); @@ -954,7 +957,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 @@ -1175,7 +1178,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; @@ -1185,7 +1189,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). @@ -1212,7 +1216,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" @@ -44,6 +45,9 @@ #define GEN_CLANG_CLAUSE_CLASS #define CLAUSE_CLASS(Enum, Str, Class) {NKI_OMPClause, #Class}, #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 { @@ -134,7 +138,17 @@ llvm_unreachable("unexpected OpenMP clause kind"); #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, @@ -162,6 +176,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"; } @@ -195,5 +211,7 @@ return SourceRange(C->getBeginLoc(), C->getEndLoc()); if (const auto *CBS = get()) return CBS->getSourceRange(); + 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 @@ -429,6 +429,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 @@ -133,6 +133,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 @@ -263,6 +265,15 @@ 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) { if (!Finder->isTraversalIgnoringImplicitNodes()) return VisitorBase::TraverseLambdaExpr(Node); @@ -345,6 +356,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. @@ -489,6 +503,7 @@ bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS); bool TraverseConstructorInitializer(CXXCtorInitializer *CtorInit); bool TraverseTemplateArgumentLoc(TemplateArgumentLoc TAL); + bool TraverseAttr(Attr *AttrNode); bool dataTraverseNode(Stmt *S, DataRecursionQueue *Queue) { if (auto *RF = dyn_cast(S)) { @@ -694,6 +709,8 @@ match(*N); } else if (auto *N = Node.get()) { match(*N); + } else if (auto *N = Node.get()) { + match(*N); } } @@ -894,6 +911,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. */ } /// @} @@ -1300,6 +1320,11 @@ return RecursiveASTVisitor::TraverseTemplateArgumentLoc(Loc); } +bool MatchASTVisitor::TraverseAttr(Attr *AttrNode) { + match(*AttrNode); + return RecursiveASTVisitor::TraverseAttr(AttrNode); +} + class MatchASTConsumer : public ASTConsumer { public: MatchASTConsumer(MatchFinder *Finder, @@ -1394,6 +1419,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()) { @@ -1420,6 +1451,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 @@ -1000,19 +1000,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 @@ -142,6 +142,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 @@ -132,6 +132,7 @@ VERIFY_NAME(CallExpr); VERIFY_NAME(Type); VERIFY_NAME(ConstantArrayType); + VERIFY_NAME(NonNullAttr); #undef VERIFY_NAME } @@ -160,6 +161,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 @@ -1885,6 +1885,29 @@ 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) { 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" @@ -2820,7 +2821,7 @@ auto M = expr(unless(integerLiteral(equals(24)))).bind("intLit"); EXPECT_TRUE(matchAndVerifyResultTrue( Code, traverse(TK_AsIs, M), - std::make_unique>("intLit", 7))); + std::make_unique>("intLit", 6))); EXPECT_TRUE(matchAndVerifyResultTrue( Code, traverse(TK_IgnoreUnlessSpelledInSource, M), std::make_unique>("intLit", 1))); @@ -2866,7 +2867,7 @@ auto M = expr().bind("allExprs"); EXPECT_TRUE(matchAndVerifyResultTrue( Code, traverse(TK_AsIs, M), - std::make_unique>("allExprs", 7))); + std::make_unique>("allExprs", 6))); EXPECT_TRUE(matchAndVerifyResultTrue( Code, traverse(TK_IgnoreUnlessSpelledInSource, M), std::make_unique>("allExprs", 1))); @@ -5446,6 +5447,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,