[clang] [Clang] disallow # operators in attribute argument lists (PR #147308)
Oleksandr T. via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 17 13:46:01 PDT 2025
https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/147308
>From 8f1c383f8f84fb636af4a78e0ff504830f9272f5 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Mon, 7 Jul 2025 17:20:48 +0300
Subject: [PATCH 1/5] [Clang] disallow operator in attribute argument lists
---
clang/docs/ReleaseNotes.rst | 1 +
clang/include/clang/Basic/DiagnosticParseKinds.td | 2 ++
clang/lib/Parse/ParseDecl.cpp | 7 +++++++
clang/test/Parser/cxx0x-attributes.cpp | 5 +++++
4 files changed, 15 insertions(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index a6be59f1d6bd7..b8032ee9c03da 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -673,6 +673,7 @@ Improvements to Clang's diagnostics
false positives in exception-heavy code, though only simple patterns
are currently recognized.
+- Clang now rejects ``#`` operators in attribute argument lists. (#GH147217)
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 6c30da376dafb..b39e2a7359c22 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -830,6 +830,8 @@ def err_ms_property_expected_comma_or_rparen : Error<
"expected ',' or ')' at end of property accessor list">;
def err_ms_property_initializer : Error<
"property declaration cannot have a default member initializer">;
+def err_invalid_attribute_argument
+ : Error<"'%0' is not allowed in attribute argument lists">;
def err_assume_attr_expects_cond_expr : Error<
"use of this expression in an %0 attribute requires parentheses">;
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 7e739e09b15e8..059636653723c 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -488,6 +488,13 @@ unsigned Parser::ParseAttributeArgsCommon(
bool AttributeHasVariadicIdentifierArg =
attributeHasVariadicIdentifierArg(*AttrName, Form.getSyntax(), ScopeName);
+ if (Tok.is(tok::hash) || Tok.is(tok::hashhash)) {
+ Diag(Tok.getLocation(), diag::err_invalid_attribute_argument)
+ << PP.getSpelling(Tok);
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return 0;
+ }
+
// Interpret "kw_this" as an identifier if the attributed requests it.
if (ChangeKWThisToIdent && Tok.is(tok::kw_this))
Tok.setKind(tok::identifier);
diff --git a/clang/test/Parser/cxx0x-attributes.cpp b/clang/test/Parser/cxx0x-attributes.cpp
index 372a373a49ec5..d343cd4f3a93e 100644
--- a/clang/test/Parser/cxx0x-attributes.cpp
+++ b/clang/test/Parser/cxx0x-attributes.cpp
@@ -477,3 +477,8 @@ namespace P2361 {
}
alignas(int) struct AlignAsAttribute {}; // expected-error {{misplaced attributes; expected attributes here}}
+
+namespace GH147217 {
+ [[clang::annotate(#)]] void a(); // expected-error {{'#' is not allowed in attribute argument lists}}
+ [[clang::annotate(##)]] void b(); // expected-error {{'##' is not allowed in attribute argument lists}}
+}
>From 962f703e11a25f6ddf9b0ba5ac045456d5167242 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Mon, 7 Jul 2025 18:49:55 +0300
Subject: [PATCH 2/5] update diagnostic message
---
clang/include/clang/Basic/DiagnosticParseKinds.td | 2 +-
clang/test/Parser/cxx0x-attributes.cpp | 6 ++++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index b39e2a7359c22..d53ef7a93bfda 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -831,7 +831,7 @@ def err_ms_property_expected_comma_or_rparen : Error<
def err_ms_property_initializer : Error<
"property declaration cannot have a default member initializer">;
def err_invalid_attribute_argument
- : Error<"'%0' is not allowed in attribute argument lists">;
+ : Error<"'%0' is not allowed in an attribute argument list">;
def err_assume_attr_expects_cond_expr : Error<
"use of this expression in an %0 attribute requires parentheses">;
diff --git a/clang/test/Parser/cxx0x-attributes.cpp b/clang/test/Parser/cxx0x-attributes.cpp
index d343cd4f3a93e..f5a3039c4470c 100644
--- a/clang/test/Parser/cxx0x-attributes.cpp
+++ b/clang/test/Parser/cxx0x-attributes.cpp
@@ -479,6 +479,8 @@ namespace P2361 {
alignas(int) struct AlignAsAttribute {}; // expected-error {{misplaced attributes; expected attributes here}}
namespace GH147217 {
- [[clang::annotate(#)]] void a(); // expected-error {{'#' is not allowed in attribute argument lists}}
- [[clang::annotate(##)]] void b(); // expected-error {{'##' is not allowed in attribute argument lists}}
+ [[clang::annotate(#)]] void a(); // expected-error {{'#' is not allowed in an attribute argument list}}
+ [[clang::annotate(##)]] void b(); // expected-error {{'##' is not allowed in an attribute argument list}}
+ [[clang::annotate(%:)]] void c(); // expected-error {{'%:' is not allowed in an attribute argument list}}
+ [[clang::annotate(%:%:)]] void d(); // expected-error {{'%:%:' is not allowed in an attribute argument list}}
}
>From a697990d1fc6aa021c4b63bc0b3ad1efe55cec5b Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Mon, 7 Jul 2025 18:51:30 +0300
Subject: [PATCH 3/5] disallow and operators in C++ mode only
---
clang/lib/Parse/ParseDecl.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 059636653723c..495aa778b0d7f 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -488,7 +488,7 @@ unsigned Parser::ParseAttributeArgsCommon(
bool AttributeHasVariadicIdentifierArg =
attributeHasVariadicIdentifierArg(*AttrName, Form.getSyntax(), ScopeName);
- if (Tok.is(tok::hash) || Tok.is(tok::hashhash)) {
+ if (getLangOpts().CPlusPlus && Tok.isOneOf(tok::hash, tok::hashhash)) {
Diag(Tok.getLocation(), diag::err_invalid_attribute_argument)
<< PP.getSpelling(Tok);
SkipUntil(tok::r_paren, StopAtSemi);
>From ac38e4d2b4432af6c1eb6f69a76394c205c3eea7 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 9 Jul 2025 00:41:52 +0300
Subject: [PATCH 4/5] update release notes
---
clang/docs/ReleaseNotes.rst | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 554f89cc498c2..a7adb8919e5b4 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -678,7 +678,10 @@ Improvements to Clang's diagnostics
- Clang now accepts ``@tparam`` comments on variable template partial
specializations. (#GH144775)
-- Clang now rejects ``#`` operators in attribute argument lists. (#GH147217)
+- Clang rejects the ``#`` and ``##`` preprocessor tokens in an attribute
+ argument list in C++. The operators can be used in macro replacement lists
+ with the usual preprocessor semantics. What is rejected are non-preprocessor
+ uses of the tokens. The same restrictions do not apply in C. (#GH147217)
Improvements to Clang's time-trace
----------------------------------
>From 9204a36843d27fa23a522aacee3a6a16d9388970 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 9 Jul 2025 00:42:59 +0300
Subject: [PATCH 5/5] handle preprocessor tokens in unknown attributes
---
clang/lib/Parse/ParseDecl.cpp | 7 ---
clang/lib/Parse/ParseDeclCXX.cpp | 21 +++++++
.../cxx0x-attributes-preprocessor-tokens.cpp | 58 +++++++++++++++++++
clang/test/Parser/cxx0x-attributes.cpp | 7 ---
4 files changed, 79 insertions(+), 14 deletions(-)
create mode 100644 clang/test/Parser/cxx0x-attributes-preprocessor-tokens.cpp
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 495aa778b0d7f..7e739e09b15e8 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -488,13 +488,6 @@ unsigned Parser::ParseAttributeArgsCommon(
bool AttributeHasVariadicIdentifierArg =
attributeHasVariadicIdentifierArg(*AttrName, Form.getSyntax(), ScopeName);
- if (getLangOpts().CPlusPlus && Tok.isOneOf(tok::hash, tok::hashhash)) {
- Diag(Tok.getLocation(), diag::err_invalid_attribute_argument)
- << PP.getSpelling(Tok);
- SkipUntil(tok::r_paren, StopAtSemi);
- return 0;
- }
-
// Interpret "kw_this" as an identifier if the attributed requests it.
if (ChangeKWThisToIdent && Tok.is(tok::kw_this))
Tok.setKind(tok::identifier);
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 6b0564dca6f45..57555a287382b 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4523,6 +4523,27 @@ bool Parser::ParseCXX11AttributeArgs(
Form = ParsedAttr::Form::Microsoft();
}
+ if (LO.CPlusPlus) {
+ TentativeParsingAction TPA(*this);
+ bool HasInvalidArgument = false;
+ while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::eof)) {
+ if (Tok.isOneOf(tok::hash, tok::hashhash)) {
+ Diag(Tok.getLocation(), diag::err_invalid_attribute_argument)
+ << PP.getSpelling(Tok);
+ HasInvalidArgument = true;
+ }
+ ConsumeAnyToken();
+ }
+
+ if (HasInvalidArgument) {
+ SkipUntil(tok::r_paren);
+ TPA.Commit();
+ return true;
+ }
+
+ TPA.Revert();
+ }
+
// If the attribute isn't known, we will not attempt to parse any
// arguments.
if (Form.getSyntax() != ParsedAttr::AS_Microsoft &&
diff --git a/clang/test/Parser/cxx0x-attributes-preprocessor-tokens.cpp b/clang/test/Parser/cxx0x-attributes-preprocessor-tokens.cpp
new file mode 100644
index 0000000000000..23a9d597e5dd8
--- /dev/null
+++ b/clang/test/Parser/cxx0x-attributes-preprocessor-tokens.cpp
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -E %s | FileCheck %s
+// RUN: %clang_cc1 -x c -fsyntax-only -verify=c %s
+// RUN: %clang_cc1 -x c -E %s | FileCheck %s
+
+#define ATTR_STR(X) [[clang::annotate(#X)]]
+#define ATTR_PASTE(X, Y) [[clang::annotate("test", X ## Y)]]
+
+[[clang::assume(#)]] void f1(); // c-error {{expected expression}} \
+ // expected-error {{'#' is not allowed in an attribute argument list}}
+
+[[clang::assume(##)]] void f2(); // c-error {{expected expression}} \
+ // expected-error {{'##' is not allowed in an attribute argument list}}
+
+[[clang::assume(1#2#3)]] void f3(); // c-error {{use of this expression in an 'assume' attribute requires parentheses}} \
+ // c-error {{expected ')'}} \
+ // c-note {{to match this '('}} \
+ // expected-error {{'#' is not allowed in an attribute argument list}} \
+ // expected-error {{'#' is not allowed in an attribute argument list}}
+
+[[unknown::unknown(#)]] void f4(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \
+ // expected-error {{'#' is not allowed in an attribute argument list}}
+
+[[unknown::unknown(##)]] void f5(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \
+ // expected-error {{'##' is not allowed in an attribute argument list}}
+
+[[unknown::unknown(1#2#3)]] void f6(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \
+ // expected-error {{'#' is not allowed in an attribute argument list}} \
+ // expected-error {{'#' is not allowed in an attribute argument list}}
+
+[[clang::assume(%:)]] void f7(); // c-error {{expected expression}} \
+ // expected-error {{'%:' is not allowed in an attribute argument list}}
+
+
+[[clang::assume(%:%:)]] void f8(); // c-error {{expected expression}} \
+ // expected-error {{'%:%:' is not allowed in an attribute argument list}}
+
+[[clang::assume(1%:2%:3)]] void f9(); // c-error {{use of this expression in an 'assume' attribute requires parentheses}} \
+ // c-error {{expected ')'}} \
+ // c-note {{to match this '('}} \
+ // expected-error {{'%:' is not allowed in an attribute argument list}} \
+ // expected-error {{'%:' is not allowed in an attribute argument list}}
+
+[[unknown::unknown(%:)]] void f10(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \
+ // expected-error {{'%:' is not allowed in an attribute argument list}}
+
+[[unknown::unknown(%:%:)]] void f11(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \
+ // expected-error {{'%:%:' is not allowed in an attribute argument list}}
+
+[[unknown::unknown(1%:2%:3)]] void f12(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \
+ // expected-error {{'%:' is not allowed in an attribute argument list}} \
+ // expected-error {{'%:' is not allowed in an attribute argument list}}
+
+ATTR_STR(stringify) void f13();
+// CHECK: {{\[\[}}clang{{::}}annotate("stringify"){{\]\]}} void f13();
+
+ATTR_PASTE(1, 2) void f14();
+// CHECK: {{\[\[}}clang{{::}}annotate("test", 12){{\]\]}} void f14();
diff --git a/clang/test/Parser/cxx0x-attributes.cpp b/clang/test/Parser/cxx0x-attributes.cpp
index f5a3039c4470c..372a373a49ec5 100644
--- a/clang/test/Parser/cxx0x-attributes.cpp
+++ b/clang/test/Parser/cxx0x-attributes.cpp
@@ -477,10 +477,3 @@ namespace P2361 {
}
alignas(int) struct AlignAsAttribute {}; // expected-error {{misplaced attributes; expected attributes here}}
-
-namespace GH147217 {
- [[clang::annotate(#)]] void a(); // expected-error {{'#' is not allowed in an attribute argument list}}
- [[clang::annotate(##)]] void b(); // expected-error {{'##' is not allowed in an attribute argument list}}
- [[clang::annotate(%:)]] void c(); // expected-error {{'%:' is not allowed in an attribute argument list}}
- [[clang::annotate(%:%:)]] void d(); // expected-error {{'%:%:' is not allowed in an attribute argument list}}
-}
More information about the cfe-commits
mailing list