diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -1283,6 +1283,21 @@ +Matcher<Stmt>attributedStmtMatcher<AttributedStmt>... +
Matches the attribute(s) attached to a Stmt
+
+Given:
+  constexpr double pow(double x, long long n) noexcept {
+    if (n > 0) [[likely]]
+         return x * pow(x, n - 1);
+    else [[unlikely]]
+         return 1;
+  }
+attributedStmt() matches "[[likely]] return ...;" and
+"[[unlikely]] return 1;"
+
+ + Matcher<Stmt>autoreleasePoolStmtMatcher<ObjCAutoreleasePoolStmt>...
Matches an Objective-C autorelease pool statement.
 
@@ -2852,6 +2867,17 @@
 
+Matcher<AttributedStmt>hasAttrattr::Kind AttrKind +
Matches declaration that has a given attribute.
+
+Given
+  __attribute__((device)) void f() { ... }
+decl(hasAttr(clang::attr::CUDADevice)) matches the function declaration of
+f. If the matcher is used from clang-query, attr::Kind parameter should be
+passed as a quoted string. e.g., hasAttr("attr::CUDADevice").
+
+ + Matcher<Attr>isImplicit
Matches an entity that has been implicitly added by the compiler (e.g.
 implicit default/copy constructors).
@@ -6214,6 +6240,17 @@
 
+Matcher<AttributedStmt>hasSubStmtMatcher<Stmt> InnerMatcher +
Matches the statement an attribute is attached to.
+
+Example:
+  attributedStmt(hasSubStmt(returnStmt()))
+would match "return 1;" here:
+  else [[unlikely]]
+    return 1;
+
+ + Matcher<AutoType>hasDeducedTypeMatcher<Type>
Matches AutoType nodes where the deduced type is a specific type.
 
@@ -9799,5 +9836,3 @@
 
 
 
-
-
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
@@ -2699,6 +2699,40 @@
   return Node.size() == N;
 }
 
+/// Matches the attribute(s) attached to a Stmt
+///
+/// Given:
+/// \code
+///   constexpr double pow(double x, long long n) noexcept {
+///     if (n > 0) [[likely]]
+///          return x * pow(x, n - 1);
+///     else [[unlikely]]
+///          return 1;
+///   }
+/// \endcode
+/// attributedStmt() matches "[[likely]] return ...;" and
+/// "[[unlikely]] return 1;"
+extern const internal::VariadicDynCastAllOfMatcher
+    attributedStmt;
+
+/// Matches the statement an attribute is attached to.
+///
+/// Example:
+/// \code
+///   attributedStmt(hasSubStmt(returnStmt()))
+/// \endcode
+/// would match "return 1;" here:
+/// \code
+///   else [[unlikely]]
+///     return 1;
+/// \endcode
+AST_MATCHER_P(AttributedStmt, hasSubStmt, internal::Matcher,
+              InnerMatcher) {
+  const Stmt *Statement = Node.getSubStmt();
+  return Statement != nullptr &&
+         InnerMatcher.matches(*Statement, Finder, Builder);
+}
+
 /// Matches \c QualTypes in the clang AST.
 extern const internal::VariadicAllOfMatcher qualType;
 
@@ -7760,12 +7794,10 @@
 /// decl(hasAttr(clang::attr::CUDADevice)) matches the function declaration of
 /// f. If the matcher is used from clang-query, attr::Kind parameter should be
 /// passed as a quoted string. e.g., hasAttr("attr::CUDADevice").
-AST_MATCHER_P(Decl, hasAttr, attr::Kind, AttrKind) {
-  for (const auto *Attr : Node.attrs()) {
-    if (Attr->getKind() == AttrKind)
-      return true;
-  }
-  return false;
+AST_POLYMORPHIC_MATCHER_P(hasAttr,
+                          AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, AttributedStmt),
+                          attr::Kind, AttrKind) {
+  return internal::HasAttrMatcher::hasAttr(Node, AttrKind);
 }
 
 /// Matches the return value expression of a return statement
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
@@ -35,6 +35,7 @@
 #define LLVM_CLANG_ASTMATCHERS_ASTMATCHERSINTERNAL_H
 
 #include "clang/AST/ASTTypeTraits.h"
+#include "clang/AST/Attr.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclFriend.h"
@@ -2187,6 +2188,21 @@
   return Node.getSubStmt();
 }
 
