From f4c2bdeec9d53c6a7221ebfdb2d995932dff9648 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 27 Nov 2023 18:28:51 +0900 Subject: Suggest swapping the order of `ref` and `box` --- compiler/rustc_parse/src/errors.rs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'compiler/rustc_parse/src/errors.rs') diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 7ce348619c6..0a919240ce5 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -137,6 +137,14 @@ pub(crate) enum InvalidVariableDeclarationSub { UseLetNotVar(#[primary_span] Span), } +#[derive(Diagnostic)] +#[diag(parse_switch_ref_box_order)] +pub(crate) struct SwitchRefBoxOrder { + #[primary_span] + #[suggestion(applicability = "machine-applicable", code = "box ref")] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_invalid_comparison_operator)] pub(crate) struct InvalidComparisonOperator { -- cgit 1.4.1-3-g733a5 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/messages.ftl | 4 ++ compiler/rustc_parse/src/errors.rs | 26 ++++++++++++ compiler/rustc_parse/src/parser/expr.rs | 48 ++++++++++++++++++---- tests/ui/expr/if/bad-if-let-suggestion.rs | 3 -- tests/ui/expr/if/bad-if-let-suggestion.stderr | 22 ++++++---- .../invalid-let-in-a-valid-let-context.stderr | 8 ++++ 6 files changed, 93 insertions(+), 18 deletions(-) (limited to 'compiler/rustc_parse/src/errors.rs') diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 49a2c414467..352a55a7cbc 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -492,9 +492,13 @@ parse_match_arm_body_without_braces = `match` arm body without braces } with a body .suggestion_use_comma_not_semicolon = replace `;` with `,` to end a `match` arm expression +parse_maybe_comparison = you might have meant to compare for equality + parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of `fn` .suggestion = replace `fn` with `impl` here +parse_maybe_missing_let = you might have meant to continue the let-chain + parse_maybe_recover_from_bad_qpath_stage_2 = missing angle brackets in associated item path .suggestion = types that don't start with an identifier need to be surrounded with angle brackets in qualified paths diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 7ce348619c6..81232f189e1 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -407,6 +407,32 @@ pub(crate) struct ExpectedExpressionFoundLet { pub span: Span, #[subdiagnostic] pub reason: ForbiddenLetReason, + #[subdiagnostic] + pub missing_let: Option, + #[subdiagnostic] + pub comparison: Option, +} + +#[derive(Subdiagnostic, Clone, Copy)] +#[multipart_suggestion( + parse_maybe_missing_let, + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct MaybeMissingLet { + #[suggestion_part(code = "let ")] + pub span: Span, +} + +#[derive(Subdiagnostic, Clone, Copy)] +#[multipart_suggestion( + parse_maybe_comparison, + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct MaybeComparison { + #[suggestion_part(code = "=")] + pub span: Span, } #[derive(Diagnostic)] 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(_) diff --git a/tests/ui/expr/if/bad-if-let-suggestion.rs b/tests/ui/expr/if/bad-if-let-suggestion.rs index 99d584ac7a5..b0d0676e1ea 100644 --- a/tests/ui/expr/if/bad-if-let-suggestion.rs +++ b/tests/ui/expr/if/bad-if-let-suggestion.rs @@ -1,6 +1,3 @@ -// FIXME(compiler-errors): This really should suggest `let` on the RHS of the -// `&&` operator, but that's kinda hard to do because of precedence. -// Instead, for now we just make sure not to suggest `if let let`. fn a() { if let x = 1 && i = 2 {} //~^ ERROR cannot find value `i` in this scope diff --git a/tests/ui/expr/if/bad-if-let-suggestion.stderr b/tests/ui/expr/if/bad-if-let-suggestion.stderr index 20ac9ca76ba..0d1f895bd82 100644 --- a/tests/ui/expr/if/bad-if-let-suggestion.stderr +++ b/tests/ui/expr/if/bad-if-let-suggestion.stderr @@ -1,19 +1,27 @@ error: expected expression, found `let` statement - --> $DIR/bad-if-let-suggestion.rs:5:8 + --> $DIR/bad-if-let-suggestion.rs:2: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 {} + | + error[E0425]: cannot find value `i` in this scope - --> $DIR/bad-if-let-suggestion.rs:5:21 + --> $DIR/bad-if-let-suggestion.rs:2:21 | LL | if let x = 1 && i = 2 {} | ^ not found in this scope error[E0425]: cannot find value `i` in this scope - --> $DIR/bad-if-let-suggestion.rs:12:9 + --> $DIR/bad-if-let-suggestion.rs:9:9 | LL | fn a() { | ------ similarly named function `a` defined here @@ -22,7 +30,7 @@ LL | if (i + j) = i {} | ^ help: a function with a similar name exists: `a` error[E0425]: cannot find value `j` in this scope - --> $DIR/bad-if-let-suggestion.rs:12:13 + --> $DIR/bad-if-let-suggestion.rs:9:13 | LL | fn a() { | ------ similarly named function `a` defined here @@ -31,7 +39,7 @@ LL | if (i + j) = i {} | ^ help: a function with a similar name exists: `a` error[E0425]: cannot find value `i` in this scope - --> $DIR/bad-if-let-suggestion.rs:12:18 + --> $DIR/bad-if-let-suggestion.rs:9:18 | LL | fn a() { | ------ similarly named function `a` defined here @@ -40,7 +48,7 @@ LL | if (i + j) = i {} | ^ help: a function with a similar name exists: `a` error[E0425]: cannot find value `x` in this scope - --> $DIR/bad-if-let-suggestion.rs:19:8 + --> $DIR/bad-if-let-suggestion.rs:16:8 | LL | fn a() { | ------ similarly named function `a` defined here @@ -49,7 +57,7 @@ LL | if x[0] = 1 {} | ^ help: a function with a similar name exists: `a` error[E0308]: mismatched types - --> $DIR/bad-if-let-suggestion.rs:5:8 + --> $DIR/bad-if-let-suggestion.rs:2:8 | LL | if let x = 1 && i = 2 {} | ^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr index 247fad2e992..f3726166eba 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr @@ -29,6 +29,10 @@ LL | if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some( | ^^^^^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions +help: you might have meant to compare for equality + | +LL | if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] == 1 { + | + error: expected expression, found `let` statement --> $DIR/invalid-let-in-a-valid-let-context.rs:24:23 @@ -53,6 +57,10 @@ LL | if let Some(elem) = _opt && [1, 2, 3][let _ = ()] = 1 { | ^^^^^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions +help: you might have meant to compare for equality + | +LL | if let Some(elem) = _opt && [1, 2, 3][let _ = ()] == 1 { + | + error: expected expression, found `let` statement --> $DIR/invalid-let-in-a-valid-let-context.rs:42:21 -- 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/errors.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/errors.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