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

Reply via email to