diff options
| author | Lieselotte <52315535+she3py@users.noreply.github.com> | 2024-02-25 22:22:11 +0100 |
|---|---|---|
| committer | Lieselotte <52315535+she3py@users.noreply.github.com> | 2024-02-25 22:24:31 +0100 |
| commit | c440a5b814005c85ec903f9b9e44e25bf5c9c565 (patch) | |
| tree | b1d4d9004e25f842949a6348a4e4269dd2a840c9 /compiler/rustc_parse/src/parser | |
| parent | a3fce72a27ee41077c3752851ff778f886f0a4fa (diff) | |
| download | rust-c440a5b814005c85ec903f9b9e44e25bf5c9c565.tar.gz rust-c440a5b814005c85ec903f9b9e44e25bf5c9c565.zip | |
Add `ErrorGuaranteed` to `ast::ExprKind::Err`
Diffstat (limited to 'compiler/rustc_parse/src/parser')
| -rw-r--r-- | compiler/rustc_parse/src/parser/diagnostics.rs | 90 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 165 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/item.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/pat.rs | 18 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/stmt.rs | 64 |
5 files changed, 191 insertions, 150 deletions
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 517e3d82787..cc1f7c8ac7d 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -744,7 +744,8 @@ impl<'a> Parser<'a> { Err(err) } - pub(super) fn attr_on_non_tail_expr(&self, expr: &Expr) { + /// The user has written `#[attr] expr` which is unsupported. (#106020) + pub(super) fn attr_on_non_tail_expr(&self, expr: &Expr) -> ErrorGuaranteed { // Missing semicolon typo error. let span = self.prev_token.span.shrink_to_hi(); let mut err = self.dcx().create_err(ExpectedSemi { @@ -787,6 +788,8 @@ impl<'a> Parser<'a> { ], Applicability::MachineApplicable, ); + + // Special handling for `#[cfg(...)]` chains let mut snapshot = self.create_snapshot_for_diagnostic(); if let [attr] = &expr.attrs[..] && let ast::AttrKind::Normal(attr_kind) = &attr.kind @@ -799,7 +802,7 @@ impl<'a> Parser<'a> { Err(inner_err) => { err.cancel(); inner_err.cancel(); - return; + return self.dcx().span_delayed_bug(expr.span, "not a tail expression"); } } && let ast::AttrKind::Normal(next_attr_kind) = next_attr.kind @@ -812,7 +815,7 @@ impl<'a> Parser<'a> { Err(inner_err) => { err.cancel(); inner_err.cancel(); - return; + return self.dcx().span_delayed_bug(expr.span, "not a tail expression"); } }; // We have for sure @@ -845,7 +848,7 @@ impl<'a> Parser<'a> { ); } } - err.emit(); + err.emit() } fn check_too_many_raw_str_terminators(&mut self, err: &mut DiagnosticBuilder<'_>) -> bool { @@ -921,10 +924,10 @@ impl<'a> Parser<'a> { // fn foo() -> Foo { Path { // field: value, // } } - err.delay_as_bug(); + let guar = err.delay_as_bug(); self.restore_snapshot(snapshot); let mut tail = self.mk_block( - thin_vec![self.mk_stmt_err(expr.span)], + thin_vec![self.mk_stmt_err(expr.span, guar)], s, lo.to(self.prev_token.span), ); @@ -990,7 +993,7 @@ impl<'a> Parser<'a> { decl_hi: Span, ) -> PResult<'a, P<Expr>> { err.span_label(lo.to(decl_hi), "while parsing the body of this closure"); - match before.kind { + let guar = match before.kind { token::OpenDelim(Delimiter::Brace) if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => { @@ -1004,8 +1007,9 @@ impl<'a> Parser<'a> { ], Applicability::MaybeIncorrect, ); - err.emit(); + let guar = err.emit(); self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Brace)]); + guar } token::OpenDelim(Delimiter::Parenthesis) if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => @@ -1022,7 +1026,7 @@ impl<'a> Parser<'a> { ], Applicability::MaybeIncorrect, ); - err.emit(); + err.emit() } _ if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => { // We don't have a heuristic to correctly identify where the block @@ -1035,8 +1039,8 @@ impl<'a> Parser<'a> { return Err(err); } _ => return Err(err), - } - Ok(self.mk_expr_err(lo.to(self.token.span))) + }; + Ok(self.mk_expr_err(lo.to(self.token.span), guar)) } /// Eats and discards tokens until one of `kets` is encountered. Respects token trees, @@ -1214,7 +1218,7 @@ impl<'a> Parser<'a> { &mut self, mut e: DiagnosticBuilder<'a>, expr: &mut P<Expr>, - ) -> PResult<'a, ()> { + ) -> PResult<'a, ErrorGuaranteed> { if let ExprKind::Binary(binop, _, _) = &expr.kind && let ast::BinOpKind::Lt = binop.node && self.eat(&token::Comma) @@ -1239,9 +1243,9 @@ impl<'a> Parser<'a> { // The subsequent expression is valid. Mark // `expr` as erroneous and emit `e` now, but // return `Ok` so parsing can continue. - e.emit(); - *expr = self.mk_expr_err(expr.span.to(self.prev_token.span)); - return Ok(()); + let guar = e.emit(); + *expr = self.mk_expr_err(expr.span.to(self.prev_token.span), guar); + return Ok(guar); } Err(err) => { err.cancel(); @@ -1393,7 +1397,8 @@ impl<'a> Parser<'a> { outer_op.node, ); - let mk_err_expr = |this: &Self, span| Ok(Some(this.mk_expr(span, ExprKind::Err))); + let mk_err_expr = + |this: &Self, span, guar| Ok(Some(this.mk_expr(span, ExprKind::Err(guar)))); match &inner_op.kind { ExprKind::Binary(op, l1, r1) if op.node.is_comparison() => { @@ -1443,11 +1448,11 @@ impl<'a> Parser<'a> { match self.parse_expr() { Ok(_) => { // 99% certain that the suggestion is correct, continue parsing. - self.dcx().emit_err(err); + let guar = self.dcx().emit_err(err); // FIXME: actually check that the two expressions in the binop are // paths and resynthesize new fn call expression instead of using // `ExprKind::Err` placeholder. - mk_err_expr(self, inner_op.span.to(self.prev_token.span)) + mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar) } Err(expr_err) => { expr_err.cancel(); @@ -1471,11 +1476,11 @@ impl<'a> Parser<'a> { match self.consume_fn_args() { Err(()) => Err(self.dcx().create_err(err)), Ok(()) => { - self.dcx().emit_err(err); + let guar = self.dcx().emit_err(err); // FIXME: actually check that the two expressions in the binop are // paths and resynthesize new fn call expression instead of using // `ExprKind::Err` placeholder. - mk_err_expr(self, inner_op.span.to(self.prev_token.span)) + mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar) } } } else { @@ -1492,8 +1497,8 @@ impl<'a> Parser<'a> { let recovered = self .attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op); if matches!(recovered, Recovered::Yes) { - self.dcx().emit_err(err); - mk_err_expr(self, inner_op.span.to(self.prev_token.span)) + let guar = self.dcx().emit_err(err); + mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar) } else { // These cases cause too many knock-down errors, bail out (#61329). Err(self.dcx().create_err(err)) @@ -1502,9 +1507,9 @@ impl<'a> Parser<'a> { } let recover = self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op); - self.dcx().emit_err(err); + let guar = self.dcx().emit_err(err); if matches!(recover, Recovered::Yes) { - return mk_err_expr(self, inner_op.span.to(self.prev_token.span)); + return mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar); } } _ => {} @@ -1925,8 +1930,8 @@ impl<'a> Parser<'a> { } else { self.recover_await_prefix(await_sp)? }; - let sp = self.error_on_incorrect_await(lo, hi, &expr, is_question); - let expr = self.mk_expr(lo.to(sp), ExprKind::Err); + let (sp, guar) = self.error_on_incorrect_await(lo, hi, &expr, is_question); + let expr = self.mk_expr_err(lo.to(sp), guar); self.maybe_recover_from_bad_qpath(expr) } @@ -1955,21 +1960,27 @@ impl<'a> Parser<'a> { Ok((expr.span, expr, is_question)) } - fn error_on_incorrect_await(&self, lo: Span, hi: Span, expr: &Expr, is_question: bool) -> Span { + fn error_on_incorrect_await( + &self, + lo: Span, + hi: Span, + expr: &Expr, + is_question: bool, + ) -> (Span, ErrorGuaranteed) { let span = lo.to(hi); let applicability = match expr.kind { ExprKind::Try(_) => Applicability::MaybeIncorrect, // `await <expr>?` _ => Applicability::MachineApplicable, }; - self.dcx().emit_err(IncorrectAwait { + let guar = self.dcx().emit_err(IncorrectAwait { span, sugg_span: (span, applicability), expr: self.span_to_snippet(expr.span).unwrap_or_else(|_| pprust::expr_to_string(expr)), question_mark: if is_question { "?" } else { "" }, }); - span + (span, guar) } /// If encountering `future.await()`, consumes and emits an error. @@ -2013,8 +2024,8 @@ impl<'a> Parser<'a> { ); } err.span_suggestion(lo.shrink_to_lo(), format!("{prefix}you can still access the deprecated `try!()` macro using the \"raw identifier\" syntax"), "r#", Applicability::MachineApplicable); - err.emit(); - Ok(self.mk_expr_err(lo.to(hi))) + let guar = err.emit(); + Ok(self.mk_expr_err(lo.to(hi), guar)) } else { Err(self.expected_expression_found()) // The user isn't trying to invoke the try! macro } @@ -2059,10 +2070,10 @@ impl<'a> Parser<'a> { lo: Span, err: PErr<'a>, ) -> P<Expr> { - err.emit(); + let guar = err.emit(); // Recover from parse error, callers expect the closing delim to be consumed. self.consume_block(delim, ConsumeClosingDelim::Yes); - self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err) + self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err(guar)) } /// Eats tokens until we can be relatively sure we reached the end of the @@ -2549,9 +2560,10 @@ impl<'a> Parser<'a> { } _ => None, }; - self.dcx().emit_err(UnexpectedConstParamDeclaration { span: param.span(), sugg }); + let guar = + self.dcx().emit_err(UnexpectedConstParamDeclaration { span: param.span(), sugg }); - let value = self.mk_expr_err(param.span()); + let value = self.mk_expr_err(param.span(), guar); Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })) } @@ -2630,8 +2642,8 @@ impl<'a> Parser<'a> { "=", Applicability::MaybeIncorrect, ); - let value = self.mk_expr_err(start.to(expr.span)); - err.emit(); + let guar = err.emit(); + let value = self.mk_expr_err(start.to(expr.span), guar); return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })); } else if token::Colon == snapshot.token.kind && expr.span.lo() == snapshot.token.span.hi() @@ -2701,8 +2713,8 @@ impl<'a> Parser<'a> { vec![(span.shrink_to_lo(), "{ ".to_string()), (span.shrink_to_hi(), " }".to_string())], Applicability::MaybeIncorrect, ); - let value = self.mk_expr_err(span); - err.emit(); + let guar = err.emit(); + let value = self.mk_expr_err(span, guar); GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }) } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 89f28777bff..e1a5e17004f 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -34,7 +34,7 @@ use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::source_map::{self, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, Pos, Span}; +use rustc_span::{BytePos, ErrorGuaranteed, Pos, Span}; use thin_vec::{thin_vec, ThinVec}; /// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression @@ -131,9 +131,9 @@ impl<'a> Parser<'a> { if self.may_recover() && self.look_ahead(1, |t| t == &token::Comma) => { // Special-case handling of `foo(_, _, _)` - err.emit(); + let guar = err.emit(); self.bump(); - Ok(self.mk_expr(self.prev_token.span, ExprKind::Err)) + Ok(self.mk_expr(self.prev_token.span, ExprKind::Err(guar))) } _ => Err(err), }, @@ -667,8 +667,8 @@ impl<'a> Parser<'a> { let (span, _) = self.parse_expr_prefix_common(box_kw)?; let inner_span = span.with_lo(box_kw.hi()); let code = self.sess.source_map().span_to_snippet(inner_span).unwrap(); - self.dcx().emit_err(errors::BoxSyntaxRemoved { span: span, code: code.trim() }); - Ok((span, ExprKind::Err)) + let guar = self.dcx().emit_err(errors::BoxSyntaxRemoved { span: span, code: code.trim() }); + Ok((span, ExprKind::Err(guar))) } fn is_mistaken_not_ident_negation(&self) -> bool { @@ -860,7 +860,7 @@ impl<'a> Parser<'a> { ExprKind::MethodCall(_) => "a method call", ExprKind::Call(_, _) => "a function call", ExprKind::Await(_, _) => "`.await`", - ExprKind::Err => return Ok(with_postfix), + ExprKind::Err(_) => return Ok(with_postfix), _ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"), } ); @@ -1315,7 +1315,7 @@ impl<'a> Parser<'a> { let fields: Vec<_> = fields.into_iter().filter(|field| !field.is_shorthand).collect(); - if !fields.is_empty() && + let guar = if !fields.is_empty() && // `token.kind` should not be compared here. // This is because the `snapshot.token.kind` is treated as the same as // that of the open delim in `TokenTreesReader::parse_token_tree`, even @@ -1338,11 +1338,11 @@ impl<'a> Parser<'a> { .collect(), }, }) - .emit(); + .emit() } else { - err.emit(); - } - Ok(self.mk_expr_err(span)) + err.emit() + }; + Ok(self.mk_expr_err(span, guar)) } Ok(_) => Err(err), Err(err2) => { @@ -1684,13 +1684,13 @@ impl<'a> Parser<'a> { && (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt)) { // We're probably inside of a `Path<'a>` that needs a turbofish - self.dcx().emit_err(errors::UnexpectedTokenAfterLabel { + let guar = self.dcx().emit_err(errors::UnexpectedTokenAfterLabel { span: self.token.span, remove_label: None, enclose_in_block: None, }); consume_colon = false; - Ok(self.mk_expr_err(lo)) + Ok(self.mk_expr_err(lo, guar)) } else { let mut err = errors::UnexpectedTokenAfterLabel { span: self.token.span, @@ -2039,7 +2039,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, L> { if let token::Interpolated(nt) = &self.token.kind && let token::NtExpr(e) | token::NtLiteral(e) = &nt.0 - && matches!(e.kind, ExprKind::Err) + && matches!(e.kind, ExprKind::Err(_)) { let mut err = self .dcx() @@ -2207,7 +2207,7 @@ impl<'a> Parser<'a> { let mut snapshot = self.create_snapshot_for_diagnostic(); match snapshot.parse_expr_array_or_repeat(Delimiter::Brace) { Ok(arr) => { - self.dcx().emit_err(errors::ArrayBracketsInsteadOfSpaces { + let guar = self.dcx().emit_err(errors::ArrayBracketsInsteadOfSpaces { span: arr.span, sub: errors::ArrayBracketsInsteadOfSpacesSugg { left: lo, @@ -2216,7 +2216,7 @@ impl<'a> Parser<'a> { }); self.restore_snapshot(snapshot); - Some(self.mk_expr_err(arr.span)) + Some(self.mk_expr_err(arr.span, guar)) } Err(e) => { e.cancel(); @@ -2370,7 +2370,10 @@ impl<'a> Parser<'a> { // It is likely that the closure body is a block but where the // braces have been removed. We will recover and eat the next // statements later in the parsing process. - body = self.mk_expr_err(body.span); + body = self.mk_expr_err( + body.span, + self.dcx().span_delayed_bug(body.span, "recovered a closure body as a block"), + ); } let body_span = body.span; @@ -2485,7 +2488,7 @@ impl<'a> Parser<'a> { ExprKind::Binary(Spanned { span: binop_span, .. }, _, right) if let ExprKind::Block(_, None) = right.kind => { - this.dcx().emit_err(errors::IfExpressionMissingThenBlock { + let guar = this.dcx().emit_err(errors::IfExpressionMissingThenBlock { if_span: lo, missing_then_block_sub: errors::IfExpressionMissingThenBlockSub::UnfinishedCondition( @@ -2493,14 +2496,14 @@ impl<'a> Parser<'a> { ), let_else_sub: None, }); - std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi())) + std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi(), guar)) } ExprKind::Block(_, None) => { - this.dcx().emit_err(errors::IfExpressionMissingCondition { + let guar = this.dcx().emit_err(errors::IfExpressionMissingCondition { if_span: lo.with_neighbor(cond.span).shrink_to_hi(), block_span: self.sess.source_map().start_point(cond_span), }); - std::mem::replace(&mut cond, this.mk_expr_err(cond_span.shrink_to_hi())) + std::mem::replace(&mut cond, this.mk_expr_err(cond_span.shrink_to_hi(), guar)) } _ => { return None; @@ -2520,14 +2523,14 @@ impl<'a> Parser<'a> { let let_else_sub = matches!(cond.kind, ExprKind::Let(..)) .then(|| errors::IfExpressionLetSomeSub { if_span: lo.until(cond_span) }); - self.dcx().emit_err(errors::IfExpressionMissingThenBlock { + let guar = self.dcx().emit_err(errors::IfExpressionMissingThenBlock { if_span: lo, missing_then_block_sub: errors::IfExpressionMissingThenBlockSub::AddThenBlock( cond_span.shrink_to_hi(), ), let_else_sub, }); - self.mk_block_err(cond_span.shrink_to_hi()) + self.mk_block_err(cond_span.shrink_to_hi(), guar) } } else { let attrs = self.parse_outer_attributes()?; // For recovery. @@ -2797,9 +2800,10 @@ impl<'a> Parser<'a> { && !matches!(self.token.kind, token::OpenDelim(Delimiter::Brace)) && self.may_recover() { - self.dcx() + let guar = self + .dcx() .emit_err(errors::MissingExpressionInForLoop { span: expr.span.shrink_to_lo() }); - let err_expr = self.mk_expr(expr.span, ExprKind::Err); + let err_expr = self.mk_expr(expr.span, ExprKind::Err(guar)); let block = self.mk_block(thin_vec![], BlockCheckMode::Default, self.prev_token.span); return Ok(self.mk_expr( lo.to(self.prev_token.span), @@ -2924,7 +2928,7 @@ impl<'a> Parser<'a> { attrs: Default::default(), pat: self.mk_pat(span, ast::PatKind::Err(guar)), guard: None, - body: Some(self.mk_expr_err(span)), + body: Some(self.mk_expr_err(span, guar)), span, id: DUMMY_NODE_ID, is_placeholder: false, @@ -2959,7 +2963,7 @@ impl<'a> Parser<'a> { let err = |this: &Parser<'_>, stmts: Vec<ast::Stmt>| { let span = stmts[0].span.to(stmts[stmts.len() - 1].span); - this.dcx().emit_err(errors::MatchArmBodyWithoutBraces { + let guar = this.dcx().emit_err(errors::MatchArmBodyWithoutBraces { statements: span, arrow: arrow_span, num_statements: stmts.len(), @@ -2972,7 +2976,7 @@ impl<'a> Parser<'a> { errors::MatchArmBodyWithoutBracesSugg::UseComma { semicolon: semi_sp } }, }); - this.mk_expr_err(span) + this.mk_expr_err(span, guar) }; // We might have either a `,` -> `;` typo, or a block without braces. We need // a more subtle parsing strategy. @@ -3433,14 +3437,20 @@ impl<'a> Parser<'a> { pth: ast::Path, recover: bool, close_delim: Delimiter, - ) -> PResult<'a, (ThinVec<ExprField>, ast::StructRest, bool)> { + ) -> PResult< + 'a, + ( + ThinVec<ExprField>, + ast::StructRest, + Option<ErrorGuaranteed>, /* async blocks are forbidden in Rust 2015 */ + ), + > { let mut fields = ThinVec::new(); let mut base = ast::StructRest::None; - let mut recover_async = false; + let mut recovered_async = None; let in_if_guard = self.restrictions.contains(Restrictions::IN_IF_GUARD); - let mut async_block_err = |e: &mut DiagnosticBuilder<'_>, span: Span| { - recover_async = true; + let async_block_err = |e: &mut DiagnosticBuilder<'_>, span: Span| { errors::AsyncBlockIn2015 { span }.add_to_diagnostic(e); errors::HelpUseLatestEdition::new().add_to_diagnostic(e); }; @@ -3465,9 +3475,34 @@ impl<'a> Parser<'a> { break; } - let recovery_field = self.find_struct_error_after_field_looking_code(); + // Peek the field's ident before parsing its expr in order to emit better diagnostics. + let peek = self + .token + .ident() + .filter(|(ident, is_raw)| { + (!ident.is_reserved() || matches!(is_raw, IdentIsRaw::Yes)) + && self.look_ahead(1, |tok| *tok == token::Colon) + }) + .map(|(ident, _)| ident); + + // We still want a field even if its expr didn't parse. + let field_ident = |this: &Self, guar: ErrorGuaranteed| { + peek.map(|ident| { + let span = ident.span; + ExprField { + ident, + span, + expr: this.mk_expr_err(span, guar), + is_shorthand: false, + attrs: AttrVec::new(), + id: DUMMY_NODE_ID, + is_placeholder: false, + } + }) + }; + let parsed_field = match self.parse_expr_field() { - Ok(f) => Some(f), + Ok(f) => Ok(f), Err(mut e) => { if pth == kw::Async { async_block_err(&mut e, pth.span); @@ -3499,7 +3534,10 @@ impl<'a> Parser<'a> { return Err(e); } - e.emit(); + let guar = e.emit(); + if pth == kw::Async { + recovered_async = Some(guar); + } // If the next token is a comma, then try to parse // what comes next as additional fields, rather than @@ -3511,18 +3549,20 @@ impl<'a> Parser<'a> { } } - None + Err(guar) } }; - let is_shorthand = parsed_field.as_ref().is_some_and(|f| f.is_shorthand); + let is_shorthand = parsed_field.as_ref().is_ok_and(|f| f.is_shorthand); // A shorthand field can be turned into a full field with `:`. // We should point this out. self.check_or_expected(!is_shorthand, TokenType::Token(token::Colon)); match self.expect_one_of(&[token::Comma], &[token::CloseDelim(close_delim)]) { Ok(_) => { - if let Some(f) = parsed_field.or(recovery_field) { + if let Some(f) = + parsed_field.or_else(|guar| field_ident(self, guar).ok_or(guar)).ok() + { // Only include the field if there's no parse error for the field name. fields.push(f); } @@ -3532,8 +3572,7 @@ impl<'a> Parser<'a> { async_block_err(&mut e, pth.span); } else { e.span_label(pth.span, "while parsing this struct"); - if let Some(f) = recovery_field { - fields.push(f); + if peek.is_some() { e.span_suggestion( self.prev_token.span.shrink_to_hi(), "try adding a comma", @@ -3545,13 +3584,18 @@ impl<'a> Parser<'a> { if !recover { return Err(e); } - e.emit(); + let guar = e.emit(); + if pth == kw::Async { + recovered_async = Some(guar); + } else if let Some(f) = field_ident(self, guar) { + fields.push(f); + } self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); self.eat(&token::Comma); } } } - Ok((fields, base, recover_async)) + Ok((fields, base, recovered_async)) } /// Precondition: already parsed the '{'. @@ -3562,39 +3606,18 @@ impl<'a> Parser<'a> { recover: bool, ) -> PResult<'a, P<Expr>> { let lo = pth.span; - let (fields, base, recover_async) = + let (fields, base, recovered_async) = self.parse_struct_fields(pth.clone(), recover, Delimiter::Brace)?; let span = lo.to(self.token.span); self.expect(&token::CloseDelim(Delimiter::Brace))?; - let expr = if recover_async { - ExprKind::Err + let expr = if let Some(guar) = recovered_async { + ExprKind::Err(guar) } else { ExprKind::Struct(P(ast::StructExpr { qself, path: pth, fields, rest: base })) }; Ok(self.mk_expr(span, expr)) } - /// Use in case of error after field-looking code: `S { foo: () with a }`. - fn find_struct_error_after_field_looking_code(&self) -> Option<ExprField> { - match self.token.ident() { - Some((ident, is_raw)) - if (matches!(is_raw, IdentIsRaw::Yes) || !ident.is_reserved()) - && self.look_ahead(1, |t| *t == token::Colon) => - { - Some(ast::ExprField { - ident, - span: self.token.span, - expr: self.mk_expr_err(self.token.span), - is_shorthand: false, - attrs: AttrVec::new(), - id: DUMMY_NODE_ID, - is_placeholder: false, - }) - } - _ => None, - } - } - fn recover_struct_comma_after_dotdot(&mut self, span: Span) { if self.token != token::Comma { return; @@ -3718,8 +3741,8 @@ impl<'a> Parser<'a> { limits: RangeLimits, ) -> ExprKind { if end.is_none() && limits == RangeLimits::Closed { - self.inclusive_range_with_incorrect_end(); - ExprKind::Err + let guar = self.inclusive_range_with_incorrect_end(); + ExprKind::Err(guar) } else { ExprKind::Range(start, end, limits) } @@ -3756,8 +3779,8 @@ impl<'a> Parser<'a> { self.mk_expr_with_attrs(span, kind, AttrVec::new()) } - pub(super) fn mk_expr_err(&self, span: Span) -> P<Expr> { - self.mk_expr(span, ExprKind::Err) + pub(super) fn mk_expr_err(&self, span: Span, guar: ErrorGuaranteed) -> P<Expr> { + self.mk_expr(span, ExprKind::Err(guar)) } /// Create expression span ensuring the span of the parent node @@ -3949,7 +3972,7 @@ impl MutVisitor for CondChecker<'_> { | ExprKind::Become(_) | ExprKind::IncludedBytes(_) | ExprKind::FormatArgs(_) - | ExprKind::Err + | ExprKind::Err(_) | ExprKind::Dummy => { // These would forbid any let expressions they contain already. } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 2e049ca908f..a678194372e 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2328,11 +2328,11 @@ impl<'a> Parser<'a> { let _ = self.parse_expr()?; self.expect_semi()?; // `;` let span = eq_sp.to(self.prev_token.span); - self.dcx().emit_err(errors::FunctionBodyEqualsExpr { + let guar = self.dcx().emit_err(errors::FunctionBodyEqualsExpr { span, sugg: errors::FunctionBodyEqualsExprSugg { eq: eq_sp, semi: self.prev_token.span }, }); - (AttrVec::new(), Some(self.mk_block_err(span))) + (AttrVec::new(), Some(self.mk_block_err(span, guar))) } else { let expected = if req_body { &[token::OpenDelim(Delimiter::Brace)][..] diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index c82b44ac6e1..9bff5b93092 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -566,7 +566,7 @@ impl<'a> Parser<'a> { match self.parse_literal_maybe_minus() { Ok(begin) => { let begin = match self.maybe_recover_trailing_expr(begin.span, false) { - Some(_) => self.mk_expr_err(begin.span), + Some(guar) => self.mk_expr_err(begin.span, guar), None => begin, }; @@ -719,7 +719,7 @@ impl<'a> Parser<'a> { self.parse_pat_range_begin_with(begin.clone(), form) } // recover ranges with parentheses around the `(start)..` - PatKind::Err(_) + PatKind::Err(guar) if self.may_recover() && let Some(form) = self.parse_range_end() => { @@ -731,7 +731,7 @@ impl<'a> Parser<'a> { }, }); - self.parse_pat_range_begin_with(self.mk_expr(pat.span, ExprKind::Err), form) + self.parse_pat_range_begin_with(self.mk_expr_err(pat.span, *guar), form) } // (pat) with optional parentheses @@ -886,7 +886,7 @@ impl<'a> Parser<'a> { Ok(PatKind::Range(Some(begin), end, re)) } - pub(super) fn inclusive_range_with_incorrect_end(&mut self) { + pub(super) fn inclusive_range_with_incorrect_end(&mut self) -> ErrorGuaranteed { let tok = &self.token; let span = self.prev_token.span; // If the user typed "..==" instead of "..=", we want to give them @@ -905,15 +905,13 @@ impl<'a> Parser<'a> { let _ = self.parse_pat_range_end().map_err(|e| e.cancel()); } - self.dcx().emit_err(InclusiveRangeExtraEquals { span: span_with_eq }); + self.dcx().emit_err(InclusiveRangeExtraEquals { span: span_with_eq }) } token::Gt if no_space => { let after_pat = span.with_hi(span.hi() - rustc_span::BytePos(1)).shrink_to_hi(); - self.dcx().emit_err(InclusiveRangeMatchArrow { span, arrow: tok.span, after_pat }); - } - _ => { - self.dcx().emit_err(InclusiveRangeNoEnd { span }); + self.dcx().emit_err(InclusiveRangeMatchArrow { span, arrow: tok.span, after_pat }) } + _ => self.dcx().emit_err(InclusiveRangeNoEnd { span }), } } @@ -987,7 +985,7 @@ impl<'a> Parser<'a> { } Ok(match recovered { - Some(_) => self.mk_expr_err(bound.span), + Some(guar) => self.mk_expr_err(bound.span, guar), None => bound, }) } diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index ee02b69c614..15f8124823f 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -22,7 +22,7 @@ use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Stmt}; use rustc_ast::{StmtKind, DUMMY_NODE_ID}; use rustc_errors::{Applicability, DiagnosticBuilder, PResult}; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, ErrorGuaranteed, Span}; use std::borrow::Cow; use std::mem; @@ -610,9 +610,9 @@ impl<'a> Parser<'a> { } } - err.emit(); + let guar = err.emit(); self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore); - Some(self.mk_stmt_err(self.token.span)) + Some(self.mk_stmt_err(self.token.span, guar)) } Ok(stmt) => stmt, Err(err) => return Err(err), @@ -651,10 +651,10 @@ impl<'a> Parser<'a> { .contains(&self.token.kind) => { // The user has written `#[attr] expr` which is unsupported. (#106020) - self.attr_on_non_tail_expr(&expr); + let guar = self.attr_on_non_tail_expr(&expr); // We already emitted an error, so don't emit another type error let sp = expr.span.to(self.prev_token.span); - *expr = self.mk_expr_err(sp); + *expr = self.mk_expr_err(sp, guar); } // Expression without semicolon. @@ -666,10 +666,18 @@ impl<'a> Parser<'a> { let expect_result = self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]); + // Try to both emit a better diagnostic, and avoid further errors by replacing + // the `expr` with `ExprKind::Err`. let replace_with_err = 'break_recover: { match expect_result { - // Recover from parser, skip type error to avoid extra errors. - Ok(Recovered::Yes) => true, + Ok(Recovered::No) => None, + Ok(Recovered::Yes) => { + // Skip type error to avoid extra errors. + let guar = self + .dcx() + .span_delayed_bug(self.prev_token.span, "expected `;` or `}`"); + Some(guar) + } Err(e) => { if self.recover_colon_as_semi() { // recover_colon_as_semi has already emitted a nicer error. @@ -677,7 +685,7 @@ impl<'a> Parser<'a> { add_semi_to_stmt = true; eat_semi = false; - break 'break_recover false; + break 'break_recover None; } match &expr.kind { @@ -705,13 +713,13 @@ impl<'a> Parser<'a> { }; match self.parse_expr_labeled(label, false) { Ok(labeled_expr) => { - e.delay_as_bug(); + e.cancel(); self.dcx().emit_err(MalformedLoopLabel { span: label.ident.span, correct_label: label.ident, }); *expr = labeled_expr; - break 'break_recover false; + break 'break_recover None; } Err(err) => { err.cancel(); @@ -723,26 +731,26 @@ impl<'a> Parser<'a> { _ => {} } - if let Err(e) = - self.check_mistyped_turbofish_with_multiple_type_params(e, expr) - { - if recover.no() { - return Err(e); - } - e.emit(); - self.recover_stmt(); - } - - true + let res = + self.check_mistyped_turbofish_with_multiple_type_params(e, expr); + + Some(if recover.no() { + res? + } else { + res.unwrap_or_else(|e| { + let guar = e.emit(); + self.recover_stmt(); + guar + }) + }) } - Ok(Recovered::No) => false, } }; - if replace_with_err { + if let Some(guar) = replace_with_err { // We already emitted an error, so don't emit another type error let sp = expr.span.to(self.prev_token.span); - *expr = self.mk_expr_err(sp); + *expr = self.mk_expr_err(sp, guar); } } StmtKind::Expr(_) | StmtKind::MacCall(_) => {} @@ -791,11 +799,11 @@ impl<'a> Parser<'a> { Stmt { id: DUMMY_NODE_ID, kind, span } } - pub(super) fn mk_stmt_err(&self, span: Span) -> Stmt { - self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span))) + pub(super) fn mk_stmt_err(&self, span: Span, guar: ErrorGuaranteed) -> Stmt { + self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span, guar))) } - pub(super) fn mk_block_err(&self, span: Span) -> P<Block> { - self.mk_block(thin_vec![self.mk_stmt_err(span)], BlockCheckMode::Default, span) + pub(super) fn mk_block_err(&self, span: Span, guar: ErrorGuaranteed) -> P<Block> { + self.mk_block(thin_vec![self.mk_stmt_err(span, guar)], BlockCheckMode::Default, span) } } |