+template  struct HasAttrMatcher {
+  static bool hasAttr(const Ty &Node, attr::Kind AttrKind) {
+    return llvm::any_of(Node.getAttrs(), [&](const Attr *A) {
+      return A->getKind() == AttrKind;
+    });
+  }
+};
+
+template <>
+inline bool HasAttrMatcher::hasAttr(const Decl &Node,
+                                          attr::Kind AttrKind) {
+  return llvm::any_of(Node.attrs(),
+                      [&](const Attr *A) { return A->getKind() == AttrKind; });
+}
+
 /// If \p Loc is (transitively) expanded from macro \p MacroName, returns the
 /// location (in the chain of expansions) at which \p MacroName was
 /// expanded. Since the macro may have been expanded inside a series of
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
@@ -981,6 +981,8 @@
     predefinedExpr;
 const internal::VariadicDynCastAllOfMatcher
     designatedInitExpr;
+const internal::VariadicDynCastAllOfMatcher
+    attributedStmt;
 const internal::VariadicOperatorMatcherFunc<
     2, std::numeric_limits::max()>
     eachOf = {internal::DynTypedMatcher::VO_EachOf};
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(atomicExpr);
   REGISTER_MATCHER(atomicType);
   REGISTER_MATCHER(attr);
+  REGISTER_MATCHER(attributedStmt);
   REGISTER_MATCHER(autoType);
   REGISTER_MATCHER(autoreleasePoolStmt)
   REGISTER_MATCHER(binaryConditionalOperator);
@@ -355,6 +356,7 @@
   REGISTER_MATCHER(hasSpecializedTemplate);
   REGISTER_MATCHER(hasStaticStorageDuration);
   REGISTER_MATCHER(hasStructuredBlock);
+  REGISTER_MATCHER(hasSubStmt);
   REGISTER_MATCHER(hasSyntacticForm);
   REGISTER_MATCHER(hasTargetDecl);
   REGISTER_MATCHER(hasTemplateArgument);
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
@@ -184,9 +184,9 @@
 
 TEST(ASTMatchersTestCUDA, HasAttrCUDA) {
   EXPECT_TRUE(matchesWithCuda("__attribute__((device)) void f() {}",
-                              hasAttr(clang::attr::CUDADevice)));
+                              decl(hasAttr(clang::attr::CUDADevice))));
   EXPECT_FALSE(notMatchesWithCuda("__attribute__((global)) void f() {}",
-                                  hasAttr(clang::attr::CUDAGlobal)));
+                                  decl(hasAttr(clang::attr::CUDAGlobal))));
 }
 
 TEST_P(ASTMatchersTest, ValueDecl) {
@@ -2509,6 +2509,37 @@
   EXPECT_FALSE(Finder.addDynamicMatcher(hasName("x"), nullptr));
 }
 
+TEST(ASTMatchersTest, AttributedStmtBasic) {
+  StringRef Code = "int foo() { [[likely]] return 1; }";
+  EXPECT_TRUE(
+      matchesConditionally(Code, attributedStmt(), true, {"-std=c++20"}));
+}
+
+TEST(ASTMatchersTest, AttributedStmt_hasAttr) {
+  StringRef Code = "int foo() { [[unlikely]] return 1; }";
+  EXPECT_TRUE(matchesConditionally(
+      Code, attributedStmt(hasAttr(attr::Unlikely)), true, {"-std=c++20"}));
+  EXPECT_FALSE(matchesConditionally(
+      Code, attributedStmt(hasAttr(attr::Builtin)), true, {"-std=c++20"}));
+}
+
+TEST(ASTMatchersTest, AttributedStmt_hasSubStmt) {
+  StringRef Code = "int foo() { [[likely]] return 1; }";
+  EXPECT_TRUE(matchesConditionally(
+      Code, attributedStmt(hasSubStmt(returnStmt())), true, {"-std=c++20"}));
+  EXPECT_FALSE(matchesConditionally(Code, attributedStmt(hasSubStmt(ifStmt())),
+                                    true, {"-std=c++20"}));
+}
+
+TEST(ASTMatchersTest, AttributedStmt_multipleAttrs) {
+  StringRef Code = "int foo();\n int main() {\n  [[clang::nomerge]] [[likely]] "
+                   "return foo();\n }";
+  EXPECT_TRUE(matchesConditionally(Code, attributedStmt(hasAttr(attr::Likely)),
+                                   true, {"-std=c++20"}));
+  EXPECT_TRUE(matchesConditionally(Code, attributedStmt(hasAttr(attr::NoMerge)),
+                                   true, {"-std=c++20"}));
+}
+
 TEST(MatchFinderAPI, MatchesDynamic) {
   StringRef SourceCode = "struct A { void f() {} };";
   auto Matcher = functionDecl(isDefinition()).bind("method");