From f8f7c27d4f723913a6929e591c612865a7b62f70 Mon Sep 17 00:00:00 2001 From: León Orell Valerian Liehr Date: Fri, 15 Aug 2025 23:01:32 +0200 Subject: Clean up parsers related to generic bounds --- compiler/rustc_parse/src/parser/expr.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'compiler/rustc_parse/src/parser/expr.rs') diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index d0604f76317..ea8cd3754a0 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2397,12 +2397,12 @@ impl<'a> Parser<'a> { let before = self.prev_token; let binder = if self.check_keyword(exp!(For)) { let lo = self.token.span; - let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?; + let (bound_vars, _) = self.parse_higher_ranked_binder()?; let span = lo.to(self.prev_token.span); self.psess.gated_spans.gate(sym::closure_lifetime_binder, span); - ClosureBinder::For { span, generic_params: lifetime_defs } + ClosureBinder::For { span, generic_params: bound_vars } } else { ClosureBinder::NotPresent }; -- cgit 1.4.1-3-g733a5 From ddd99930f34b79f209c61cc25706a1dac1173762 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 15 Aug 2025 23:39:33 +0800 Subject: Turn invalid index suffixes into hard errors --- compiler/rustc_parse/messages.ftl | 3 - compiler/rustc_parse/src/errors.rs | 4 - compiler/rustc_parse/src/parser/expr.rs | 26 +---- compiler/rustc_parse/src/parser/mod.rs | 5 +- .../auxiliary/tuple-index-suffix-proc-macro-aux.rs | 8 +- tests/ui/parser/tuple-index-suffix-proc-macro.rs | 7 +- .../ui/parser/tuple-index-suffix-proc-macro.stderr | 22 ++-- tests/ui/parser/tuple-index-suffix.rs | 44 ++++---- tests/ui/parser/tuple-index-suffix.stderr | 124 ++++++++------------- 9 files changed, 92 insertions(+), 151 deletions(-) (limited to 'compiler/rustc_parse/src/parser/expr.rs') diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 9e0075c21b9..0d1a3c78389 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -473,9 +473,6 @@ parse_invalid_label = parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid .label = invalid suffix `{$suffix}` - .tuple_exception_line_1 = `{$suffix}` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - .tuple_exception_line_2 = on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - .tuple_exception_line_3 = see issue #60210 for more information parse_invalid_logical_operator = `{$incorrect}` is not a logical operator .note = unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index a105dd1909e..8a10e7d05eb 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1016,10 +1016,6 @@ pub(crate) struct InvalidLiteralSuffixOnTupleIndex { #[label] pub span: Span, pub suffix: Symbol, - #[help(parse_tuple_exception_line_1)] - #[help(parse_tuple_exception_line_2)] - #[help(parse_tuple_exception_line_3)] - pub exception: bool, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index d0604f76317..7d33f3de15c 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1163,7 +1163,10 @@ impl<'a> Parser<'a> { suffix, }) => { if let Some(suffix) = suffix { - self.expect_no_tuple_index_suffix(current.span, suffix); + self.dcx().emit_err(errors::InvalidLiteralSuffixOnTupleIndex { + span: current.span, + suffix, + }); } match self.break_up_float(symbol, current.span) { // 1e2 @@ -1239,7 +1242,8 @@ impl<'a> Parser<'a> { suffix: Option, ) -> Box { if let Some(suffix) = suffix { - self.expect_no_tuple_index_suffix(ident_span, suffix); + self.dcx() + .emit_err(errors::InvalidLiteralSuffixOnTupleIndex { span: ident_span, suffix }); } self.mk_expr(lo.to(ident_span), ExprKind::Field(base, Ident::new(field, ident_span))) } @@ -2225,24 +2229,6 @@ impl<'a> Parser<'a> { }) } - pub(super) fn expect_no_tuple_index_suffix(&self, span: Span, suffix: Symbol) { - if [sym::i32, sym::u32, sym::isize, sym::usize].contains(&suffix) { - // #59553: warn instead of reject out of hand to allow the fix to percolate - // through the ecosystem when people fix their macros - self.dcx().emit_warn(errors::InvalidLiteralSuffixOnTupleIndex { - span, - suffix, - exception: true, - }); - } else { - self.dcx().emit_err(errors::InvalidLiteralSuffixOnTupleIndex { - span, - suffix, - exception: false, - }); - } - } - /// Matches `'-' lit | lit` (cf. `ast_validation::AstValidator::check_expr_within_pat`). /// Keep this in sync with `Token::can_begin_literal_maybe_minus`. pub fn parse_literal_maybe_minus(&mut self) -> PResult<'a, Box> { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 41ed1f95a01..db72faf4ec7 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1333,7 +1333,10 @@ impl<'a> Parser<'a> { if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = self.token.kind { if let Some(suffix) = suffix { - self.expect_no_tuple_index_suffix(self.token.span, suffix); + self.dcx().emit_err(errors::InvalidLiteralSuffixOnTupleIndex { + span: self.token.span, + suffix, + }); } self.bump(); Ok(Ident::new(symbol, self.prev_token.span)) diff --git a/tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs b/tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs index fa40ed948a6..a5084b55aac 100644 --- a/tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs +++ b/tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs @@ -18,14 +18,14 @@ pub fn bad_tup_indexing(input: TokenStream) -> TokenStream { pub fn bad_tup_struct_indexing(input: TokenStream) -> TokenStream { let mut input = input.into_iter(); - let id_tt = input.next().unwrap(); + let ident = input.next().unwrap(); let _comma = input.next().unwrap(); - let tt = input.next().unwrap(); + let lit = input.next().unwrap(); - let TokenTree::Ident(ident) = id_tt else { + let TokenTree::Ident(ident) = ident else { unreachable!("id"); }; - let TokenTree::Literal(indexing_expr) = tt else { + let TokenTree::Literal(indexing_expr) = lit else { unreachable!("lit"); }; diff --git a/tests/ui/parser/tuple-index-suffix-proc-macro.rs b/tests/ui/parser/tuple-index-suffix-proc-macro.rs index feca6f9cdfb..557c67738d3 100644 --- a/tests/ui/parser/tuple-index-suffix-proc-macro.rs +++ b/tests/ui/parser/tuple-index-suffix-proc-macro.rs @@ -11,12 +11,13 @@ fn main() { struct TupStruct(i32); let tup_struct = TupStruct(42); - // #60186 carve outs `{i,u}{32,usize}` as non-lint pseudo-FCW warnings. + // Previously, #60186 had carve outs for `{i,u}{32,usize}` as non-lint pseudo-FCW warnings. Now, + // they all hard error. aux::bad_tup_indexing!(0usize); - //~^ WARN suffixes on a tuple index are invalid + //~^ ERROR suffixes on a tuple index are invalid aux::bad_tup_struct_indexing!(tup_struct, 0isize); - //~^ WARN suffixes on a tuple index are invalid + //~^ ERROR suffixes on a tuple index are invalid // Not part of the #60186 carve outs. diff --git a/tests/ui/parser/tuple-index-suffix-proc-macro.stderr b/tests/ui/parser/tuple-index-suffix-proc-macro.stderr index c8bc3a4576b..47d179d3555 100644 --- a/tests/ui/parser/tuple-index-suffix-proc-macro.stderr +++ b/tests/ui/parser/tuple-index-suffix-proc-macro.stderr @@ -1,34 +1,26 @@ -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix-proc-macro.rs:16:28 +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix-proc-macro.rs:17:28 | LL | aux::bad_tup_indexing!(0usize); | ^^^^^^ invalid suffix `usize` - | - = help: `usize` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix-proc-macro.rs:18:47 +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix-proc-macro.rs:19:47 | LL | aux::bad_tup_struct_indexing!(tup_struct, 0isize); | ^^^^^^ invalid suffix `isize` - | - = help: `isize` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix-proc-macro.rs:23:28 + --> $DIR/tuple-index-suffix-proc-macro.rs:24:28 | LL | aux::bad_tup_indexing!(0u8); | ^^^ invalid suffix `u8` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix-proc-macro.rs:25:47 + --> $DIR/tuple-index-suffix-proc-macro.rs:26:47 | LL | aux::bad_tup_struct_indexing!(tup_struct, 0u64); | ^^^^ invalid suffix `u64` -error: aborting due to 2 previous errors; 2 warnings emitted +error: aborting due to 4 previous errors diff --git a/tests/ui/parser/tuple-index-suffix.rs b/tests/ui/parser/tuple-index-suffix.rs index 31c5bc25063..c4769500005 100644 --- a/tests/ui/parser/tuple-index-suffix.rs +++ b/tests/ui/parser/tuple-index-suffix.rs @@ -1,8 +1,10 @@ -//! See #60210. +//! Regression test for both the original regression in #59418 where invalid suffixes in indexing +//! positions were accidentally accepted, and also for the removal of the temporary carve out that +//! mitigated ecosystem impact following trying to reject #59418 (this was implemented as a FCW +//! tracked in #60210). //! //! Check that we hard error on invalid suffixes in tuple indexing subexpressions and struct numeral -//! field names, modulo carve-outs for `{i,u}{32,usize}` at warning level to mitigate ecosystem -//! impact. +//! field names. struct X(i32,i32,i32); @@ -10,28 +12,28 @@ fn main() { let tup_struct = X(1, 2, 3); let invalid_tup_struct_suffix = tup_struct.0suffix; //~^ ERROR suffixes on a tuple index are invalid - let carve_out_tup_struct_suffix = tup_struct.0i32; - //~^ WARN suffixes on a tuple index are invalid + let previous_carve_out_tup_struct_suffix = tup_struct.0i32; + //~^ ERROR suffixes on a tuple index are invalid let tup = (1, 2, 3); let invalid_tup_suffix = tup.0suffix; //~^ ERROR suffixes on a tuple index are invalid - let carve_out_tup_suffix = tup.0u32; - //~^ WARN suffixes on a tuple index are invalid + let previous_carve_out_tup_suffix = tup.0u32; + //~^ ERROR suffixes on a tuple index are invalid numeral_struct_field_name_suffix_invalid(); - numeral_struct_field_name_suffix_carve_out(); + numeral_struct_field_name_suffix_previous_carve_out(); } -// Very limited carve outs as a ecosystem impact mitigation implemented in #60186. *Only* -// `{i,u}{32,usize}` suffixes are temporarily accepted. -fn carve_outs() { - // Ok, only pseudo-FCW warnings. +// Previously, there were very limited carve outs as a ecosystem impact mitigation implemented in +// #60186. *Only* `{i,u}{32,usize}` suffixes were temporarily accepted. Now, they all hard error. +fn previous_carve_outs() { + // Previously temporarily accepted by a pseudo-FCW (#60210), now hard error. - let carve_out_i32 = (42,).0i32; //~ WARN suffixes on a tuple index are invalid - let carve_out_i32 = (42,).0u32; //~ WARN suffixes on a tuple index are invalid - let carve_out_isize = (42,).0isize; //~ WARN suffixes on a tuple index are invalid - let carve_out_usize = (42,).0usize; //~ WARN suffixes on a tuple index are invalid + let previous_carve_out_i32 = (42,).0i32; //~ ERROR suffixes on a tuple index are invalid + let previous_carve_out_i32 = (42,).0u32; //~ ERROR suffixes on a tuple index are invalid + let previous_carve_out_isize = (42,).0isize; //~ ERROR suffixes on a tuple index are invalid + let previous_carve_out_usize = (42,).0usize; //~ ERROR suffixes on a tuple index are invalid // Not part of the carve outs! let error_i8 = (42,).0i8; //~ ERROR suffixes on a tuple index are invalid @@ -53,12 +55,12 @@ fn numeral_struct_field_name_suffix_invalid() { } } -fn numeral_struct_field_name_suffix_carve_out() { +fn numeral_struct_field_name_suffix_previous_carve_out() { let carve_out_struct_name = X { 0u32: 0, 1: 1, 2: 2 }; - //~^ WARN suffixes on a tuple index are invalid + //~^ ERROR suffixes on a tuple index are invalid match carve_out_struct_name { X { 0u32: _, .. } => {} - //~^ WARN suffixes on a tuple index are invalid + //~^ ERROR suffixes on a tuple index are invalid } } @@ -67,9 +69,9 @@ fn offset_of_suffix() { #[repr(C)] pub struct Struct(u8, T); - // Carve outs + // Previous pseudo-FCW carve outs assert_eq!(std::mem::offset_of!(Struct, 0usize), 0); - //~^ WARN suffixes on a tuple index are invalid + //~^ ERROR suffixes on a tuple index are invalid // Not part of carve outs assert_eq!(std::mem::offset_of!(Struct, 0u8), 0); diff --git a/tests/ui/parser/tuple-index-suffix.stderr b/tests/ui/parser/tuple-index-suffix.stderr index 3a499dd6a8d..6d96c6d3cbf 100644 --- a/tests/ui/parser/tuple-index-suffix.stderr +++ b/tests/ui/parser/tuple-index-suffix.stderr @@ -1,170 +1,134 @@ error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:11:48 + --> $DIR/tuple-index-suffix.rs:13:48 | LL | let invalid_tup_struct_suffix = tup_struct.0suffix; | ^^^^^^^ invalid suffix `suffix` -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:13:50 - | -LL | let carve_out_tup_struct_suffix = tup_struct.0i32; - | ^^^^ invalid suffix `i32` +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:15:59 | - = help: `i32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information +LL | let previous_carve_out_tup_struct_suffix = tup_struct.0i32; + | ^^^^ invalid suffix `i32` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:17:34 + --> $DIR/tuple-index-suffix.rs:19:34 | LL | let invalid_tup_suffix = tup.0suffix; | ^^^^^^^ invalid suffix `suffix` -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:19:36 - | -LL | let carve_out_tup_suffix = tup.0u32; - | ^^^^ invalid suffix `u32` +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:21:45 | - = help: `u32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information +LL | let previous_carve_out_tup_suffix = tup.0u32; + | ^^^^ invalid suffix `u32` -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:31:31 - | -LL | let carve_out_i32 = (42,).0i32; - | ^^^^ invalid suffix `i32` +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:33:40 | - = help: `i32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information +LL | let previous_carve_out_i32 = (42,).0i32; + | ^^^^ invalid suffix `i32` -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:32:31 - | -LL | let carve_out_i32 = (42,).0u32; - | ^^^^ invalid suffix `u32` +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:34:40 | - = help: `u32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information +LL | let previous_carve_out_i32 = (42,).0u32; + | ^^^^ invalid suffix `u32` -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:33:33 - | -LL | let carve_out_isize = (42,).0isize; - | ^^^^^^ invalid suffix `isize` +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:35:42 | - = help: `isize` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information +LL | let previous_carve_out_isize = (42,).0isize; + | ^^^^^^ invalid suffix `isize` -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:34:33 - | -LL | let carve_out_usize = (42,).0usize; - | ^^^^^^ invalid suffix `usize` +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:36:42 | - = help: `usize` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information +LL | let previous_carve_out_usize = (42,).0usize; + | ^^^^^^ invalid suffix `usize` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:37:26 + --> $DIR/tuple-index-suffix.rs:39:26 | LL | let error_i8 = (42,).0i8; | ^^^ invalid suffix `i8` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:38:26 + --> $DIR/tuple-index-suffix.rs:40:26 | LL | let error_u8 = (42,).0u8; | ^^^ invalid suffix `u8` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:39:27 + --> $DIR/tuple-index-suffix.rs:41:27 | LL | let error_i16 = (42,).0i16; | ^^^^ invalid suffix `i16` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:40:27 + --> $DIR/tuple-index-suffix.rs:42:27 | LL | let error_u16 = (42,).0u16; | ^^^^ invalid suffix `u16` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:41:27 + --> $DIR/tuple-index-suffix.rs:43:27 | LL | let error_i64 = (42,).0i64; | ^^^^ invalid suffix `i64` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:42:27 + --> $DIR/tuple-index-suffix.rs:44:27 | LL | let error_u64 = (42,).0u64; | ^^^^ invalid suffix `u64` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:43:28 + --> $DIR/tuple-index-suffix.rs:45:28 | LL | let error_i128 = (42,).0i128; | ^^^^^ invalid suffix `i128` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:44:28 + --> $DIR/tuple-index-suffix.rs:46:28 | LL | let error_u128 = (42,).0u128; | ^^^^^ invalid suffix `u128` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:48:35 + --> $DIR/tuple-index-suffix.rs:50:35 | LL | let invalid_struct_name = X { 0suffix: 0, 1: 1, 2: 2 }; | ^^^^^^^ invalid suffix `suffix` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:51:13 + --> $DIR/tuple-index-suffix.rs:53:13 | LL | X { 0suffix: _, .. } => {} | ^^^^^^^ invalid suffix `suffix` -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:57:37 +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:59:37 | LL | let carve_out_struct_name = X { 0u32: 0, 1: 1, 2: 2 }; | ^^^^ invalid suffix `u32` - | - = help: `u32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:60:13 +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:62:13 | LL | X { 0u32: _, .. } => {} | ^^^^ invalid suffix `u32` - | - = help: `u32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:71:50 +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:73:50 | LL | assert_eq!(std::mem::offset_of!(Struct, 0usize), 0); | ^^^^^^ invalid suffix `usize` - | - = help: `usize` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:75:50 + --> $DIR/tuple-index-suffix.rs:77:50 | LL | assert_eq!(std::mem::offset_of!(Struct, 0u8), 0); | ^^^ invalid suffix `u8` -error: aborting due to 13 previous errors; 9 warnings emitted +error: aborting due to 22 previous errors -- cgit 1.4.1-3-g733a5 From db0c825d2c745fdf87edd501ebda222052f98771 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 19 Aug 2025 13:12:17 +0000 Subject: Gate static coroutines behind a parser feature --- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_parse/src/parser/expr.rs | 8 +++-- tests/ui/coroutine/static-closure-unexpanded.rs | 10 ++++++ .../ui/coroutine/static-closure-unexpanded.stderr | 13 +++++++ tests/ui/static/static-closures.rs | 1 + tests/ui/static/static-closures.stderr | 15 ++++++-- tests/ui/unpretty/exhaustive.expanded.stdout | 1 + tests/ui/unpretty/exhaustive.hir.stderr | 40 +++++++++++----------- tests/ui/unpretty/exhaustive.hir.stdout | 1 + tests/ui/unpretty/exhaustive.rs | 1 + 10 files changed, 67 insertions(+), 24 deletions(-) create mode 100644 tests/ui/coroutine/static-closure-unexpanded.rs create mode 100644 tests/ui/coroutine/static-closure-unexpanded.stderr (limited to 'compiler/rustc_parse/src/parser/expr.rs') diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index c7f41fc3cb1..6741b8c340e 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -525,6 +525,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(where_clause_attrs, "attributes in `where` clause are unstable"); gate_all!(super_let, "`super let` is experimental"); gate_all!(frontmatter, "frontmatters are experimental"); + gate_all!(coroutines, "coroutine syntax is experimental"); if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 3cedc86dc0d..4d84d48c94b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2401,8 +2401,12 @@ impl<'a> Parser<'a> { let constness = self.parse_closure_constness(); - let movability = - if self.eat_keyword(exp!(Static)) { Movability::Static } else { Movability::Movable }; + let movability = if self.eat_keyword(exp!(Static)) { + self.psess.gated_spans.gate(sym::coroutines, self.prev_token.span); + Movability::Static + } else { + Movability::Movable + }; let coroutine_kind = if self.token_uninterpolated_span().at_least_rust_2018() { self.parse_coroutine_kind(Case::Sensitive) diff --git a/tests/ui/coroutine/static-closure-unexpanded.rs b/tests/ui/coroutine/static-closure-unexpanded.rs new file mode 100644 index 00000000000..7cf24774ded --- /dev/null +++ b/tests/ui/coroutine/static-closure-unexpanded.rs @@ -0,0 +1,10 @@ +// Tests that static closures are not stable in the parser grammar unless the +// coroutine feature is enabled. + +#[cfg(any())] +fn foo() { + let _ = static || {}; + //~^ ERROR coroutine syntax is experimental +} + +fn main() {} diff --git a/tests/ui/coroutine/static-closure-unexpanded.stderr b/tests/ui/coroutine/static-closure-unexpanded.stderr new file mode 100644 index 00000000000..f08bafd368f --- /dev/null +++ b/tests/ui/coroutine/static-closure-unexpanded.stderr @@ -0,0 +1,13 @@ +error[E0658]: coroutine syntax is experimental + --> $DIR/static-closure-unexpanded.rs:6:13 + | +LL | let _ = static || {}; + | ^^^^^^ + | + = note: see issue #43122 for more information + = help: add `#![feature(coroutines)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/static/static-closures.rs b/tests/ui/static/static-closures.rs index 1bd518d6ffe..e836f1b85eb 100644 --- a/tests/ui/static/static-closures.rs +++ b/tests/ui/static/static-closures.rs @@ -1,4 +1,5 @@ fn main() { static || {}; //~^ ERROR closures cannot be static + //~| ERROR coroutine syntax is experimental } diff --git a/tests/ui/static/static-closures.stderr b/tests/ui/static/static-closures.stderr index b11c0b5a530..ecc961cc1e4 100644 --- a/tests/ui/static/static-closures.stderr +++ b/tests/ui/static/static-closures.stderr @@ -1,9 +1,20 @@ +error[E0658]: coroutine syntax is experimental + --> $DIR/static-closures.rs:2:5 + | +LL | static || {}; + | ^^^^^^ + | + = note: see issue #43122 for more information + = help: add `#![feature(coroutines)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0697]: closures cannot be static --> $DIR/static-closures.rs:2:5 | LL | static || {}; | ^^^^^^^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0697`. +Some errors have detailed explanations: E0658, E0697. +For more information about an error, try `rustc --explain E0658`. diff --git a/tests/ui/unpretty/exhaustive.expanded.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout index 53ca3c8e391..220e2263f0d 100644 --- a/tests/ui/unpretty/exhaustive.expanded.stdout +++ b/tests/ui/unpretty/exhaustive.expanded.stdout @@ -13,6 +13,7 @@ #![feature(box_patterns)] #![feature(builtin_syntax)] #![feature(const_trait_impl)] +#![feature(coroutines)] #![feature(decl_macro)] #![feature(deref_patterns)] #![feature(explicit_tail_calls)] diff --git a/tests/ui/unpretty/exhaustive.hir.stderr b/tests/ui/unpretty/exhaustive.hir.stderr index aa411ce81eb..eb5c186bd2c 100644 --- a/tests/ui/unpretty/exhaustive.hir.stderr +++ b/tests/ui/unpretty/exhaustive.hir.stderr @@ -1,17 +1,17 @@ error[E0697]: closures cannot be static - --> $DIR/exhaustive.rs:209:9 + --> $DIR/exhaustive.rs:210:9 | LL | static || value; | ^^^^^^^^^ error[E0697]: closures cannot be static - --> $DIR/exhaustive.rs:210:9 + --> $DIR/exhaustive.rs:211:9 | LL | static move || value; | ^^^^^^^^^^^^^^ error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/exhaustive.rs:239:13 + --> $DIR/exhaustive.rs:240:13 | LL | fn expr_await() { | --------------- this is not `async` @@ -20,19 +20,19 @@ LL | fut.await; | ^^^^^ only allowed inside `async` functions and blocks error: in expressions, `_` can only be used on the left-hand side of an assignment - --> $DIR/exhaustive.rs:288:9 + --> $DIR/exhaustive.rs:289:9 | LL | _; | ^ `_` not allowed here error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:298:9 + --> $DIR/exhaustive.rs:299:9 | LL | x::(); | ^^^^^ only `Fn` traits may use parentheses error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:299:9 + --> $DIR/exhaustive.rs:300:9 | LL | x::(T, T) -> T; | ^^^^^^^^^^^^^^ only `Fn` traits may use parentheses @@ -44,31 +44,31 @@ LL + x:: -> T; | error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:300:9 + --> $DIR/exhaustive.rs:301:9 | LL | crate::() -> ()::expressions::() -> ()::expr_path; | ^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:300:26 + --> $DIR/exhaustive.rs:301:26 | LL | crate::() -> ()::expressions::() -> ()::expr_path; | ^^^^^^^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:303:9 + --> $DIR/exhaustive.rs:304:9 | LL | core::()::marker::()::PhantomData; | ^^^^^^^^ only `Fn` traits may use parentheses error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:303:19 + --> $DIR/exhaustive.rs:304:19 | LL | core::()::marker::()::PhantomData; | ^^^^^^^^^^ only `Fn` traits may use parentheses error: `yield` can only be used in `#[coroutine]` closures, or `gen` blocks - --> $DIR/exhaustive.rs:390:9 + --> $DIR/exhaustive.rs:391:9 | LL | yield; | ^^^^^ @@ -79,7 +79,7 @@ LL | #[coroutine] fn expr_yield() { | ++++++++++++ error[E0703]: invalid ABI: found `C++` - --> $DIR/exhaustive.rs:470:23 + --> $DIR/exhaustive.rs:471:23 | LL | unsafe extern "C++" {} | ^^^^^ invalid ABI @@ -87,7 +87,7 @@ LL | unsafe extern "C++" {} = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions error: `..` patterns are not allowed here - --> $DIR/exhaustive.rs:677:13 + --> $DIR/exhaustive.rs:678:13 | LL | let ..; | ^^ @@ -95,13 +95,13 @@ LL | let ..; = note: only allowed in tuple, tuple struct, and slice patterns error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:792:16 + --> $DIR/exhaustive.rs:793:16 | LL | let _: T() -> !; | ^^^^^^^^ only `Fn` traits may use parentheses error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:806:16 + --> $DIR/exhaustive.rs:807:16 | LL | let _: impl Send; | ^^^^^^^^^ @@ -112,7 +112,7 @@ LL | let _: impl Send; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:807:16 + --> $DIR/exhaustive.rs:808:16 | LL | let _: impl Send + 'static; | ^^^^^^^^^^^^^^^^^^^ @@ -123,7 +123,7 @@ LL | let _: impl Send + 'static; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:808:16 + --> $DIR/exhaustive.rs:809:16 | LL | let _: impl 'static + Send; | ^^^^^^^^^^^^^^^^^^^ @@ -134,7 +134,7 @@ LL | let _: impl 'static + Send; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:809:16 + --> $DIR/exhaustive.rs:810:16 | LL | let _: impl ?Sized; | ^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | let _: impl ?Sized; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:810:16 + --> $DIR/exhaustive.rs:811:16 | LL | let _: impl [const] Clone; | ^^^^^^^^^^^^^^^^^^ @@ -156,7 +156,7 @@ LL | let _: impl [const] Clone; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:811:16 + --> $DIR/exhaustive.rs:812:16 | LL | let _: impl for<'a> Send; | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index 2b8f3b21396..57cf3d21a8e 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -12,6 +12,7 @@ #![feature(box_patterns)] #![feature(builtin_syntax)] #![feature(const_trait_impl)] +#![feature(coroutines)] #![feature(decl_macro)] #![feature(deref_patterns)] #![feature(explicit_tail_calls)] diff --git a/tests/ui/unpretty/exhaustive.rs b/tests/ui/unpretty/exhaustive.rs index 5292ddad4f6..2b62ad24524 100644 --- a/tests/ui/unpretty/exhaustive.rs +++ b/tests/ui/unpretty/exhaustive.rs @@ -12,6 +12,7 @@ #![feature(box_patterns)] #![feature(builtin_syntax)] #![feature(const_trait_impl)] +#![feature(coroutines)] #![feature(decl_macro)] #![feature(deref_patterns)] #![feature(explicit_tail_calls)] -- cgit 1.4.1-3-g733a5 From 30bb7045d6b80b979b235396626f3b405cfec160 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Mon, 4 Aug 2025 09:42:27 +0000 Subject: don't print invalid labels with `r#` --- compiler/rustc_parse/src/errors.rs | 2 +- compiler/rustc_parse/src/parser/expr.rs | 8 +++++++- tests/ui/closures/issue-52437.rs | 2 +- tests/ui/closures/issue-52437.stderr | 2 +- tests/ui/issues/issue-46311.rs | 2 +- tests/ui/issues/issue-46311.stderr | 2 +- tests/ui/label/label-static.rs | 4 ++-- tests/ui/label/label-static.stderr | 4 ++-- tests/ui/label/label-underscore.rs | 4 ++-- tests/ui/label/label-underscore.stderr | 4 ++-- tests/ui/parser/require-parens-for-chained-comparison.stderr | 4 ++-- 11 files changed, 22 insertions(+), 16 deletions(-) (limited to 'compiler/rustc_parse/src/parser/expr.rs') diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 2c046329e33..7d84fc2a2f9 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2232,7 +2232,7 @@ pub(crate) struct KeywordLifetime { pub(crate) struct InvalidLabel { #[primary_span] pub span: Span, - pub name: Symbol, + pub name: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index cb7f755f524..effcfd54a8f 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3094,7 +3094,13 @@ impl<'a> Parser<'a> { if let Some((ident, is_raw)) = self.token.lifetime() { // Disallow `'fn`, but with a better error message than `expect_lifetime`. if matches!(is_raw, IdentIsRaw::No) && ident.without_first_quote().is_reserved() { - self.dcx().emit_err(errors::InvalidLabel { span: ident.span, name: ident.name }); + self.dcx().emit_err(errors::InvalidLabel { + span: ident.span, + // `IntoDiagArg` prints the symbol as if it was an ident, + // so `'break` is printed as `r#break`. We don't want that + // here so convert to string eagerly. + name: ident.without_first_quote().name.to_string(), + }); } self.bump(); diff --git a/tests/ui/closures/issue-52437.rs b/tests/ui/closures/issue-52437.rs index 6ac5380a5aa..0655eac517b 100644 --- a/tests/ui/closures/issue-52437.rs +++ b/tests/ui/closures/issue-52437.rs @@ -1,5 +1,5 @@ fn main() { [(); &(&'static: loop { |x| {}; }) as *const _ as usize] - //~^ ERROR: invalid label name `'static` + //~^ ERROR: invalid label name `static` //~| ERROR: type annotations needed } diff --git a/tests/ui/closures/issue-52437.stderr b/tests/ui/closures/issue-52437.stderr index 9ba24c7a886..e8832f42f10 100644 --- a/tests/ui/closures/issue-52437.stderr +++ b/tests/ui/closures/issue-52437.stderr @@ -1,4 +1,4 @@ -error: invalid label name `'static` +error: invalid label name `static` --> $DIR/issue-52437.rs:2:13 | LL | [(); &(&'static: loop { |x| {}; }) as *const _ as usize] diff --git a/tests/ui/issues/issue-46311.rs b/tests/ui/issues/issue-46311.rs index 1233a49c582..69826d78e2d 100644 --- a/tests/ui/issues/issue-46311.rs +++ b/tests/ui/issues/issue-46311.rs @@ -1,4 +1,4 @@ fn main() { - 'break: loop { //~ ERROR invalid label name `'break` + 'break: loop { //~ ERROR invalid label name `break` } } diff --git a/tests/ui/issues/issue-46311.stderr b/tests/ui/issues/issue-46311.stderr index 86a3602899a..e2fe3130d72 100644 --- a/tests/ui/issues/issue-46311.stderr +++ b/tests/ui/issues/issue-46311.stderr @@ -1,4 +1,4 @@ -error: invalid label name `'break` +error: invalid label name `break` --> $DIR/issue-46311.rs:2:5 | LL | 'break: loop { diff --git a/tests/ui/label/label-static.rs b/tests/ui/label/label-static.rs index 95e764d0187..46fa6ac81d8 100644 --- a/tests/ui/label/label-static.rs +++ b/tests/ui/label/label-static.rs @@ -1,5 +1,5 @@ fn main() { - 'static: loop { //~ ERROR invalid label name `'static` - break 'static //~ ERROR invalid label name `'static` + 'static: loop { //~ ERROR invalid label name `static` + break 'static //~ ERROR invalid label name `static` } } diff --git a/tests/ui/label/label-static.stderr b/tests/ui/label/label-static.stderr index 1d3251d1b5f..890a7b12c61 100644 --- a/tests/ui/label/label-static.stderr +++ b/tests/ui/label/label-static.stderr @@ -1,10 +1,10 @@ -error: invalid label name `'static` +error: invalid label name `static` --> $DIR/label-static.rs:2:5 | LL | 'static: loop { | ^^^^^^^ -error: invalid label name `'static` +error: invalid label name `static` --> $DIR/label-static.rs:3:15 | LL | break 'static diff --git a/tests/ui/label/label-underscore.rs b/tests/ui/label/label-underscore.rs index de67f3d2c3e..ed44957d1d8 100644 --- a/tests/ui/label/label-underscore.rs +++ b/tests/ui/label/label-underscore.rs @@ -1,5 +1,5 @@ fn main() { - '_: loop { //~ ERROR invalid label name `'_` - break '_ //~ ERROR invalid label name `'_` + '_: loop { //~ ERROR invalid label name `_` + break '_ //~ ERROR invalid label name `_` } } diff --git a/tests/ui/label/label-underscore.stderr b/tests/ui/label/label-underscore.stderr index 4558ec4cb41..31c962b6ae5 100644 --- a/tests/ui/label/label-underscore.stderr +++ b/tests/ui/label/label-underscore.stderr @@ -1,10 +1,10 @@ -error: invalid label name `'_` +error: invalid label name `_` --> $DIR/label-underscore.rs:2:5 | LL | '_: loop { | ^^ -error: invalid label name `'_` +error: invalid label name `_` --> $DIR/label-underscore.rs:3:15 | LL | break '_ diff --git a/tests/ui/parser/require-parens-for-chained-comparison.stderr b/tests/ui/parser/require-parens-for-chained-comparison.stderr index 857c4a55788..f7a7cb848c0 100644 --- a/tests/ui/parser/require-parens-for-chained-comparison.stderr +++ b/tests/ui/parser/require-parens-for-chained-comparison.stderr @@ -53,7 +53,7 @@ help: use `::<...>` instead of `<...>` to specify lifetime, type, or const argum LL | let _ = f::(); | ++ -error: invalid label name `'_` +error: invalid label name `_` --> $DIR/require-parens-for-chained-comparison.rs:22:15 | LL | let _ = f<'_, i8>(); @@ -81,7 +81,7 @@ help: use `::<...>` instead of `<...>` to specify lifetime, type, or const argum LL | let _ = f::<'_, i8>(); | ++ -error: invalid label name `'_` +error: invalid label name `_` --> $DIR/require-parens-for-chained-comparison.rs:29:7 | LL | f<'_>(); -- cgit 1.4.1-3-g733a5 From 4970127c33a2cfd7b7035daedba7cac512a2e201 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 22 Aug 2025 13:06:49 +0800 Subject: address review comments --- compiler/rustc_parse/messages.ftl | 5 ++--- compiler/rustc_parse/src/errors.rs | 5 ++--- compiler/rustc_parse/src/parser/expr.rs | 8 +------- compiler/rustc_parse/src/parser/ty.rs | 3 +-- compiler/rustc_span/src/symbol.rs | 12 ++++++++---- tests/ui/closures/issue-52437.rs | 2 +- tests/ui/closures/issue-52437.stderr | 2 +- tests/ui/issues/issue-46311.rs | 2 +- tests/ui/issues/issue-46311.stderr | 2 +- tests/ui/label/label-static.rs | 4 ++-- tests/ui/label/label-static.stderr | 4 ++-- tests/ui/label/label-underscore.rs | 4 ++-- tests/ui/label/label-underscore.stderr | 4 ++-- tests/ui/parser/require-parens-for-chained-comparison.rs | 4 ++-- tests/ui/parser/require-parens-for-chained-comparison.stderr | 4 ++-- 15 files changed, 30 insertions(+), 35 deletions(-) (limited to 'compiler/rustc_parse/src/parser/expr.rs') diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index a107a682184..40df0d38848 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -468,9 +468,6 @@ parse_invalid_dyn_keyword = invalid `dyn` keyword parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else` parse_invalid_identifier_with_leading_number = identifiers cannot start with a number -parse_invalid_label = - invalid label name `{$name}` - parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid .label = invalid suffix `{$suffix}` .tuple_exception_line_1 = `{$suffix}` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases @@ -500,6 +497,8 @@ parse_invalid_unicode_escape = invalid unicode character escape parse_invalid_variable_declaration = invalid variable declaration +parse_keyword_label = labels cannot use keyword names + parse_keyword_lifetime = lifetimes cannot use keyword names diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 7d84fc2a2f9..4cc53f1e518 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2228,11 +2228,10 @@ pub(crate) struct KeywordLifetime { } #[derive(Diagnostic)] -#[diag(parse_invalid_label)] -pub(crate) struct InvalidLabel { +#[diag(parse_keyword_label)] +pub(crate) struct KeywordLabel { #[primary_span] pub span: Span, - pub name: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index effcfd54a8f..4ac5e1a578c 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3094,13 +3094,7 @@ impl<'a> Parser<'a> { if let Some((ident, is_raw)) = self.token.lifetime() { // Disallow `'fn`, but with a better error message than `expect_lifetime`. if matches!(is_raw, IdentIsRaw::No) && ident.without_first_quote().is_reserved() { - self.dcx().emit_err(errors::InvalidLabel { - span: ident.span, - // `IntoDiagArg` prints the symbol as if it was an ident, - // so `'break` is printed as `r#break`. We don't want that - // here so convert to string eagerly. - name: ident.without_first_quote().name.to_string(), - }); + self.dcx().emit_err(errors::KeywordLabel { span: ident.span }); } self.bump(); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 899a43955ab..109c77b9bab 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -1479,8 +1479,7 @@ impl<'a> Parser<'a> { pub(super) fn expect_lifetime(&mut self) -> Lifetime { if let Some((ident, is_raw)) = self.token.lifetime() { if matches!(is_raw, IdentIsRaw::No) - && ident.without_first_quote().is_reserved() - && ![kw::UnderscoreLifetime, kw::StaticLifetime].contains(&ident.name) + && ident.without_first_quote().is_reserved_lifetime() { self.dcx().emit_err(errors::KeywordLifetime { span: ident.span }); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d6acd902c7c..744a4771aae 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -3045,13 +3045,17 @@ impl Ident { self.name.can_be_raw() && self.is_reserved() } + /// Given the name of a lifetime without the first quote (`'`), + /// returns whether the lifetime name is reserved (therefore invalid) + pub fn is_reserved_lifetime(self) -> bool { + self.is_reserved() && ![kw::Underscore, kw::Static].contains(&self.name) + } + pub fn is_raw_lifetime_guess(self) -> bool { - // this should be kept consistent with `Parser::expect_lifetime` found under - // compiler/rustc_parse/src/parser/ty.rs let name_without_apostrophe = self.without_first_quote(); name_without_apostrophe.name != self.name - && ![kw::UnderscoreLifetime, kw::StaticLifetime].contains(&self.name) - && name_without_apostrophe.is_raw_guess() + && name_without_apostrophe.name.can_be_raw() + && name_without_apostrophe.is_reserved_lifetime() } pub fn guess_print_mode(self) -> IdentPrintMode { diff --git a/tests/ui/closures/issue-52437.rs b/tests/ui/closures/issue-52437.rs index 0655eac517b..98b04d179af 100644 --- a/tests/ui/closures/issue-52437.rs +++ b/tests/ui/closures/issue-52437.rs @@ -1,5 +1,5 @@ fn main() { [(); &(&'static: loop { |x| {}; }) as *const _ as usize] - //~^ ERROR: invalid label name `static` + //~^ ERROR: labels cannot use keyword names //~| ERROR: type annotations needed } diff --git a/tests/ui/closures/issue-52437.stderr b/tests/ui/closures/issue-52437.stderr index e8832f42f10..8c6fa097ec5 100644 --- a/tests/ui/closures/issue-52437.stderr +++ b/tests/ui/closures/issue-52437.stderr @@ -1,4 +1,4 @@ -error: invalid label name `static` +error: labels cannot use keyword names --> $DIR/issue-52437.rs:2:13 | LL | [(); &(&'static: loop { |x| {}; }) as *const _ as usize] diff --git a/tests/ui/issues/issue-46311.rs b/tests/ui/issues/issue-46311.rs index 69826d78e2d..24435665501 100644 --- a/tests/ui/issues/issue-46311.rs +++ b/tests/ui/issues/issue-46311.rs @@ -1,4 +1,4 @@ fn main() { - 'break: loop { //~ ERROR invalid label name `break` + 'break: loop { //~ ERROR labels cannot use keyword names } } diff --git a/tests/ui/issues/issue-46311.stderr b/tests/ui/issues/issue-46311.stderr index e2fe3130d72..f040ba2c026 100644 --- a/tests/ui/issues/issue-46311.stderr +++ b/tests/ui/issues/issue-46311.stderr @@ -1,4 +1,4 @@ -error: invalid label name `break` +error: labels cannot use keyword names --> $DIR/issue-46311.rs:2:5 | LL | 'break: loop { diff --git a/tests/ui/label/label-static.rs b/tests/ui/label/label-static.rs index 46fa6ac81d8..ef06658506a 100644 --- a/tests/ui/label/label-static.rs +++ b/tests/ui/label/label-static.rs @@ -1,5 +1,5 @@ fn main() { - 'static: loop { //~ ERROR invalid label name `static` - break 'static //~ ERROR invalid label name `static` + 'static: loop { //~ ERROR labels cannot use keyword names + break 'static //~ ERROR labels cannot use keyword names } } diff --git a/tests/ui/label/label-static.stderr b/tests/ui/label/label-static.stderr index 890a7b12c61..f6ae8b44c08 100644 --- a/tests/ui/label/label-static.stderr +++ b/tests/ui/label/label-static.stderr @@ -1,10 +1,10 @@ -error: invalid label name `static` +error: labels cannot use keyword names --> $DIR/label-static.rs:2:5 | LL | 'static: loop { | ^^^^^^^ -error: invalid label name `static` +error: labels cannot use keyword names --> $DIR/label-static.rs:3:15 | LL | break 'static diff --git a/tests/ui/label/label-underscore.rs b/tests/ui/label/label-underscore.rs index ed44957d1d8..8f1f90fe7c0 100644 --- a/tests/ui/label/label-underscore.rs +++ b/tests/ui/label/label-underscore.rs @@ -1,5 +1,5 @@ fn main() { - '_: loop { //~ ERROR invalid label name `_` - break '_ //~ ERROR invalid label name `_` + '_: loop { //~ ERROR labels cannot use keyword names + break '_ //~ ERROR labels cannot use keyword names } } diff --git a/tests/ui/label/label-underscore.stderr b/tests/ui/label/label-underscore.stderr index 31c962b6ae5..c0eb178d0f0 100644 --- a/tests/ui/label/label-underscore.stderr +++ b/tests/ui/label/label-underscore.stderr @@ -1,10 +1,10 @@ -error: invalid label name `_` +error: labels cannot use keyword names --> $DIR/label-underscore.rs:2:5 | LL | '_: loop { | ^^ -error: invalid label name `_` +error: labels cannot use keyword names --> $DIR/label-underscore.rs:3:15 | LL | break '_ diff --git a/tests/ui/parser/require-parens-for-chained-comparison.rs b/tests/ui/parser/require-parens-for-chained-comparison.rs index 21a908923f2..6152fff6c03 100644 --- a/tests/ui/parser/require-parens-for-chained-comparison.rs +++ b/tests/ui/parser/require-parens-for-chained-comparison.rs @@ -24,14 +24,14 @@ fn main() { //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments //~| ERROR expected //~| HELP add `'` to close the char literal - //~| ERROR invalid label name + //~| ERROR labels cannot use keyword names f<'_>(); //~^ ERROR comparison operators cannot be chained //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments //~| ERROR expected //~| HELP add `'` to close the char literal - //~| ERROR invalid label name + //~| ERROR labels cannot use keyword names let _ = f; //~^ ERROR comparison operators cannot be chained diff --git a/tests/ui/parser/require-parens-for-chained-comparison.stderr b/tests/ui/parser/require-parens-for-chained-comparison.stderr index f7a7cb848c0..9edfae36250 100644 --- a/tests/ui/parser/require-parens-for-chained-comparison.stderr +++ b/tests/ui/parser/require-parens-for-chained-comparison.stderr @@ -53,7 +53,7 @@ help: use `::<...>` instead of `<...>` to specify lifetime, type, or const argum LL | let _ = f::(); | ++ -error: invalid label name `_` +error: labels cannot use keyword names --> $DIR/require-parens-for-chained-comparison.rs:22:15 | LL | let _ = f<'_, i8>(); @@ -81,7 +81,7 @@ help: use `::<...>` instead of `<...>` to specify lifetime, type, or const argum LL | let _ = f::<'_, i8>(); | ++ -error: invalid label name `_` +error: labels cannot use keyword names --> $DIR/require-parens-for-chained-comparison.rs:29:7 | LL | f<'_>(); -- cgit 1.4.1-3-g733a5 From 549314bdb7b7a3ac1313feea9283366f5115fca7 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 31 Jul 2025 15:37:43 +0200 Subject: Rewrite the new attribute parser --- compiler/rustc_attr_parsing/messages.ftl | 7 + compiler/rustc_attr_parsing/src/parser.rs | 480 +++++++++++---------- .../rustc_attr_parsing/src/session_diagnostics.rs | 36 ++ compiler/rustc_parse/src/parser/expr.rs | 6 +- compiler/rustc_parse/src/parser/mod.rs | 13 +- compiler/rustc_parse/src/parser/path.rs | 4 +- 6 files changed, 303 insertions(+), 243 deletions(-) (limited to 'compiler/rustc_parse/src/parser/expr.rs') diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 1a2815c54ec..40e9d597530 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -182,3 +182,10 @@ attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute .label = this is not an unsafe attribute .suggestion = remove the `unsafe(...)` .note = extraneous unsafe is not allowed in attributes + +attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr} + .remove_neg_sugg = negative numbers are not literals, try removing the `-` sign + .quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal + +attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes + .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index aecaae947c9..364f8819d13 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -3,45 +3,30 @@ //! //! FIXME(jdonszelmann): delete `rustc_ast/attr/mod.rs` +use std::borrow::Cow; use std::fmt::{Debug, Display}; -use std::iter::Peekable; -use rustc_ast::token::{self, Delimiter, Token}; -use rustc_ast::tokenstream::{TokenStreamIter, TokenTree}; +use rustc_ast::token::{self, Delimiter, MetaVarKind}; +use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path}; use rustc_ast_pretty::pprust; -use rustc_errors::DiagCtxtHandle; +use rustc_errors::PResult; use rustc_hir::{self as hir, AttrPath}; -use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; - -pub struct SegmentIterator<'a> { - offset: usize, - path: &'a PathParser<'a>, -} - -impl<'a> Iterator for SegmentIterator<'a> { - type Item = &'a Ident; - - fn next(&mut self) -> Option { - if self.offset >= self.path.len() { - return None; - } - - let res = match self.path { - PathParser::Ast(ast_path) => &ast_path.segments[self.offset].ident, - PathParser::Attr(attr_path) => &attr_path.segments[self.offset], - }; - - self.offset += 1; - Some(res) - } -} +use rustc_parse::exp; +use rustc_parse::parser::{Parser, PathStyle, token_descr}; +use rustc_session::errors::report_lit_error; +use rustc_session::parse::ParseSess; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, sym}; +use thin_vec::ThinVec; + +use crate::ShouldEmit; +use crate::session_diagnostics::{ + InvalidMetaItem, InvalidMetaItemQuoteIdentSugg, InvalidMetaItemRemoveNegSugg, MetaBadDelim, + MetaBadDelimSugg, SuffixedLiteralInAttribute, +}; #[derive(Clone, Debug)] -pub enum PathParser<'a> { - Ast(&'a Path), - Attr(AttrPath), -} +pub struct PathParser<'a>(pub Cow<'a, Path>); impl<'a> PathParser<'a> { pub fn get_attribute_path(&self) -> hir::AttrPath { @@ -52,21 +37,15 @@ impl<'a> PathParser<'a> { } pub fn segments(&'a self) -> impl Iterator { - SegmentIterator { offset: 0, path: self } + self.0.segments.iter().map(|seg| &seg.ident) } pub fn span(&self) -> Span { - match self { - PathParser::Ast(path) => path.span, - PathParser::Attr(attr_path) => attr_path.span, - } + self.0.span } pub fn len(&self) -> usize { - match self { - PathParser::Ast(path) => path.segments.len(), - PathParser::Attr(attr_path) => attr_path.segments.len(), - } + self.0.segments.len() } pub fn segments_is(&self, segments: &[Symbol]) -> bool { @@ -99,10 +78,7 @@ impl<'a> PathParser<'a> { impl Display for PathParser<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - PathParser::Ast(path) => write!(f, "{}", pprust::path_to_string(path)), - PathParser::Attr(attr_path) => write!(f, "{attr_path}"), - } + write!(f, "{}", pprust::path_to_string(&self.0)) } } @@ -123,21 +99,39 @@ impl<'a> ArgParser<'a> { } } - pub fn from_attr_args<'sess>(value: &'a AttrArgs, dcx: DiagCtxtHandle<'sess>) -> Self { - match value { + pub fn from_attr_args<'sess>( + value: &'a AttrArgs, + parts: &[Symbol], + psess: &'sess ParseSess, + should_emit: ShouldEmit, + ) -> Option { + Some(match value { AttrArgs::Empty => Self::NoArgs, - AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => { - Self::List(MetaItemListParser::new(args, dcx)) - } AttrArgs::Delimited(args) => { - Self::List(MetaItemListParser { sub_parsers: vec![], span: args.dspan.entire() }) + // The arguments of rustc_dummy are not validated if the arguments are delimited + if parts == &[sym::rustc_dummy] { + return Some(ArgParser::List(MetaItemListParser { + sub_parsers: ThinVec::new(), + span: args.dspan.entire(), + })); + } + + if args.delim != Delimiter::Parenthesis { + psess.dcx().emit_err(MetaBadDelim { + span: args.dspan.entire(), + sugg: MetaBadDelimSugg { open: args.dspan.open, close: args.dspan.close }, + }); + return None; + } + + Self::List(MetaItemListParser::new(args, psess, should_emit)?) } AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser { eq_span: *eq_span, - value: expr_to_lit(dcx, &expr, *eq_span), + value: expr_to_lit(psess, &expr, expr.span, should_emit)?, value_span: expr.span, }), - } + }) } /// Asserts that this MetaItem is a list @@ -249,11 +243,16 @@ impl<'a> Debug for MetaItemParser<'a> { impl<'a> MetaItemParser<'a> { /// Create a new parser from a [`NormalAttr`], which is stored inside of any /// [`ast::Attribute`](rustc_ast::Attribute) - pub fn from_attr<'sess>(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'sess>) -> Self { - Self { - path: PathParser::Ast(&attr.item.path), - args: ArgParser::from_attr_args(&attr.item.args, dcx), - } + pub fn from_attr<'sess>( + attr: &'a NormalAttr, + parts: &[Symbol], + psess: &'sess ParseSess, + should_emit: ShouldEmit, + ) -> Option { + Some(Self { + path: PathParser(Cow::Borrowed(&attr.item.path)), + args: ArgParser::from_attr_args(&attr.item.args, parts, psess, should_emit)?, + }) } } @@ -318,215 +317,232 @@ impl NameValueParser { } } -fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr, span: Span) -> MetaItemLit { - // In valid code the value always ends up as a single literal. Otherwise, a dummy - // literal suffices because the error is handled elsewhere. - if let ExprKind::Lit(token_lit) = expr.kind - && let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span) - { - lit +fn expr_to_lit( + psess: &ParseSess, + expr: &Expr, + span: Span, + should_emit: ShouldEmit, +) -> Option { + if let ExprKind::Lit(token_lit) = expr.kind { + let res = MetaItemLit::from_token_lit(token_lit, expr.span); + match res { + Ok(lit) => { + if token_lit.suffix.is_some() { + psess + .dcx() + .create_err(SuffixedLiteralInAttribute { span: lit.span }) + .emit_unless_delay(!should_emit.should_emit()); + None + } else { + if should_emit.should_emit() && !lit.kind.is_unsuffixed() { + // Emit error and continue, we can still parse the attribute as if the suffix isn't there + psess.dcx().emit_err(SuffixedLiteralInAttribute { span: lit.span }); + } + + Some(lit) + } + } + Err(err) => { + let guar = report_lit_error(psess, err, token_lit, expr.span); + let lit = MetaItemLit { + symbol: token_lit.symbol, + suffix: token_lit.suffix, + kind: LitKind::Err(guar), + span: expr.span, + }; + Some(lit) + } + } } else { - let guar = dcx.span_delayed_bug( - span, - "expr in place where literal is expected (builtin attr parsing)", - ); - MetaItemLit { symbol: sym::dummy, suffix: None, kind: LitKind::Err(guar), span } + // Example cases: + // - `#[foo = 1+1]`: results in `ast::ExprKind::BinOp`. + // - `#[foo = include_str!("nonexistent-file.rs")]`: + // results in `ast::ExprKind::Err`. In that case we delay + // the error because an earlier error will have already + // been reported. + let msg = "attribute value must be a literal"; + let mut err = psess.dcx().struct_span_err(span, msg); + if let ExprKind::Err(_) = expr.kind { + err.downgrade_to_delayed_bug(); + } + + err.emit_unless_delay(!should_emit.should_emit()); + None } } struct MetaItemListParserContext<'a, 'sess> { - // the tokens inside the delimiters, so `#[some::attr(a b c)]` would have `a b c` inside - inside_delimiters: Peekable>, - dcx: DiagCtxtHandle<'sess>, + parser: &'a mut Parser<'sess>, + should_emit: ShouldEmit, } impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { - fn done(&mut self) -> bool { - self.inside_delimiters.peek().is_none() - } - - fn next_path(&mut self) -> Option { - // FIXME: Share code with `parse_path`. - let tt = self.inside_delimiters.next().map(|tt| TokenTree::uninterpolate(tt)); - - match tt.as_deref()? { - &TokenTree::Token( - Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span }, - _, - ) => { - // here we have either an ident or pathsep `::`. - - let mut segments = if let &token::Ident(name, _) = kind { - // when we lookahead another pathsep, more path's coming - if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) = - self.inside_delimiters.peek() - { - self.inside_delimiters.next(); - vec![Ident::new(name, span)] - } else { - // else we have a single identifier path, that's all - return Some(AttrPath { - segments: vec![Ident::new(name, span)].into_boxed_slice(), - span, - }); - } - } else { - // if `::` is all we get, we just got a path root - vec![Ident::new(kw::PathRoot, span)] - }; + fn parse_unsuffixed_meta_item_lit(&mut self) -> PResult<'sess, MetaItemLit> { + let uninterpolated_span = self.parser.token_uninterpolated_span(); + let Some(token_lit) = self.parser.eat_token_lit() else { + return self.parser.handle_missing_lit(Parser::mk_meta_item_lit_char); + }; - // one segment accepted. accept n more - loop { - // another ident? - if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) = - self.inside_delimiters - .next() - .map(|tt| TokenTree::uninterpolate(tt)) - .as_deref() - { - segments.push(Ident::new(name, span)); - } else { - return None; - } - // stop unless we see another `::` - if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) = - self.inside_delimiters.peek() - { - self.inside_delimiters.next(); - } else { - break; - } - } - let span = span.with_hi(segments.last().unwrap().span.hi()); - Some(AttrPath { segments: segments.into_boxed_slice(), span }) - } - TokenTree::Token(Token { kind, .. }, _) if kind.is_delim() => None, - _ => { - // malformed attributes can get here. We can't crash, but somewhere else should've - // already warned for this. - None + let lit = match MetaItemLit::from_token_lit(token_lit, self.parser.prev_token.span) { + Ok(lit) => lit, + Err(err) => { + let guar = + report_lit_error(&self.parser.psess, err, token_lit, uninterpolated_span); + // Pack possible quotes and prefixes from the original literal into + // the error literal's symbol so they can be pretty-printed faithfully. + let suffixless_lit = token::Lit::new(token_lit.kind, token_lit.symbol, None); + let symbol = Symbol::intern(&suffixless_lit.to_string()); + let token_lit = token::Lit::new(token::Err(guar), symbol, token_lit.suffix); + MetaItemLit::from_token_lit(token_lit, uninterpolated_span).unwrap() } + }; + + if self.should_emit.should_emit() && !lit.kind.is_unsuffixed() { + // Emit error and continue, we can still parse the attribute as if the suffix isn't there + self.parser.dcx().emit_err(SuffixedLiteralInAttribute { span: lit.span }); } - } - fn value(&mut self) -> Option { - match self.inside_delimiters.next() { - Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => { - MetaItemListParserContext { - inside_delimiters: inner_tokens.iter().peekable(), - dcx: self.dcx, - } - .value() - } - Some(TokenTree::Token(token, _)) => MetaItemLit::from_token(token), - _ => None, + Ok(lit) + } + + fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser<'static>> { + if let Some(MetaVarKind::Meta { has_meta_form }) = self.parser.token.is_metavar_seq() { + return if has_meta_form { + let attr_item = self + .parser + .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| { + MetaItemListParserContext { parser: this, should_emit: self.should_emit } + .parse_attr_item() + }) + .unwrap(); + Ok(attr_item) + } else { + self.parser.unexpected_any() + }; } + + let path = self.parser.parse_path(PathStyle::Mod)?; + + // Check style of arguments that this meta item has + let args = if self.parser.check(exp!(OpenParen)) { + let start = self.parser.token.span; + let (sub_parsers, _) = self.parser.parse_paren_comma_seq(|parser| { + MetaItemListParserContext { parser, should_emit: self.should_emit } + .parse_meta_item_inner() + })?; + let end = self.parser.prev_token.span; + ArgParser::List(MetaItemListParser { sub_parsers, span: start.with_hi(end.hi()) }) + } else if self.parser.eat(exp!(Eq)) { + let eq_span = self.parser.prev_token.span; + let value = self.parse_unsuffixed_meta_item_lit()?; + + ArgParser::NameValue(NameValueParser { eq_span, value, value_span: value.span }) + } else { + ArgParser::NoArgs + }; + + Ok(MetaItemParser { path: PathParser(Cow::Owned(path)), args }) } - /// parses one element on the inside of a list attribute like `#[my_attr( )]` - /// - /// parses a path followed be either: - /// 1. nothing (a word attr) - /// 2. a parenthesized list - /// 3. an equals sign and a literal (name-value) - /// - /// Can also parse *just* a literal. This is for cases like as `#[my_attr("literal")]` - /// where no path is given before the literal - /// - /// Some exceptions too for interpolated attributes which are already pre-processed - fn next(&mut self) -> Option> { - // a list element is either a literal - if let Some(TokenTree::Token(token, _)) = self.inside_delimiters.peek() - && let Some(lit) = MetaItemLit::from_token(token) - { - self.inside_delimiters.next(); - return Some(MetaItemOrLitParser::Lit(lit)); - } else if let Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) = - self.inside_delimiters.peek() + fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser<'static>> { + match self.parse_unsuffixed_meta_item_lit() { + Ok(lit) => return Ok(MetaItemOrLitParser::Lit(lit)), + Err(err) => err.cancel(), // we provide a better error below + } + + match self.parse_attr_item() { + Ok(mi) => return Ok(MetaItemOrLitParser::MetaItemParser(mi)), + Err(err) => err.cancel(), // we provide a better error below + } + + let mut err = InvalidMetaItem { + span: self.parser.token.span, + descr: token_descr(&self.parser.token), + quote_ident_sugg: None, + remove_neg_sugg: None, + }; + + // Suggest quoting idents, e.g. in `#[cfg(key = value)]`. We don't use `Token::ident` and + // don't `uninterpolate` the token to avoid suggesting anything butchered or questionable + // when macro metavariables are involved. + if self.parser.prev_token == token::Eq + && let token::Ident(..) = self.parser.token.kind { - self.inside_delimiters.next(); - return MetaItemListParserContext { - inside_delimiters: inner_tokens.iter().peekable(), - dcx: self.dcx, + let before = self.parser.token.span.shrink_to_lo(); + while let token::Ident(..) = self.parser.token.kind { + self.parser.bump(); } - .next(); + err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg { + before, + after: self.parser.prev_token.span.shrink_to_hi(), + }); } - // or a path. - let path = self.next_path()?; - - // Paths can be followed by: - // - `(more meta items)` (another list) - // - `= lit` (a name-value) - // - nothing - Some(MetaItemOrLitParser::MetaItemParser(match self.inside_delimiters.peek() { - Some(TokenTree::Delimited(dspan, _, Delimiter::Parenthesis, inner_tokens)) => { - self.inside_delimiters.next(); - - MetaItemParser { - path: PathParser::Attr(path), - args: ArgParser::List(MetaItemListParser::new_tts( - inner_tokens.iter(), - dspan.entire(), - self.dcx, - )), - } - } - Some(TokenTree::Delimited(_, ..)) => { - self.inside_delimiters.next(); - // self.dcx.span_delayed_bug(span.entire(), "wrong delimiters"); - return None; - } - Some(TokenTree::Token(Token { kind: token::Eq, span }, _)) => { - self.inside_delimiters.next(); - let value = self.value()?; - MetaItemParser { - path: PathParser::Attr(path), - args: ArgParser::NameValue(NameValueParser { - eq_span: *span, - value_span: value.span, - value, - }), - } - } - _ => MetaItemParser { path: PathParser::Attr(path), args: ArgParser::NoArgs }, - })) + if self.parser.token == token::Minus + && self + .parser + .look_ahead(1, |t| matches!(t.kind, rustc_ast::token::TokenKind::Literal { .. })) + { + err.remove_neg_sugg = + Some(InvalidMetaItemRemoveNegSugg { negative_sign: self.parser.token.span }); + self.parser.bump(); + self.parser.bump(); + } + + Err(self.parser.dcx().create_err(err)) } - fn parse(mut self, span: Span) -> MetaItemListParser<'a> { - let mut sub_parsers = Vec::new(); + fn parse( + tokens: TokenStream, + psess: &'sess ParseSess, + span: Span, + should_emit: ShouldEmit, + ) -> PResult<'sess, MetaItemListParser<'static>> { + let mut parser = Parser::new(psess, tokens, None); + let mut this = MetaItemListParserContext { parser: &mut parser, should_emit }; - while !self.done() { - let Some(n) = self.next() else { - continue; - }; - sub_parsers.push(n); + // Presumably, the majority of the time there will only be one attr. + let mut sub_parsers = ThinVec::with_capacity(1); + while this.parser.token != token::Eof { + sub_parsers.push(this.parse_meta_item_inner()?); - match self.inside_delimiters.peek() { - None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => { - self.inside_delimiters.next(); - } - Some(_) => {} + if !this.parser.eat(exp!(Comma)) { + break; } } - MetaItemListParser { sub_parsers, span } + if parser.token != token::Eof { + parser.unexpected()?; + } + + Ok(MetaItemListParser { sub_parsers, span }) } } #[derive(Debug, Clone)] pub struct MetaItemListParser<'a> { - sub_parsers: Vec>, + sub_parsers: ThinVec>, pub span: Span, } impl<'a> MetaItemListParser<'a> { - fn new<'sess>(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'sess>) -> Self { - MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx) - } - - fn new_tts<'sess>(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'sess>) -> Self { - MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span) + fn new<'sess>( + delim: &'a DelimArgs, + psess: &'sess ParseSess, + should_emit: ShouldEmit, + ) -> Option { + match MetaItemListParserContext::parse( + delim.tokens.clone(), + psess, + delim.dspan.entire(), + should_emit, + ) { + Ok(s) => Some(s), + Err(e) => { + e.emit_unless_delay(!should_emit.should_emit()); + None + } + } } /// Lets you pick and choose as what you want to parse each element in the list diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index b877caa143a..593affc0537 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -790,3 +790,39 @@ pub(crate) struct MetaBadDelimSugg { #[suggestion_part(code = ")")] pub close: Span, } + +#[derive(Diagnostic)] +#[diag(attr_parsing_invalid_meta_item)] +pub(crate) struct InvalidMetaItem { + #[primary_span] + pub span: Span, + pub descr: String, + #[subdiagnostic] + pub quote_ident_sugg: Option, + #[subdiagnostic] + pub remove_neg_sugg: Option, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(attr_parsing_quote_ident_sugg, applicability = "machine-applicable")] +pub(crate) struct InvalidMetaItemQuoteIdentSugg { + #[suggestion_part(code = "\"")] + pub before: Span, + #[suggestion_part(code = "\"")] + pub after: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(attr_parsing_remove_neg_sugg, applicability = "machine-applicable")] +pub(crate) struct InvalidMetaItemRemoveNegSugg { + #[suggestion_part(code = "")] + pub negative_sign: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_suffixed_literal_in_attribute)] +#[help] +pub(crate) struct SuffixedLiteralInAttribute { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index cb7f755f524..6aa6fc2c790 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2077,7 +2077,7 @@ impl<'a> Parser<'a> { (token::Lit { symbol: name, suffix: None, kind: token::Char }, span) } - fn mk_meta_item_lit_char(name: Symbol, span: Span) -> MetaItemLit { + pub fn mk_meta_item_lit_char(name: Symbol, span: Span) -> MetaItemLit { ast::MetaItemLit { symbol: name, suffix: None, @@ -2086,7 +2086,7 @@ impl<'a> Parser<'a> { } } - fn handle_missing_lit( + pub fn handle_missing_lit( &mut self, mk_lit_char: impl FnOnce(Symbol, Span) -> L, ) -> PResult<'a, L> { @@ -2156,7 +2156,7 @@ impl<'a> Parser<'a> { /// Keep this in sync with `Token::can_begin_literal_maybe_minus` and /// `Lit::from_token` (excluding unary negation). - fn eat_token_lit(&mut self) -> Option { + pub fn eat_token_lit(&mut self) -> Option { let check_expr = |expr: Box| { if let ast::ExprKind::Lit(token_lit) = expr.kind { Some(token_lit) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 21f42b54f21..d19114df812 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -24,7 +24,7 @@ pub use diagnostics::AttemptLocalParseRecovery; pub(crate) use expr::ForbiddenLetReason; pub(crate) use item::{FnContext, FnParseMode}; pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; -use path::PathStyle; +pub use path::PathStyle; use rustc_ast::token::{ self, IdentIsRaw, InvisibleOrigin, MetaVarKind, NtExprKind, NtPatKind, Token, TokenKind, }; @@ -285,7 +285,7 @@ pub enum FollowedByType { } #[derive(Copy, Clone, Debug)] -enum Trailing { +pub enum Trailing { No, Yes, } @@ -494,7 +494,7 @@ impl<'a> Parser<'a> { /// This method will automatically add `tok` to `expected_token_types` if `tok` is not /// encountered. #[inline] - fn check(&mut self, exp: ExpTokenPair<'_>) -> bool { + pub fn check(&mut self, exp: ExpTokenPair<'_>) -> bool { let is_present = self.token == *exp.tok; if !is_present { self.expected_token_types.insert(exp.token_type); @@ -633,7 +633,7 @@ impl<'a> Parser<'a> { } /// Consume a sequence produced by a metavar expansion, if present. - fn eat_metavar_seq( + pub fn eat_metavar_seq( &mut self, mv_kind: MetaVarKind, f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, @@ -1094,7 +1094,7 @@ impl<'a> Parser<'a> { /// Parses a comma-separated sequence delimited by parentheses (e.g. `(x, y)`). /// The function `f` must consume tokens until reaching the next separator or /// closing bracket. - fn parse_paren_comma_seq( + pub fn parse_paren_comma_seq( &mut self, f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (ThinVec, Trailing)> { @@ -1355,7 +1355,8 @@ impl<'a> Parser<'a> { AttrArgs::Delimited(args) } else if self.eat(exp!(Eq)) { let eq_span = self.prev_token.span; - AttrArgs::Eq { eq_span, expr: self.parse_expr_force_collect()? } + let expr = self.parse_expr_force_collect()?; + AttrArgs::Eq { eq_span, expr } } else { AttrArgs::Empty }) diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 37fc723cd89..86045648859 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -26,7 +26,7 @@ use crate::parser::{ /// Specifies how to parse a path. #[derive(Copy, Clone, PartialEq)] -pub(super) enum PathStyle { +pub enum PathStyle { /// In some contexts, notably in expressions, paths with generic arguments are ambiguous /// with something else. For example, in expressions `segment < ....` can be interpreted /// as a comparison and `segment ( ....` can be interpreted as a function call. @@ -150,7 +150,7 @@ impl<'a> Parser<'a> { true } - pub(super) fn parse_path(&mut self, style: PathStyle) -> PResult<'a, Path> { + pub fn parse_path(&mut self, style: PathStyle) -> PResult<'a, Path> { self.parse_path_inner(style, None) } -- cgit 1.4.1-3-g733a5 From a06c3887bd1ce85aeef491eef42b3a3d774413bc Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 25 Aug 2025 07:29:54 +1000 Subject: Remove the lifetime from `ExpTokenPair`/`SeqSep`. `TokenKind` now impls `Copy`, so we can store it directly rather than a reference. --- compiler/rustc_parse/src/parser/diagnostics.rs | 18 ++++---- compiler/rustc_parse/src/parser/expr.rs | 8 ++-- compiler/rustc_parse/src/parser/item.rs | 4 +- compiler/rustc_parse/src/parser/mod.rs | 62 +++++++++++++------------- compiler/rustc_parse/src/parser/token_type.rs | 6 +-- 5 files changed, 49 insertions(+), 49 deletions(-) (limited to 'compiler/rustc_parse/src/parser/expr.rs') diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 220e4ac18fc..a28af7833c3 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -463,8 +463,8 @@ impl<'a> Parser<'a> { pub(super) fn expected_one_of_not_found( &mut self, - edible: &[ExpTokenPair<'_>], - inedible: &[ExpTokenPair<'_>], + edible: &[ExpTokenPair], + inedible: &[ExpTokenPair], ) -> PResult<'a, ErrorGuaranteed> { debug!("expected_one_of_not_found(edible: {:?}, inedible: {:?})", edible, inedible); fn tokens_to_string(tokens: &[TokenType]) -> String { @@ -1092,7 +1092,7 @@ impl<'a> Parser<'a> { /// Eats and discards tokens until one of `closes` is encountered. Respects token trees, /// passes through any errors encountered. Used for error recovery. - pub(super) fn eat_to_tokens(&mut self, closes: &[ExpTokenPair<'_>]) { + pub(super) fn eat_to_tokens(&mut self, closes: &[ExpTokenPair]) { if let Err(err) = self .parse_seq_to_before_tokens(closes, &[], SeqSep::none(), |p| Ok(p.parse_token_tree())) { @@ -1113,7 +1113,7 @@ impl<'a> Parser<'a> { pub(super) fn check_trailing_angle_brackets( &mut self, segment: &PathSegment, - end: &[ExpTokenPair<'_>], + end: &[ExpTokenPair], ) -> Option { if !self.may_recover() { return None; @@ -1196,7 +1196,7 @@ impl<'a> Parser<'a> { // second case. if self.look_ahead(position, |t| { trace!("check_trailing_angle_brackets: t={:?}", t); - end.iter().any(|exp| exp.tok == &t.kind) + end.iter().any(|exp| exp.tok == t.kind) }) { // Eat from where we started until the end token so that parsing can continue // as if we didn't have those extra angle brackets. @@ -2120,8 +2120,8 @@ impl<'a> Parser<'a> { pub(super) fn recover_seq_parse_error( &mut self, - open: ExpTokenPair<'_>, - close: ExpTokenPair<'_>, + open: ExpTokenPair, + close: ExpTokenPair, lo: Span, err: Diag<'a>, ) -> Box { @@ -2386,8 +2386,8 @@ impl<'a> Parser<'a> { pub(super) fn consume_block( &mut self, - open: ExpTokenPair<'_>, - close: ExpTokenPair<'_>, + open: ExpTokenPair, + close: ExpTokenPair, consume_close: ConsumeClosingDelim, ) { let mut brace_depth = 0; diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 1499808966c..f0f58e901a9 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1598,7 +1598,7 @@ impl<'a> Parser<'a> { self.maybe_recover_from_bad_qpath(expr) } - fn parse_expr_array_or_repeat(&mut self, close: ExpTokenPair<'_>) -> PResult<'a, Box> { + fn parse_expr_array_or_repeat(&mut self, close: ExpTokenPair) -> PResult<'a, Box> { let lo = self.token.span; self.bump(); // `[` or other open delim @@ -3661,7 +3661,7 @@ impl<'a> Parser<'a> { &mut self, pth: ast::Path, recover: bool, - close: ExpTokenPair<'_>, + close: ExpTokenPair, ) -> PResult< 'a, ( @@ -3680,8 +3680,8 @@ impl<'a> Parser<'a> { errors::HelpUseLatestEdition::new().add_to_diag(e); }; - while self.token != *close.tok { - if self.eat(exp!(DotDot)) || self.recover_struct_field_dots(close.tok) { + while self.token != close.tok { + if self.eat(exp!(DotDot)) || self.recover_struct_field_dots(&close.tok) { let exp_span = self.prev_token.span; // We permit `.. }` on the left-hand side of a destructuring assignment. if self.check(close) { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index fd9fb65417c..eb264f59fed 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -54,7 +54,7 @@ impl<'a> Parser<'a> { /// - `}` for mod items pub fn parse_mod( &mut self, - term: ExpTokenPair<'_>, + term: ExpTokenPair, ) -> PResult<'a, (AttrVec, ThinVec>, ModSpans)> { let lo = self.token.span; let attrs = self.parse_inner_attributes()?; @@ -1201,7 +1201,7 @@ impl<'a> Parser<'a> { }?; let dash = exp!(Minus); - if self.token != *dash.tok { + if self.token != dash.tok { return Ok(ident); } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index d19114df812..15598f32429 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -261,19 +261,19 @@ struct CaptureState { /// A sequence separator. #[derive(Debug)] -struct SeqSep<'a> { +struct SeqSep { /// The separator token. - sep: Option>, + sep: Option, /// `true` if a trailing separator is allowed. trailing_sep_allowed: bool, } -impl<'a> SeqSep<'a> { - fn trailing_allowed(sep: ExpTokenPair<'a>) -> SeqSep<'a> { +impl SeqSep { + fn trailing_allowed(sep: ExpTokenPair) -> SeqSep { SeqSep { sep: Some(sep), trailing_sep_allowed: true } } - fn none() -> SeqSep<'a> { + fn none() -> SeqSep { SeqSep { sep: None, trailing_sep_allowed: false } } } @@ -425,13 +425,13 @@ impl<'a> Parser<'a> { } /// Expects and consumes the token `t`. Signals an error if the next token is not `t`. - pub fn expect(&mut self, exp: ExpTokenPair<'_>) -> PResult<'a, Recovered> { + pub fn expect(&mut self, exp: ExpTokenPair) -> PResult<'a, Recovered> { if self.expected_token_types.is_empty() { - if self.token == *exp.tok { + if self.token == exp.tok { self.bump(); Ok(Recovered::No) } else { - self.unexpected_try_recover(exp.tok) + self.unexpected_try_recover(&exp.tok) } } else { self.expect_one_of(slice::from_ref(&exp), &[]) @@ -443,13 +443,13 @@ impl<'a> Parser<'a> { /// anything. Signal a fatal error if next token is unexpected. fn expect_one_of( &mut self, - edible: &[ExpTokenPair<'_>], - inedible: &[ExpTokenPair<'_>], + edible: &[ExpTokenPair], + inedible: &[ExpTokenPair], ) -> PResult<'a, Recovered> { - if edible.iter().any(|exp| exp.tok == &self.token.kind) { + if edible.iter().any(|exp| exp.tok == self.token.kind) { self.bump(); Ok(Recovered::No) - } else if inedible.iter().any(|exp| exp.tok == &self.token.kind) { + } else if inedible.iter().any(|exp| exp.tok == self.token.kind) { // leave it in the input Ok(Recovered::No) } else if self.token != token::Eof @@ -494,8 +494,8 @@ impl<'a> Parser<'a> { /// This method will automatically add `tok` to `expected_token_types` if `tok` is not /// encountered. #[inline] - pub fn check(&mut self, exp: ExpTokenPair<'_>) -> bool { - let is_present = self.token == *exp.tok; + pub fn check(&mut self, exp: ExpTokenPair) -> bool { + let is_present = self.token == exp.tok; if !is_present { self.expected_token_types.insert(exp.token_type); } @@ -542,7 +542,7 @@ impl<'a> Parser<'a> { /// Consumes a token 'tok' if it exists. Returns whether the given token was present. #[inline] #[must_use] - pub fn eat(&mut self, exp: ExpTokenPair<'_>) -> bool { + pub fn eat(&mut self, exp: ExpTokenPair) -> bool { let is_present = self.check(exp); if is_present { self.bump() @@ -745,13 +745,13 @@ impl<'a> Parser<'a> { /// Eats the expected token if it's present possibly breaking /// compound tokens like multi-character operators in process. /// Returns `true` if the token was eaten. - fn break_and_eat(&mut self, exp: ExpTokenPair<'_>) -> bool { - if self.token == *exp.tok { + fn break_and_eat(&mut self, exp: ExpTokenPair) -> bool { + if self.token == exp.tok { self.bump(); return true; } match self.token.kind.break_two_token_op(1) { - Some((first, second)) if first == *exp.tok => { + Some((first, second)) if first == exp.tok => { let first_span = self.psess.source_map().start_point(self.token.span); let second_span = self.token.span.with_lo(first_span.hi()); self.token = Token::new(first, first_span); @@ -826,7 +826,7 @@ impl<'a> Parser<'a> { /// Checks if the next token is contained within `closes`, and returns `true` if so. fn expect_any_with_type( &mut self, - closes_expected: &[ExpTokenPair<'_>], + closes_expected: &[ExpTokenPair], closes_not_expected: &[&TokenKind], ) -> bool { closes_expected.iter().any(|&close| self.check(close)) @@ -838,9 +838,9 @@ impl<'a> Parser<'a> { /// closing bracket. fn parse_seq_to_before_tokens( &mut self, - closes_expected: &[ExpTokenPair<'_>], + closes_expected: &[ExpTokenPair], closes_not_expected: &[&TokenKind], - sep: SeqSep<'_>, + sep: SeqSep, mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (ThinVec, Trailing, Recovered)> { let mut first = true; @@ -869,7 +869,7 @@ impl<'a> Parser<'a> { } Err(mut expect_err) => { let sp = self.prev_token.span.shrink_to_hi(); - let token_str = pprust::token_kind_to_string(exp.tok); + let token_str = pprust::token_kind_to_string(&exp.tok); match self.current_closure.take() { Some(closure_spans) if self.token == TokenKind::Semi => { @@ -1039,8 +1039,8 @@ impl<'a> Parser<'a> { /// closing bracket. fn parse_seq_to_before_end( &mut self, - close: ExpTokenPair<'_>, - sep: SeqSep<'_>, + close: ExpTokenPair, + sep: SeqSep, f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (ThinVec, Trailing, Recovered)> { self.parse_seq_to_before_tokens(&[close], &[], sep, f) @@ -1051,8 +1051,8 @@ impl<'a> Parser<'a> { /// closing bracket. fn parse_seq_to_end( &mut self, - close: ExpTokenPair<'_>, - sep: SeqSep<'_>, + close: ExpTokenPair, + sep: SeqSep, f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (ThinVec, Trailing)> { let (val, trailing, recovered) = self.parse_seq_to_before_end(close, sep, f)?; @@ -1070,9 +1070,9 @@ impl<'a> Parser<'a> { /// closing bracket. fn parse_unspanned_seq( &mut self, - open: ExpTokenPair<'_>, - close: ExpTokenPair<'_>, - sep: SeqSep<'_>, + open: ExpTokenPair, + close: ExpTokenPair, + sep: SeqSep, f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (ThinVec, Trailing)> { self.expect(open)?; @@ -1084,8 +1084,8 @@ impl<'a> Parser<'a> { /// closing bracket. fn parse_delim_comma_seq( &mut self, - open: ExpTokenPair<'_>, - close: ExpTokenPair<'_>, + open: ExpTokenPair, + close: ExpTokenPair, f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (ThinVec, Trailing)> { self.parse_unspanned_seq(open, close, SeqSep::trailing_allowed(exp!(Comma)), f) diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs index b91548196a3..bd4bb368df0 100644 --- a/compiler/rustc_parse/src/parser/token_type.rs +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -416,8 +416,8 @@ impl TokenType { /// is always by used those methods. The second field is only used when the /// first field doesn't match. #[derive(Clone, Copy, Debug)] -pub struct ExpTokenPair<'a> { - pub tok: &'a TokenKind, +pub struct ExpTokenPair { + pub tok: TokenKind, pub token_type: TokenType, } @@ -444,7 +444,7 @@ macro_rules! exp { // `ExpTokenPair` helper rules. (@tok, $tok:ident) => { $crate::parser::token_type::ExpTokenPair { - tok: &rustc_ast::token::$tok, + tok: rustc_ast::token::$tok, token_type: $crate::parser::token_type::TokenType::$tok } }; -- cgit 1.4.1-3-g733a5 From f32870927604ab4dbf12ff84681b77e65a096940 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 28 Aug 2025 20:03:58 +0200 Subject: Improve error messages around invalid literals in attribute arguments Signed-off-by: Jonathan Brouwer --- compiler/rustc_attr_parsing/src/parser.rs | 59 +++++++++++++++++++------------ compiler/rustc_parse/src/parser/expr.rs | 4 +-- compiler/rustc_session/src/errors.rs | 24 +++++++------ 3 files changed, 52 insertions(+), 35 deletions(-) (limited to 'compiler/rustc_parse/src/parser/expr.rs') diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 6d3cf684296..4f903594225 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -10,11 +10,11 @@ use rustc_ast::token::{self, Delimiter, MetaVarKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path}; use rustc_ast_pretty::pprust; -use rustc_errors::PResult; +use rustc_errors::{Diag, PResult}; use rustc_hir::{self as hir, AttrPath}; use rustc_parse::exp; use rustc_parse::parser::{Parser, PathStyle, token_descr}; -use rustc_session::errors::report_lit_error; +use rustc_session::errors::{create_lit_error, report_lit_error}; use rustc_session::parse::ParseSess; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, sym}; use thin_vec::ThinVec; @@ -379,22 +379,23 @@ struct MetaItemListParserContext<'a, 'sess> { impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { fn parse_unsuffixed_meta_item_lit(&mut self) -> PResult<'sess, MetaItemLit> { - let uninterpolated_span = self.parser.token_uninterpolated_span(); - let Some(token_lit) = self.parser.eat_token_lit() else { - return self.parser.handle_missing_lit(Parser::mk_meta_item_lit_char); - }; + let Some(token_lit) = self.parser.eat_token_lit() else { return Err(self.expected_lit()) }; + self.unsuffixed_meta_item_from_lit(token_lit) + } + fn unsuffixed_meta_item_from_lit( + &mut self, + token_lit: token::Lit, + ) -> PResult<'sess, MetaItemLit> { let lit = match MetaItemLit::from_token_lit(token_lit, self.parser.prev_token.span) { Ok(lit) => lit, Err(err) => { - let guar = - report_lit_error(&self.parser.psess, err, token_lit, uninterpolated_span); - // Pack possible quotes and prefixes from the original literal into - // the error literal's symbol so they can be pretty-printed faithfully. - let suffixless_lit = token::Lit::new(token_lit.kind, token_lit.symbol, None); - let symbol = Symbol::intern(&suffixless_lit.to_string()); - let token_lit = token::Lit::new(token::Err(guar), symbol, token_lit.suffix); - MetaItemLit::from_token_lit(token_lit, uninterpolated_span).unwrap() + return Err(create_lit_error( + &self.parser.psess, + err, + token_lit, + self.parser.prev_token_uninterpolated_span(), + )); } }; @@ -448,16 +449,28 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { } fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser<'static>> { - match self.parse_unsuffixed_meta_item_lit() { - Ok(lit) => return Ok(MetaItemOrLitParser::Lit(lit)), - Err(err) => err.cancel(), // we provide a better error below - } - - match self.parse_attr_item() { - Ok(mi) => return Ok(MetaItemOrLitParser::MetaItemParser(mi)), - Err(err) => err.cancel(), // we provide a better error below + if let Some(token_lit) = self.parser.eat_token_lit() { + // If a literal token is parsed, we commit to parsing a MetaItemLit for better errors + Ok(MetaItemOrLitParser::Lit(self.unsuffixed_meta_item_from_lit(token_lit)?)) + } else { + let prev_pros = self.parser.approx_token_stream_pos(); + match self.parse_attr_item() { + Ok(item) => Ok(MetaItemOrLitParser::MetaItemParser(item)), + Err(err) => { + // If `parse_attr_item` made any progress, it likely has a more precise error we should prefer + // If it didn't make progress we use the `expected_lit` from below + if self.parser.approx_token_stream_pos() != prev_pros { + Err(err) + } else { + err.cancel(); + Err(self.expected_lit()) + } + } + } } + } + fn expected_lit(&mut self) -> Diag<'sess> { let mut err = InvalidMetaItem { span: self.parser.token.span, descr: token_descr(&self.parser.token), @@ -492,7 +505,7 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { self.parser.bump(); } - Err(self.parser.dcx().create_err(err)) + self.parser.dcx().create_err(err) } fn parse( diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f0f58e901a9..7de4f6efd0b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2077,7 +2077,7 @@ impl<'a> Parser<'a> { (token::Lit { symbol: name, suffix: None, kind: token::Char }, span) } - pub fn mk_meta_item_lit_char(name: Symbol, span: Span) -> MetaItemLit { + fn mk_meta_item_lit_char(name: Symbol, span: Span) -> MetaItemLit { ast::MetaItemLit { symbol: name, suffix: None, @@ -2086,7 +2086,7 @@ impl<'a> Parser<'a> { } } - pub fn handle_missing_lit( + fn handle_missing_lit( &mut self, mk_lit_char: impl FnOnce(Symbol, Span) -> L, ) -> PResult<'a, L> { diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 34da54a20bf..50bc7348dc9 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -384,6 +384,10 @@ pub fn report_lit_error( lit: token::Lit, span: Span, ) -> ErrorGuaranteed { + create_lit_error(psess, err, lit, span).emit() +} + +pub fn create_lit_error(psess: &ParseSess, err: LitError, lit: token::Lit, span: Span) -> Diag<'_> { // Checks if `s` looks like i32 or u1234 etc. fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { s.len() > 1 && s.starts_with(first_chars) && s[1..].chars().all(|c| c.is_ascii_digit()) @@ -414,32 +418,32 @@ pub fn report_lit_error( let dcx = psess.dcx(); match err { LitError::InvalidSuffix(suffix) => { - dcx.emit_err(InvalidLiteralSuffix { span, kind: lit.kind.descr(), suffix }) + dcx.create_err(InvalidLiteralSuffix { span, kind: lit.kind.descr(), suffix }) } LitError::InvalidIntSuffix(suffix) => { let suf = suffix.as_str(); if looks_like_width_suffix(&['i', 'u'], suf) { // If it looks like a width, try to be helpful. - dcx.emit_err(InvalidIntLiteralWidth { span, width: suf[1..].into() }) + dcx.create_err(InvalidIntLiteralWidth { span, width: suf[1..].into() }) } else if let Some(fixed) = fix_base_capitalisation(lit.symbol.as_str(), suf) { - dcx.emit_err(InvalidNumLiteralBasePrefix { span, fixed }) + dcx.create_err(InvalidNumLiteralBasePrefix { span, fixed }) } else { - dcx.emit_err(InvalidNumLiteralSuffix { span, suffix: suf.to_string() }) + dcx.create_err(InvalidNumLiteralSuffix { span, suffix: suf.to_string() }) } } LitError::InvalidFloatSuffix(suffix) => { let suf = suffix.as_str(); if looks_like_width_suffix(&['f'], suf) { // If it looks like a width, try to be helpful. - dcx.emit_err(InvalidFloatLiteralWidth { span, width: suf[1..].to_string() }) + dcx.create_err(InvalidFloatLiteralWidth { span, width: suf[1..].to_string() }) } else { - dcx.emit_err(InvalidFloatLiteralSuffix { span, suffix: suf.to_string() }) + dcx.create_err(InvalidFloatLiteralSuffix { span, suffix: suf.to_string() }) } } LitError::NonDecimalFloat(base) => match base { - 16 => dcx.emit_err(HexadecimalFloatLiteralNotSupported { span }), - 8 => dcx.emit_err(OctalFloatLiteralNotSupported { span }), - 2 => dcx.emit_err(BinaryFloatLiteralNotSupported { span }), + 16 => dcx.create_err(HexadecimalFloatLiteralNotSupported { span }), + 8 => dcx.create_err(OctalFloatLiteralNotSupported { span }), + 2 => dcx.create_err(BinaryFloatLiteralNotSupported { span }), _ => unreachable!(), }, LitError::IntTooLarge(base) => { @@ -450,7 +454,7 @@ pub fn report_lit_error( 16 => format!("{max:#x}"), _ => format!("{max}"), }; - dcx.emit_err(IntLiteralTooLarge { span, limit }) + dcx.create_err(IntLiteralTooLarge { span, limit }) } } } -- cgit 1.4.1-3-g733a5 From d8df6312d5b3290c6976e5b55f5ae8344cab1bb4 Mon Sep 17 00:00:00 2001 From: Moritz Hedtke Date: Mon, 1 Sep 2025 17:26:20 +0200 Subject: Make `Parser::parse_for_head` public for rustfmt usage --- compiler/rustc_parse/src/parser/expr.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'compiler/rustc_parse/src/parser/expr.rs') diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 7de4f6efd0b..9b09cbba7af 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2910,7 +2910,8 @@ impl<'a> Parser<'a> { } } - fn parse_for_head(&mut self) -> PResult<'a, (Box, Box)> { + // Public to use it for custom `for` expressions in rustfmt forks like https://github.com/tucant/rustfmt + pub fn parse_for_head(&mut self) -> PResult<'a, (Box, Box)> { let begin_paren = if self.token == token::OpenParen { // Record whether we are about to parse `for (`. // This is used below for recovery in case of `for ( $stuff ) $block` -- cgit 1.4.1-3-g733a5 From 7fada167f9a3cfcfa35fbfc33a17b926f8ef3352 Mon Sep 17 00:00:00 2001 From: Moritz Hedtke Date: Sat, 6 Sep 2025 18:01:31 +0200 Subject: Make `LetChainsPolicy` public for rustfmt usage --- compiler/rustc_parse/src/parser/expr.rs | 2 +- compiler/rustc_parse/src/parser/mod.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'compiler/rustc_parse/src/parser/expr.rs') diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 9b09cbba7af..4c02547357e 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2742,7 +2742,7 @@ impl<'a> Parser<'a> { /// The specified `edition` in `let_chains_policy` should be that of the whole `if` construct, /// i.e. the same span we use to later decide whether the drop behaviour should be that of /// edition `..=2021` or that of `2024..`. - // Public because it is used in rustfmt forks such as https://github.com/tucant/rustfmt/blob/30c83df9e1db10007bdd16dafce8a86b404329b2/src/parse/macros/html.rs#L57 for custom if expressions. + // Public to use it for custom `if` expressions in rustfmt forks like https://github.com/tucant/rustfmt pub fn parse_expr_cond( &mut self, let_chains_policy: LetChainsPolicy, diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 15598f32429..3bbca622975 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -22,6 +22,8 @@ use std::{fmt, mem, slice}; use attr_wrapper::{AttrWrapper, UsePreAttrPos}; pub use diagnostics::AttemptLocalParseRecovery; pub(crate) use expr::ForbiddenLetReason; +// Public to use it for custom `if` expressions in rustfmt forks like https://github.com/tucant/rustfmt +pub use expr::LetChainsPolicy; pub(crate) use item::{FnContext, FnParseMode}; pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; pub use path::PathStyle; -- cgit 1.4.1-3-g733a5