From 4e0baddbbf1a6ceadda28f2eb7edc188822942f3 Mon Sep 17 00:00:00 2001 From: Lieselotte <52315535+she3py@users.noreply.github.com> Date: Wed, 3 Jan 2024 15:27:58 +0100 Subject: [PATCH] Recover parentheses in range patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: León Orell Valerian Liehr --- compiler/rustc_parse/messages.ftl | 3 + compiler/rustc_parse/src/errors.rs | 21 +++++++ compiler/rustc_parse/src/parser/pat.rs | 56 ++++++++++++++++++- .../range_pat_interactions2.rs | 3 +- .../range_pat_interactions2.stderr | 17 ++---- tests/ui/parser/pat-recover-ranges.rs | 19 +++++++ tests/ui/parser/pat-recover-ranges.stderr | 50 +++++++++++++++++ 7 files changed, 151 insertions(+), 18 deletions(-) create mode 100644 tests/ui/parser/pat-recover-ranges.rs create mode 100644 tests/ui/parser/pat-recover-ranges.stderr diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index c6bddbfacd6ec..aa8c8538b5408 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -767,6 +767,9 @@ parse_unexpected_if_with_if = unexpected `if` in the condition expression parse_unexpected_lifetime_in_pattern = unexpected lifetime `{$symbol}` in pattern .suggestion = remove the lifetime +parse_unexpected_paren_in_range_pat = range pattern bounds cannot have parentheses +parse_unexpected_paren_in_range_pat_sugg = remove these parentheses + parse_unexpected_parentheses_in_for_head = unexpected parentheses surrounding `for` loop head .suggestion = remove parentheses in `for` loop diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index e276b34ca37af..d67ae14934429 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2378,6 +2378,27 @@ pub(crate) struct ExpectedCommaAfterPatternField { pub span: Span, } +#[derive(Diagnostic)] +#[diag(parse_unexpected_paren_in_range_pat)] +pub(crate) struct UnexpectedParenInRangePat { + #[primary_span] + pub span: Vec, + #[subdiagnostic] + pub sugg: UnexpectedParenInRangePatSugg, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + parse_unexpected_paren_in_range_pat_sugg, + applicability = "machine-applicable" +)] +pub(crate) struct UnexpectedParenInRangePatSugg { + #[suggestion_part(code = "")] + pub start_span: Span, + #[suggestion_part(code = "")] + pub end_span: Span, +} + #[derive(Diagnostic)] #[diag(parse_return_types_use_thin_arrow)] pub(crate) struct ReturnTypesUseThinArrow { diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index afbc253757816..7d17b1d4c4d67 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -6,7 +6,8 @@ use crate::errors::{ InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, - TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam, + TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, + UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, }; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; @@ -579,6 +580,8 @@ impl<'a> Parser<'a> { /// Parse a tuple or parenthesis pattern. fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> { + let open_paren = self.token.span; + let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| { p.parse_pat_allow_top_alt( None, @@ -591,7 +594,29 @@ impl<'a> Parser<'a> { // Here, `(pat,)` is a tuple pattern. // For backward compatibility, `(..)` is a tuple pattern as well. Ok(if fields.len() == 1 && !(trailing_comma || fields[0].is_rest()) { - PatKind::Paren(fields.into_iter().next().unwrap()) + let pat = fields.into_iter().next().unwrap(); + let close_paren = self.prev_token.span; + + match &pat.kind { + // recover ranges with parentheses around the `(start)..` + PatKind::Lit(begin) + if self.may_recover() + && let Some(form) = self.parse_range_end() => + { + self.dcx().emit_err(UnexpectedParenInRangePat { + span: vec![open_paren, close_paren], + sugg: UnexpectedParenInRangePatSugg { + start_span: open_paren, + end_span: close_paren, + }, + }); + + self.parse_pat_range_begin_with(begin.clone(), form)? + } + + // (pat) with optional parentheses + _ => PatKind::Paren(pat), + } } else { PatKind::Tuple(fields) }) @@ -794,11 +819,21 @@ impl<'a> Parser<'a> { || t.can_begin_literal_maybe_minus() // e.g. `42`. || t.is_whole_expr() || t.is_lifetime() // recover `'a` instead of `'a'` + || (self.may_recover() // recover leading `(` + && t.kind == token::OpenDelim(Delimiter::Parenthesis) + && self.look_ahead(dist + 1, |t| t.kind != token::OpenDelim(Delimiter::Parenthesis)) + && self.is_pat_range_end_start(dist + 1)) }) } + /// Parse a range pattern end bound fn parse_pat_range_end(&mut self) -> PResult<'a, P> { - if self.check_inline_const(0) { + // recover leading `(` + let open_paren = (self.may_recover() + && self.eat_noexpect(&token::OpenDelim(Delimiter::Parenthesis))) + .then_some(self.prev_token.span); + + let bound = if self.check_inline_const(0) { self.parse_const_block(self.token.span, true) } else if self.check_path() { let lo = self.token.span; @@ -814,7 +849,22 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path))) } else { self.parse_literal_maybe_minus() + }?; + + // recover trailing `)` + if let Some(open_paren) = open_paren { + self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + + self.dcx().emit_err(UnexpectedParenInRangePat { + span: vec![open_paren, self.prev_token.span], + sugg: UnexpectedParenInRangePatSugg { + start_span: open_paren, + end_span: self.prev_token.span, + }, + }); } + + Ok(bound) } /// Is this the start of a pattern beginning with a path? diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions2.rs b/tests/ui/half-open-range-patterns/range_pat_interactions2.rs index 4615ebd688a9c..0e96cfe785857 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions2.rs +++ b/tests/ui/half-open-range-patterns/range_pat_interactions2.rs @@ -8,8 +8,7 @@ fn main() { for x in -9 + 1..=(9 - 2) { match x as i32 { 0..=(5+1) => errors_only.push(x), - //~^ error: inclusive range with no end - //~| error: expected one of `=>`, `if`, or `|`, found `(` + //~^ error: expected `)`, found `+` 1 | -3..0 => first_or.push(x), y @ (0..5 | 6) => or_two.push(y), y @ 0..const { 5 + 1 } => assert_eq!(y, 5), diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr index 13a5542a4741f..a54f29a3b3263 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr +++ b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr @@ -1,17 +1,8 @@ -error[E0586]: inclusive range with no end - --> $DIR/range_pat_interactions2.rs:10:14 +error: expected `)`, found `+` + --> $DIR/range_pat_interactions2.rs:10:19 | LL | 0..=(5+1) => errors_only.push(x), - | ^^^ help: use `..` instead - | - = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) - -error: expected one of `=>`, `if`, or `|`, found `(` - --> $DIR/range_pat_interactions2.rs:10:17 - | -LL | 0..=(5+1) => errors_only.push(x), - | ^ expected one of `=>`, `if`, or `|` + | ^ expected `)` -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0586`. diff --git a/tests/ui/parser/pat-recover-ranges.rs b/tests/ui/parser/pat-recover-ranges.rs new file mode 100644 index 0000000000000..65a6fc6fe21f3 --- /dev/null +++ b/tests/ui/parser/pat-recover-ranges.rs @@ -0,0 +1,19 @@ +fn main() { + match -1 { + 0..=1 => (), + 0..=(1) => (), + //~^ error: range pattern bounds cannot have parentheses + (-12)..=4 => (), + //~^ error: range pattern bounds cannot have parentheses + (0)..=(-4) => (), + //~^ error: range pattern bounds cannot have parentheses + //~| error: range pattern bounds cannot have parentheses + }; +} + +macro_rules! m { + ($pat:pat) => {}; + (($s:literal)..($e:literal)) => {}; +} + +m!((7)..(7)); diff --git a/tests/ui/parser/pat-recover-ranges.stderr b/tests/ui/parser/pat-recover-ranges.stderr new file mode 100644 index 0000000000000..0d722b5aa95c8 --- /dev/null +++ b/tests/ui/parser/pat-recover-ranges.stderr @@ -0,0 +1,50 @@ +error: range pattern bounds cannot have parentheses + --> $DIR/pat-recover-ranges.rs:4:13 + | +LL | 0..=(1) => (), + | ^ ^ + | +help: remove these parentheses + | +LL - 0..=(1) => (), +LL + 0..=1 => (), + | + +error: range pattern bounds cannot have parentheses + --> $DIR/pat-recover-ranges.rs:6:9 + | +LL | (-12)..=4 => (), + | ^ ^ + | +help: remove these parentheses + | +LL - (-12)..=4 => (), +LL + -12..=4 => (), + | + +error: range pattern bounds cannot have parentheses + --> $DIR/pat-recover-ranges.rs:8:9 + | +LL | (0)..=(-4) => (), + | ^ ^ + | +help: remove these parentheses + | +LL - (0)..=(-4) => (), +LL + 0..=(-4) => (), + | + +error: range pattern bounds cannot have parentheses + --> $DIR/pat-recover-ranges.rs:8:15 + | +LL | (0)..=(-4) => (), + | ^ ^ + | +help: remove these parentheses + | +LL - (0)..=(-4) => (), +LL + (0)..=-4 => (), + | + +error: aborting due to 4 previous errors +