diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 07ce901fb417a..610c5c36a3ce3 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -21,7 +21,7 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, PResult}; use rustc_feature::Features; use rustc_parse::parser::{ - AttemptLocalParseRecovery, ForceCollect, Parser, RecoverColon, RecoverComma, + AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma, }; use rustc_parse::validate_attr; use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; @@ -914,6 +914,7 @@ pub fn parse_ast_fragment<'a>( None, RecoverComma::No, RecoverColon::Yes, + CommaRecoveryMode::LikelyTuple, )?), AstFragmentKind::Crate => AstFragment::Crate(this.parse_crate_mod()?), AstFragmentKind::Arms diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 2b1b2f3fce496..90b84a485ac6f 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -3,6 +3,7 @@ #![feature(array_windows)] #![feature(crate_visibility_modifier)] #![feature(if_let_guard)] +#![feature(let_else)] #![feature(box_patterns)] #![recursion_limit = "256"] diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 9677e7642b88c..64eb2ff58e301 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1,8 +1,8 @@ use super::pat::Expected; use super::ty::AllowPlus; use super::{ - BlockMode, Parser, PathStyle, RecoverColon, RecoverComma, Restrictions, SemiColonMode, SeqSep, - TokenExpectType, TokenType, + BlockMode, CommaRecoveryMode, Parser, PathStyle, RecoverColon, RecoverComma, Restrictions, + SemiColonMode, SeqSep, TokenExpectType, TokenType, }; use rustc_ast as ast; @@ -2200,12 +2200,32 @@ impl<'a> Parser<'a> { first_pat } + crate fn maybe_recover_unexpected_block_label(&mut self) -> bool { + let Some(label) = self.eat_label().filter(|_| { + self.eat(&token::Colon) && self.token.kind == token::OpenDelim(token::Brace) + }) else { + return false; + }; + let span = label.ident.span.to(self.prev_token.span); + let mut err = self.struct_span_err(span, "block label not supported here"); + err.span_label(span, "not supported here"); + err.tool_only_span_suggestion( + label.ident.span.until(self.token.span), + "remove this block label", + String::new(), + Applicability::MachineApplicable, + ); + err.emit(); + true + } + /// Some special error handling for the "top-level" patterns in a match arm, /// `for` loop, `let`, &c. (in contrast to subpatterns within such). crate fn maybe_recover_unexpected_comma( &mut self, lo: Span, rc: RecoverComma, + rt: CommaRecoveryMode, ) -> PResult<'a, ()> { if rc == RecoverComma::No || self.token != token::Comma { return Ok(()); @@ -2225,20 +2245,25 @@ impl<'a> Parser<'a> { let seq_span = lo.to(self.prev_token.span); let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern"); if let Ok(seq_snippet) = self.span_to_snippet(seq_span) { - const MSG: &str = "try adding parentheses to match on a tuple..."; - - err.span_suggestion( - seq_span, - MSG, - format!("({})", seq_snippet), - Applicability::MachineApplicable, - ); - err.span_suggestion( - seq_span, - "...or a vertical bar to match on multiple alternatives", - seq_snippet.replace(',', " |"), + err.multipart_suggestion( + &format!( + "try adding parentheses to match on a tuple{}", + if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." }, + ), + vec![ + (seq_span.shrink_to_lo(), "(".to_string()), + (seq_span.shrink_to_hi(), ")".to_string()), + ], Applicability::MachineApplicable, ); + if let CommaRecoveryMode::EitherTupleOrPipe = rt { + err.span_suggestion( + seq_span, + "...or a vertical bar to match on multiple alternatives", + seq_snippet.replace(',', " |"), + Applicability::MachineApplicable, + ); + } } Err(err) } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f706a98a4fcfa..9b9285867eb85 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,4 +1,4 @@ -use super::pat::{RecoverColon, RecoverComma, PARAM_EXPECTED}; +use super::pat::{CommaRecoveryMode, RecoverColon, RecoverComma, PARAM_EXPECTED}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, TokenType, @@ -1269,18 +1269,27 @@ impl<'a> Parser<'a> { } else if let Some(label) = self.eat_label() { self.parse_labeled_expr(label, attrs, true) } else if self.eat_keyword(kw::Loop) { - self.parse_loop_expr(None, self.prev_token.span, attrs) + let sp = self.prev_token.span; + self.parse_loop_expr(None, self.prev_token.span, attrs).map_err(|mut err| { + err.span_label(sp, "while parsing this `loop` expression"); + err + }) } else if self.eat_keyword(kw::Continue) { let kind = ExprKind::Continue(self.eat_label()); Ok(self.mk_expr(lo.to(self.prev_token.span), kind, attrs)) } else if self.eat_keyword(kw::Match) { let match_sp = self.prev_token.span; self.parse_match_expr(attrs).map_err(|mut err| { - err.span_label(match_sp, "while parsing this match expression"); + err.span_label(match_sp, "while parsing this `match` expression"); err }) } else if self.eat_keyword(kw::Unsafe) { + let sp = self.prev_token.span; self.parse_block_expr(None, lo, BlockCheckMode::Unsafe(ast::UserProvided), attrs) + .map_err(|mut err| { + err.span_label(sp, "while parsing this `unsafe` expression"); + err + }) } else if self.check_inline_const(0) { self.parse_const_block(lo.to(self.token.span), false) } else if self.is_do_catch_block() { @@ -2110,7 +2119,12 @@ impl<'a> Parser<'a> { /// The `let` token has already been eaten. fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.prev_token.span; - let pat = self.parse_pat_allow_top_alt(None, RecoverComma::Yes, RecoverColon::Yes)?; + let pat = self.parse_pat_allow_top_alt( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::LikelyTuple, + )?; self.expect(&token::Eq)?; let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| { this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into()) @@ -2173,7 +2187,12 @@ impl<'a> Parser<'a> { _ => None, }; - let pat = self.parse_pat_allow_top_alt(None, RecoverComma::Yes, RecoverColon::Yes)?; + let pat = self.parse_pat_allow_top_alt( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::LikelyTuple, + )?; if !self.eat_keyword(kw::In) { self.error_missing_in_for_loop(); } @@ -2216,8 +2235,15 @@ impl<'a> Parser<'a> { lo: Span, mut attrs: AttrVec, ) -> PResult<'a, P> { - let cond = self.parse_cond_expr()?; - let (iattrs, body) = self.parse_inner_attrs_and_block()?; + let cond = self.parse_cond_expr().map_err(|mut err| { + err.span_label(lo, "while parsing the condition of this `while` expression"); + err + })?; + let (iattrs, body) = self.parse_inner_attrs_and_block().map_err(|mut err| { + err.span_label(lo, "while parsing the body of this `while` expression"); + err.span_label(cond.span, "this `while` condition successfully parsed"); + err + })?; attrs.extend(iattrs); Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::While(cond, body, opt_label), attrs)) } @@ -2234,7 +2260,7 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::Loop(body, opt_label), attrs)) } - fn eat_label(&mut self) -> Option