On 4/15/26 12:30 AM, [email protected] wrote:
Some errors while parsing attributes on class are ignored due to tentative
parsing, this patch redo parsing to reject it.
This patch is also missing ChangeLog entries.
---
gcc/cp/parser.cc | 20 ++++++++++++++++++++
gcc/testsuite/g++.dg/reflect/pr123609.C | 5 +++++
2 files changed, 25 insertions(+)
create mode 100644 gcc/testsuite/g++.dg/reflect/pr123609.C
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 9dbc2933e7b..2911f40e5c2 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -30220,6 +30220,8 @@ cp_parser_class_head (cp_parser* parser,
bool invalid_nested_name_p = false;
bool invalid_explicit_specialization_p = false;
bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
+ bool attributes_error_p = false;
+ cp_token_position attributes_start_token_position;
Putting declarations at the start of the function isn't necessary
anymore, the existing ones are left from when we were still compiling as C.
tree pushed_scope = NULL_TREE;
unsigned num_templates;
cp_token *type_start_token = NULL, *nested_name_specifier_token_start = NULL;
@@ -30238,7 +30240,16 @@ cp_parser_class_head (cp_parser* parser,
location_t class_head_start_location = input_location;
/* Parse the attributes. */
+ /* Save tokens for some unhandled error (PR123609), we didn't use
+ cp_lexer_save_tokens because it will be commited by
+ cp_parser_commit_to_tentative_parse */
+ attributes_start_token_position = cp_lexer_token_position (parser->lexer,
false);
+ cp_parser_parse_tentatively (parser);
attributes = cp_parser_attributes_opt (parser);
+ if (cp_parser_error_occurred (parser))
+ attributes_error_p = true;
+ cp_parser_commit_to_topmost_tentative_parse (parser);
+ cp_parser_parse_definitely (parser);
All these tentative parsing changes are unnecessary as well; we only
have this issue if we're already parsing tentatively.
/* If the next token is `::', that is invalid -- but sometimes
people do try to write:
@@ -30380,6 +30391,15 @@ cp_parser_class_head (cp_parser* parser,
/* At this point, we're going ahead with the class-specifier, even
if some other problem occurs. */
cp_parser_commit_to_tentative_parse (parser);
+ /* Some errors while parsing attributes are ignored due to tentative parsing,
+ redo parsing to reject it. */
+ if (attributes_error_p)
+ {
+ auto current_position = cp_lexer_token_position (parser->lexer, false);
+ cp_lexer_set_token_position (parser->lexer,
attributes_start_token_position);
+ cp_parser_attributes_opt (parser);
+ cp_lexer_set_token_position (parser->lexer, current_position);
+ }
/* Issue the error about the overly-qualified name now. */
if (qualified_p)
{
diff --git a/gcc/testsuite/g++.dg/reflect/pr123609.C
b/gcc/testsuite/g++.dg/reflect/pr123609.C
C++ tests are usually named for the feature they test rather than the PR
number.
So I'm pushing this adjusted version. Thanks!
Jason
From bbe434b9dfd0851a491cac043a5402c3971d6a25 Mon Sep 17 00:00:00 2001
From: "[email protected]" <[email protected]>
Date: Wed, 22 Apr 2026 10:26:42 -0400
Subject: [PATCH] c++/reflection: reject invalid annotation on class [PR123609]
To: [email protected]
Some errors while parsing attributes on class are ignored due to tentative
parsing, this patch redo parsing to reject it.
PR c++/123609
gcc/cp/ChangeLog:
* parser.cc (cp_parser_class_head): Re-parse attributes
that caused a tentative parsing failure.
gcc/testsuite/ChangeLog:
* g++.dg/cpp0x/attr-nodiscard1.C: Adjust expected errors.
* g++.dg/reflect/annotations19.C: New test.
Co-authored-by: Jason Merrill <[email protected]>
---
gcc/cp/parser.cc | 15 +++++++++++++++
gcc/testsuite/g++.dg/cpp0x/attr-nodiscard1.C | 4 ++--
gcc/testsuite/g++.dg/reflect/annotations19.C | 5 +++++
3 files changed, 22 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/reflect/annotations19.C
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index c08f4cee920..c7708f15f39 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -30295,7 +30295,13 @@ cp_parser_class_head (cp_parser* parser,
location_t class_head_start_location = input_location;
/* Parse the attributes. */
+ /* Save tokens for some unhandled error (PR123609). */
+ cp_token_position attribute_error_position = 0;
+ if (!cp_parser_error_occurred (parser))
+ attribute_error_position = cp_lexer_token_position (parser->lexer, false);
attributes = cp_parser_attributes_opt (parser);
+ if (!cp_parser_error_occurred (parser))
+ attribute_error_position = 0;
/* If the next token is `::', that is invalid -- but sometimes
people do try to write:
@@ -30437,6 +30443,15 @@ cp_parser_class_head (cp_parser* parser,
/* At this point, we're going ahead with the class-specifier, even
if some other problem occurs. */
cp_parser_commit_to_tentative_parse (parser);
+ /* Some errors while parsing attributes are ignored due to tentative parsing,
+ redo parsing to reject it. */
+ if (attribute_error_position)
+ {
+ auto current_position = cp_lexer_token_position (parser->lexer, false);
+ cp_lexer_set_token_position (parser->lexer, attribute_error_position);
+ cp_parser_attributes_opt (parser);
+ cp_lexer_set_token_position (parser->lexer, current_position);
+ }
/* Issue the error about the overly-qualified name now. */
if (qualified_p)
{
diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-nodiscard1.C b/gcc/testsuite/g++.dg/cpp0x/attr-nodiscard1.C
index 5abdd380f9d..5b97d0b6104 100644
--- a/gcc/testsuite/g++.dg/cpp0x/attr-nodiscard1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/attr-nodiscard1.C
@@ -10,9 +10,9 @@ foo (int n)
{
struct [[nodiscard]] S1 {};
struct [[nodiscard ("foobar")]] S2 {};
- struct [[nodiscard (0)]] S3 {}; // { dg-error "'nodiscard' attribute argument must be a string constant" }
+ struct [[nodiscard (0)]] S3 {}; // { dg-error "'nodiscard' attribute argument must be a string constant|expected string-literal" }
struct [[nodiscard ("foo", "bar", "baz")]] S4 {}; // { dg-error "wrong number of arguments specified for 'nodiscard' attribute" }
- struct [[nodiscard (0, 1, 2)]] S5 {}; // { dg-error "wrong number of arguments specified for 'nodiscard' attribute" }
+ struct [[nodiscard (0, 1, 2)]] S5 {}; // { dg-error "wrong number of arguments specified for 'nodiscard' attribute|expected string-literal" }
auto a = [] [[nodiscard]] () {};
auto b = [] constexpr [[nodiscard]] {}; // { dg-warning "'nodiscard' attribute can only be applied to functions or to class or enumeration types" }
diff --git a/gcc/testsuite/g++.dg/reflect/annotations19.C b/gcc/testsuite/g++.dg/reflect/annotations19.C
new file mode 100644
index 00000000000..2408aec809c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/annotations19.C
@@ -0,0 +1,5 @@
+// PR c++/123609
+// { dg-do compile { target c++26 } }
+
+struct [[=]] S {}; // { dg-error "expected primary-expression before ']' token" }
+[[=]] int a; // { dg-error "expected primary-expression before ']' token" }
--
2.54.0