From 55e4e3e3932ef41353efd888ff96dfc96ec61bd3 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Wed, 22 Nov 2023 23:27:15 +0000 Subject: Suggest `let` or `==` on typo'd let-chain When encountering a bare assignment in a let-chain, suggest turning the assignment into a `let` expression or an equality check. ``` error: expected expression, found `let` statement --> $DIR/bad-if-let-suggestion.rs:5:8 | LL | if let x = 1 && i = 2 {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions help: you might have meant to continue the let-chain | LL | if let x = 1 && let i = 2 {} | +++ help: you might have meant to compare for equality | LL | if let x = 1 && i == 2 {} | + ``` --- compiler/rustc_parse/src/parser/expr.rs | 48 +++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 8 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 79567eb3898..e334837c1d4 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength use super::diagnostics::SnapshotParser; use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; @@ -2477,7 +2478,7 @@ impl<'a> Parser<'a> { let mut cond = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, None)?; - CondChecker { parser: self, forbid_let_reason: None }.visit_expr(&mut cond); + CondChecker::new(self).visit_expr(&mut cond); if let ExprKind::Let(_, _, _, None) = cond.kind { // Remove the last feature gating of a `let` expression since it's stable. @@ -2493,6 +2494,8 @@ impl<'a> Parser<'a> { let err = errors::ExpectedExpressionFoundLet { span: self.token.span, reason: ForbiddenLetReason::OtherForbidden, + missing_let: None, + comparison: None, }; if self.prev_token.kind == token::BinOp(token::Or) { // This was part of a closure, the that part of the parser recover. @@ -2876,7 +2879,7 @@ impl<'a> Parser<'a> { let if_span = this.prev_token.span; let mut cond = this.parse_match_guard_condition()?; - CondChecker { parser: this, forbid_let_reason: None }.visit_expr(&mut cond); + CondChecker::new(this).visit_expr(&mut cond); let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond); if has_let_expr { @@ -3552,6 +3555,14 @@ pub(crate) enum ForbiddenLetReason { struct CondChecker<'a> { parser: &'a Parser<'a>, forbid_let_reason: Option, + missing_let: Option, + comparison: Option, +} + +impl<'a> CondChecker<'a> { + fn new(parser: &'a Parser<'a>) -> Self { + CondChecker { parser, forbid_let_reason: None, missing_let: None, comparison: None } + } } impl MutVisitor for CondChecker<'_> { @@ -3562,11 +3573,13 @@ impl MutVisitor for CondChecker<'_> { match e.kind { ExprKind::Let(_, _, _, ref mut is_recovered @ None) => { if let Some(reason) = self.forbid_let_reason { - *is_recovered = Some( - self.parser - .sess - .emit_err(errors::ExpectedExpressionFoundLet { span, reason }), - ); + *is_recovered = + Some(self.parser.sess.emit_err(errors::ExpectedExpressionFoundLet { + span, + reason, + missing_let: self.missing_let, + comparison: self.comparison, + })); } else { self.parser.sess.gated_spans.gate(sym::let_chains, span); } @@ -3590,9 +3603,28 @@ impl MutVisitor for CondChecker<'_> { noop_visit_expr(e, self); self.forbid_let_reason = forbid_let_reason; } + ExprKind::Assign(ref lhs, _, span) => { + let forbid_let_reason = self.forbid_let_reason; + self.forbid_let_reason = Some(OtherForbidden); + let missing_let = self.missing_let; + if let ExprKind::Binary(_, _, rhs) = &lhs.kind + && let ExprKind::Path(_, _) + | ExprKind::Struct(_) + | ExprKind::Call(_, _) + | ExprKind::Array(_) = rhs.kind + { + self.missing_let = + Some(errors::MaybeMissingLet { span: rhs.span.shrink_to_lo() }); + } + let comparison = self.comparison; + self.comparison = Some(errors::MaybeComparison { span: span.shrink_to_hi() }); + noop_visit_expr(e, self); + self.forbid_let_reason = forbid_let_reason; + self.missing_let = missing_let; + self.comparison = comparison; + } ExprKind::Unary(_, _) | ExprKind::Await(_, _) - | ExprKind::Assign(_, _, _) | ExprKind::AssignOp(_, _, _) | ExprKind::Range(_, _, _) | ExprKind::Try(_) -- cgit 1.4.1-3-g733a5 From 075c599188db71474eb2ad3c8b89287f79ac57e3 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Mon, 6 Nov 2023 21:35:21 +0000 Subject: More accurate span for unnecessary parens suggestion --- compiler/rustc_parse/src/errors.rs | 6 ++--- compiler/rustc_parse/src/parser/diagnostics.rs | 29 ++++------------------ compiler/rustc_parse/src/parser/expr.rs | 5 +++- .../recover-for-loop-parens-around-head.stderr | 2 +- 4 files changed, 12 insertions(+), 30 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 e5d4cb6f4da..714ec7f2742 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1275,12 +1275,10 @@ pub(crate) struct ParenthesesInForHead { #[derive(Subdiagnostic)] #[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] pub(crate) struct ParenthesesInForHeadSugg { - #[suggestion_part(code = "{left_snippet}")] + #[suggestion_part(code = " ")] pub left: Span, - pub left_snippet: String, - #[suggestion_part(code = "{right_snippet}")] + #[suggestion_part(code = " ")] pub right: Span, - pub right_snippet: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index ecb840f067e..0d42035e74b 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2001,37 +2001,18 @@ impl<'a> Parser<'a> { pub(super) fn recover_parens_around_for_head( &mut self, pat: P, - begin_paren: Option, + begin_paren: Option<(Span, Span)>, ) -> P { match (&self.token.kind, begin_paren) { - (token::CloseDelim(Delimiter::Parenthesis), Some(begin_par_sp)) => { + (token::CloseDelim(Delimiter::Parenthesis), Some((begin_par_sp, left))) => { + let right = self.prev_token.span.between(self.look_ahead(1, |t| t.span)); self.bump(); - - let sm = self.sess.source_map(); - let left = begin_par_sp; - let right = self.prev_token.span; - let left_snippet = if let Ok(snip) = sm.span_to_prev_source(left) - && !snip.ends_with(' ') - { - " ".to_string() - } else { - "".to_string() - }; - - let right_snippet = if let Ok(snip) = sm.span_to_next_source(right) - && !snip.starts_with(' ') - { - " ".to_string() - } else { - "".to_string() - }; - self.sess.emit_err(ParenthesesInForHead { - span: vec![left, right], + span: vec![begin_par_sp, self.prev_token.span], // With e.g. `for (x) in y)` this would replace `(x) in y)` // with `x) in y)` which is syntactically invalid. // However, this is prevented before we get here. - sugg: ParenthesesInForHeadSugg { left, right, left_snippet, right_snippet }, + sugg: ParenthesesInForHeadSugg { left, right }, }); // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint. diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e334837c1d4..8d04769a568 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2615,7 +2615,10 @@ impl<'a> Parser<'a> { // This is used below for recovery in case of `for ( $stuff ) $block` // in which case we will suggest `for $stuff $block`. let begin_paren = match self.token.kind { - token::OpenDelim(Delimiter::Parenthesis) => Some(self.token.span), + token::OpenDelim(Delimiter::Parenthesis) => Some(( + self.token.span, + self.prev_token.span.between(self.look_ahead(1, |t| t.span)), + )), _ => None, }; diff --git a/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr b/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr index 3bad29f20af..58c83b65680 100644 --- a/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr +++ b/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr @@ -13,7 +13,7 @@ LL | for ( elem in vec ) { help: remove parentheses in `for` loop | LL - for ( elem in vec ) { -LL + for elem in vec { +LL + for elem in vec { | error[E0308]: mismatched types -- cgit 1.4.1-3-g733a5 From c47318983bcad308e0cf438fcd7d7104db607b1a Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Mon, 6 Nov 2023 23:31:13 +0000 Subject: Account for `(pat if expr) => {}` When encountering match arm (pat if expr) => {}, recover and suggest removing parentheses. Fix #100825. --- compiler/rustc_parse/messages.ftl | 3 + compiler/rustc_parse/src/errors.rs | 18 +++ compiler/rustc_parse/src/parser/expr.rs | 125 ++++++++++++++------- .../recover-parens-around-match-arm-head.rs | 5 +- .../recover-parens-around-match-arm-head.stderr | 28 ++++- 5 files changed, 132 insertions(+), 47 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 eadd2892d93..1c3c433d8b7 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -777,6 +777,9 @@ parse_unexpected_lifetime_in_pattern = unexpected lifetime `{$symbol}` in patter parse_unexpected_parentheses_in_for_head = unexpected parentheses surrounding `for` loop head .suggestion = remove parentheses in `for` loop +parse_unexpected_parentheses_in_match_arm_pattern = unexpected parentheses surrounding `match` arm pattern + .suggestion = remove parentheses surrounding the pattern + parse_unexpected_self_in_generic_parameters = unexpected keyword `Self` in generic parameters .note = you cannot use `Self` as a generic parameter because it is reserved for associated items diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 714ec7f2742..03e047b297d 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1281,6 +1281,24 @@ pub(crate) struct ParenthesesInForHeadSugg { pub right: Span, } +#[derive(Diagnostic)] +#[diag(parse_unexpected_parentheses_in_match_arm_pattern)] +pub(crate) struct ParenthesesInMatchPat { + #[primary_span] + pub span: Vec, + #[subdiagnostic] + pub sugg: ParenthesesInMatchPatSugg, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +pub(crate) struct ParenthesesInMatchPatSugg { + #[suggestion_part(code = "")] + pub left: Span, + #[suggestion_part(code = "")] + pub right: Span, +} + #[derive(Diagnostic)] #[diag(parse_doc_comment_on_param_type)] pub(crate) struct DocCommentOnParamType { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8d04769a568..8a4b7b5b99e 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -10,7 +10,7 @@ use super::{ use crate::errors; use crate::maybe_recover_from_interpolated_ty_qpath; use ast::mut_visit::{noop_visit_expr, MutVisitor}; -use ast::{GenBlockKind, Path, PathSegment}; +use ast::{GenBlockKind, Pat, Path, PathSegment}; use core::mem; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; @@ -2856,47 +2856,10 @@ impl<'a> Parser<'a> { } pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> { - // Used to check the `let_chains` and `if_let_guard` features mostly by scanning - // `&&` tokens. - fn check_let_expr(expr: &Expr) -> (bool, bool) { - match &expr.kind { - ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, lhs, rhs) => { - let lhs_rslt = check_let_expr(lhs); - let rhs_rslt = check_let_expr(rhs); - (lhs_rslt.0 || rhs_rslt.0, false) - } - ExprKind::Let(..) => (true, true), - _ => (false, true), - } - } let attrs = self.parse_outer_attributes()?; self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; - let pat = this.parse_pat_allow_top_alt( - None, - RecoverComma::Yes, - RecoverColon::Yes, - CommaRecoveryMode::EitherTupleOrPipe, - )?; - let guard = if this.eat_keyword(kw::If) { - let if_span = this.prev_token.span; - let mut cond = this.parse_match_guard_condition()?; - - CondChecker::new(this).visit_expr(&mut cond); - - let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond); - if has_let_expr { - if does_not_have_bin_op { - // Remove the last feature gating of a `let` expression since it's stable. - this.sess.gated_spans.ungate_last(sym::let_chains, cond.span); - } - let span = if_span.to(cond.span); - this.sess.gated_spans.gate(sym::if_let_guard, span); - } - Some(cond) - } else { - None - }; + let (pat, guard) = this.parse_match_arm_pat_and_guard()?; let arrow_span = this.token.span; if let Err(mut err) = this.expect(&token::FatArrow) { // We might have a `=>` -> `=` or `->` typo (issue #89396). @@ -3026,6 +2989,90 @@ impl<'a> Parser<'a> { }) } + fn parse_match_arm_guard(&mut self) -> PResult<'a, Option>> { + // Used to check the `let_chains` and `if_let_guard` features mostly by scanning + // `&&` tokens. + fn check_let_expr(expr: &Expr) -> (bool, bool) { + match &expr.kind { + ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, lhs, rhs) => { + let lhs_rslt = check_let_expr(lhs); + let rhs_rslt = check_let_expr(rhs); + (lhs_rslt.0 || rhs_rslt.0, false) + } + ExprKind::Let(..) => (true, true), + _ => (false, true), + } + } + if !self.eat_keyword(kw::If) { + // No match arm guard present. + return Ok(None); + } + + let if_span = self.prev_token.span; + let mut cond = self.parse_match_guard_condition()?; + + CondChecker::new(self).visit_expr(&mut cond); + + let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond); + if has_let_expr { + if does_not_have_bin_op { + // Remove the last feature gating of a `let` expression since it's stable. + self.sess.gated_spans.ungate_last(sym::let_chains, cond.span); + } + let span = if_span.to(cond.span); + self.sess.gated_spans.gate(sym::if_let_guard, span); + } + Ok(Some(cond)) + } + + fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P, Option>)> { + if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) { + // Detect and recover from `($pat if $cond) => $arm`. + let left = self.token.span; + match self.parse_pat_allow_top_alt( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::EitherTupleOrPipe, + ) { + Ok(pat) => Ok((pat, self.parse_match_arm_guard()?)), + Err(err) + if let prev_sp = self.prev_token.span + && let true = self.eat_keyword(kw::If) => + { + // We know for certain we've found `($pat if` so far. + let mut cond = match self.parse_match_guard_condition() { + Ok(cond) => cond, + Err(cond_err) => { + cond_err.cancel(); + return Err(err); + } + }; + err.cancel(); + CondChecker::new(self).visit_expr(&mut cond); + self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]); + self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + let right = self.prev_token.span; + self.sess.emit_err(errors::ParenthesesInMatchPat { + span: vec![left, right], + sugg: errors::ParenthesesInMatchPatSugg { left, right }, + }); + Ok((self.mk_pat(left.to(prev_sp), ast::PatKind::Wild), Some(cond))) + } + Err(err) => Err(err), + } + } else { + // Regular parser flow: + let pat = self.parse_pat_allow_top_alt( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::EitherTupleOrPipe, + )?; + Ok((pat, self.parse_match_arm_guard()?)) + } + } + fn parse_match_guard_condition(&mut self) -> PResult<'a, P> { self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, None).map_err( |mut err| { diff --git a/tests/ui/parser/recover/recover-parens-around-match-arm-head.rs b/tests/ui/parser/recover/recover-parens-around-match-arm-head.rs index 9ed733bf079..382d99cd79d 100644 --- a/tests/ui/parser/recover/recover-parens-around-match-arm-head.rs +++ b/tests/ui/parser/recover/recover-parens-around-match-arm-head.rs @@ -2,10 +2,7 @@ fn main() { let val = 42; let x = match val { (0 if true) => { - //~^ ERROR expected identifier, found keyword `if` - //~| ERROR expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found keyword `if` - //~| ERROR expected one of `)`, `,`, `@`, or `|`, found keyword `true` - //~| ERROR mismatched types + //~^ ERROR unexpected parentheses surrounding `match` arm pattern 42u8 } _ => 0u8, diff --git a/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr b/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr index 526d3017b3c..0d1143fbc99 100644 --- a/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr +++ b/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr @@ -1,8 +1,28 @@ -error: expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found keyword `if` - --> $DIR/recover-parens-around-match-arm-head.rs:4:12 +error: unexpected parentheses surrounding `match` arm pattern + --> $DIR/recover-parens-around-match-arm-head.rs:4:9 | LL | (0 if true) => { - | ^^ expected one of `)`, `,`, `...`, `..=`, `..`, or `|` + | ^ ^ + | +help: remove parentheses surrounding the pattern + | +LL - (0 if true) => { +LL + 0 if true => { + | + +error[E0308]: mismatched types + --> $DIR/recover-parens-around-match-arm-head.rs:10:19 + | +LL | let _y: u32 = x; + | --- ^ expected `u32`, found `u8` + | | + | expected due to this + | +help: you can convert a `u8` to a `u32` + | +LL | let _y: u32 = x.into(); + | +++++++ -error: aborting due to previous error +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0308`. -- cgit 1.4.1-3-g733a5 From 0ff331bc78f55d637d0b943686d783642a017512 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Mon, 6 Nov 2023 23:41:49 +0000 Subject: Change how `for (x in foo) {}` is handled Use the same approach used for match arm patterns. --- compiler/rustc_parse/src/parser/diagnostics.rs | 43 ++----------- compiler/rustc_parse/src/parser/expr.rs | 73 ++++++++++++++++------ tests/ui/lint/issue-103435-extra-parentheses.fixed | 5 +- tests/ui/lint/issue-103435-extra-parentheses.rs | 7 +-- .../ui/lint/issue-103435-extra-parentheses.stderr | 18 ++++-- tests/ui/parser/recover/recover-enum2.rs | 3 +- tests/ui/parser/recover/recover-enum2.stderr | 19 +++++- .../recover-for-loop-parens-around-head.fixed | 15 +++++ .../recover/recover-for-loop-parens-around-head.rs | 8 +-- .../recover-for-loop-parens-around-head.stderr | 28 +++++++-- 10 files changed, 138 insertions(+), 81 deletions(-) create mode 100644 tests/ui/parser/recover/recover-for-loop-parens-around-head.fixed (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 0d42035e74b..8921c1c6a03 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -11,12 +11,12 @@ use crate::errors::{ DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg, HelpIdentifierStartsWithNumber, InInTypo, IncorrectAwait, IncorrectSemicolon, - IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg, - PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, - StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, - StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, - TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, - UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType, + IncorrectUseOfAwait, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, + SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, + StructLiteralNeedingParens, StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, + SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator, UnexpectedConstInGenericParam, + UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, + UseEqInstead, WrapType, }; use crate::fluent_generated as fluent; @@ -1994,37 +1994,6 @@ impl<'a> Parser<'a> { } } - /// Recovers a situation like `for ( $pat in $expr )` - /// and suggest writing `for $pat in $expr` instead. - /// - /// This should be called before parsing the `$block`. - pub(super) fn recover_parens_around_for_head( - &mut self, - pat: P, - begin_paren: Option<(Span, Span)>, - ) -> P { - match (&self.token.kind, begin_paren) { - (token::CloseDelim(Delimiter::Parenthesis), Some((begin_par_sp, left))) => { - let right = self.prev_token.span.between(self.look_ahead(1, |t| t.span)); - self.bump(); - self.sess.emit_err(ParenthesesInForHead { - span: vec![begin_par_sp, self.prev_token.span], - // With e.g. `for (x) in y)` this would replace `(x) in y)` - // with `x) in y)` which is syntactically invalid. - // However, this is prevented before we get here. - sugg: ParenthesesInForHeadSugg { left, right }, - }); - - // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint. - pat.and_then(|pat| match pat.kind { - PatKind::Paren(pat) => pat, - _ => P(pat), - }) - } - _ => pat, - } - } - pub(super) fn recover_seq_parse_error( &mut self, delim: Delimiter, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8a4b7b5b99e..88c0c9703db 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2609,33 +2609,66 @@ impl<'a> Parser<'a> { } } - /// Parses `for in ` (`for` token already eaten). - fn parse_expr_for(&mut self, opt_label: Option