diff options
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 636 |
1 files changed, 364 insertions, 272 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 1a57474bac2..1b99bc015b6 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -4,39 +4,39 @@ use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, - SemiColonMode, SeqSep, TokenExpectType, TokenType, TrailingToken, + SemiColonMode, SeqSep, TokenExpectType, TokenType, Trailing, TrailingToken, }; use crate::errors; use crate::maybe_recover_from_interpolated_ty_qpath; use ast::mut_visit::{noop_visit_expr, MutVisitor}; -use ast::{CoroutineKind, ForLoopKind, GenBlockKind, Pat, Path, PathSegment}; +use ast::token::IdentIsRaw; +use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment, Recovered}; use core::mem; +use core::ops::ControlFlow; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; -use rustc_ast::tokenstream::Spacing; use rustc_ast::util::case::Case; use rustc_ast::util::classify; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; -use rustc_ast::visit::Visitor; +use rustc_ast::visit::{walk_expr, Visitor}; use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, UnOp, DUMMY_NODE_ID}; use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind}; use rustc_ast::{Arm, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::{ - AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, PResult, StashKey, -}; +use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic}; use rustc_lexer::unescape::unescape_char; use rustc_macros::Subdiagnostic; use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded}; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; -use rustc_session::lint::BuiltinLintDiagnostics; +use rustc_session::lint::BuiltinLintDiag; 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}; +use tracing::instrument; /// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression /// dropped into the token stream, which happens while parsing the result of @@ -46,7 +46,7 @@ use thin_vec::{thin_vec, ThinVec}; macro_rules! maybe_whole_expr { ($p:expr) => { if let token::Interpolated(nt) = &$p.token.kind { - match &nt.0 { + match &**nt { token::NtExpr(e) | token::NtLiteral(e) => { let e = e.clone(); $p.bump(); @@ -128,13 +128,13 @@ impl<'a> Parser<'a> { match self.parse_expr_res(restrictions, None) { Ok(expr) => Ok(expr), Err(err) => match self.token.ident() { - Some((Ident { name: kw::Underscore, .. }, false)) + Some((Ident { name: kw::Underscore, .. }, IdentIsRaw::No)) 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), }, @@ -328,7 +328,9 @@ impl<'a> Parser<'a> { this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::NotYetParsed) })?; - let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); + self.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span); + let span = lhs_span.to(rhs.span); + lhs = match op { AssocOp::Add | AssocOp::Subtract @@ -404,8 +406,8 @@ impl<'a> Parser<'a> { // suggestions based on the assumption that double-refs are rarely intentional, // and closures are distinct enough that they don't get mixed up with their // return value. - let sp = self.sess.source_map().start_point(self.token.span); - self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span); + let sp = self.psess.source_map().start_point(self.token.span); + self.psess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span); false } (true, Some(op)) if !op.can_continue_expr_unambiguously() => false, @@ -427,11 +429,23 @@ impl<'a> Parser<'a> { }); } + fn error_ambiguous_outer_attrs(&self, lhs: &P<Expr>, lhs_span: Span, rhs_span: Span) { + if let Some(attr) = lhs.attrs.iter().find(|a| a.style == AttrStyle::Outer) { + self.dcx().emit_err(errors::AmbiguousOuterAttributes { + span: attr.span.to(rhs_span), + sugg: errors::WrapInParentheses::Expression { + left: attr.span.shrink_to_lo(), + right: lhs_span.shrink_to_hi(), + }, + }); + } + } + /// Possibly translate the current token to an associative operator. /// The method does not advance the current token. /// /// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively. - fn check_assoc_op(&self) -> Option<Spanned<AssocOp>> { + pub fn check_assoc_op(&self) -> Option<Spanned<AssocOp>> { let (op, span) = match (AssocOp::from_token(&self.token), self.token.ident()) { // When parsing const expressions, stop parsing when encountering `>`. ( @@ -459,7 +473,9 @@ impl<'a> Parser<'a> { return None; } (Some(op), _) => (op, self.token.span), - (None, Some((Ident { name: sym::and, span }, false))) if self.may_recover() => { + (None, Some((Ident { name: sym::and, span }, IdentIsRaw::No))) + if self.may_recover() => + { self.dcx().emit_err(errors::InvalidLogicalOperator { span: self.token.span, incorrect: "and".into(), @@ -467,7 +483,7 @@ impl<'a> Parser<'a> { }); (AssocOp::LAnd, span) } - (None, Some((Ident { name: sym::or, span }, false))) if self.may_recover() => { + (None, Some((Ident { name: sym::or, span }, IdentIsRaw::No))) if self.may_recover() => { self.dcx().emit_err(errors::InvalidLogicalOperator { span: self.token.span, incorrect: "or".into(), @@ -482,8 +498,7 @@ impl<'a> Parser<'a> { /// Checks if this expression is a successfully parsed statement. fn expr_is_complete(&self, e: &Expr) -> bool { - self.restrictions.contains(Restrictions::STMT_EXPR) - && !classify::expr_requires_semi_to_be_stmt(e) + self.restrictions.contains(Restrictions::STMT_EXPR) && classify::expr_is_complete(e) } /// Parses `x..y`, `x..=y`, and `x..`/`x..=`. @@ -505,7 +520,8 @@ impl<'a> Parser<'a> { None }; let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span); - let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span); + self.error_ambiguous_outer_attrs(&lhs, lhs.span, rhs_span); + let span = lhs.span.to(rhs_span); let limits = if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; let range = self.mk_range(Some(lhs), rhs, limits); @@ -607,7 +623,7 @@ impl<'a> Parser<'a> { }; // a block on the LHS might have been intended to be an expression instead - if let Some(sp) = this.sess.ambiguous_block_expr_parse.borrow().get(&lo) { + if let Some(sp) = this.psess.ambiguous_block_expr_parse.borrow().get(&lo) { err.add_parentheses = Some(ExprParenthesesNeeded::surrounding(*sp)); } else { err.remove_plus = Some(lo); @@ -665,9 +681,9 @@ impl<'a> Parser<'a> { fn parse_expr_box(&mut self, box_kw: Span) -> PResult<'a, (Span, ExprKind)> { 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 code = self.psess.source_map().span_to_snippet(inner_span).unwrap(); + 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 { @@ -699,7 +715,7 @@ impl<'a> Parser<'a> { // Span the `not` plus trailing whitespace to avoid // trailing whitespace after the `!` in our suggestion sub: sub_diag( - self.sess.source_map().span_until_non_whitespace(lo.to(negated_token.span)), + self.psess.source_map().span_until_non_whitespace(lo.to(negated_token.span)), ), }); @@ -709,7 +725,9 @@ impl<'a> Parser<'a> { /// Returns the span of expr if it was not interpolated, or the span of the interpolated token. fn interpolated_or_expr_span(&self, expr: &Expr) -> Span { match self.prev_token.kind { - TokenKind::Interpolated(..) => self.prev_token.span, + TokenKind::NtIdent(..) | TokenKind::NtLifetime(..) | TokenKind::Interpolated(..) => { + self.prev_token.span + } _ => expr.span, } } @@ -721,7 +739,8 @@ impl<'a> Parser<'a> { expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind, ) -> PResult<'a, P<Expr>> { let mk_expr = |this: &mut Self, lhs: P<Expr>, rhs: P<Ty>| { - this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, rhs.span), expr_kind(lhs, rhs)) + this.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span); + this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs)) }; // Save the state of the parser before parsing type normally, in case there is a @@ -744,7 +763,7 @@ impl<'a> Parser<'a> { ( // `foo: ` ExprKind::Path(None, ast::Path { segments, .. }), - token::Ident(kw::For | kw::Loop | kw::While, false), + token::Ident(kw::For | kw::Loop | kw::While, IdentIsRaw::No), ) if segments.len() == 1 => { let snapshot = self.create_snapshot_for_diagnostic(); let label = Label { @@ -859,13 +878,14 @@ impl<'a> Parser<'a> { ExprKind::MethodCall(_) => "a method call", ExprKind::Call(_, _) => "a function call", ExprKind::Await(_, _) => "`.await`", - ExprKind::Err => return Ok(with_postfix), + ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match", + ExprKind::Err(_) => return Ok(with_postfix), _ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"), } ); let mut err = self.dcx().struct_span_err(span, msg); - let suggest_parens = |err: &mut Diagnostic| { + let suggest_parens = |err: &mut Diag<'_>| { let suggestions = vec![ (span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), ")".to_string()), @@ -914,7 +934,7 @@ impl<'a> Parser<'a> { let found_raw = self.eat_keyword(kw::Raw); assert!(found_raw); let mutability = self.parse_const_or_mut().unwrap(); - self.sess.gated_spans.gate(sym::raw_ref_op, lo.to(self.prev_token.span)); + self.psess.gated_spans.gate(sym::raw_ref_op, lo.to(self.prev_token.span)); (ast::BorrowKind::Raw, mutability) } else { // `mut?` @@ -941,7 +961,10 @@ impl<'a> Parser<'a> { // Stitch the list of outer attributes onto the return value. // A little bit ugly, but the best way given the current code // structure - let res = self.parse_expr_dot_or_call_with_(e0, lo); + let res = ensure_sufficient_stack( + // this expr demonstrates the recursion it guards against + || self.parse_expr_dot_or_call_with_(e0, lo), + ); if attrs.is_empty() { res } else { @@ -957,22 +980,29 @@ impl<'a> Parser<'a> { fn parse_expr_dot_or_call_with_(&mut self, mut e: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> { loop { - let has_question = if self.prev_token.kind == TokenKind::Ident(kw::Return, false) { - // we are using noexpect here because we don't expect a `?` directly after a `return` - // which could be suggested otherwise - self.eat_noexpect(&token::Question) - } else { - self.eat(&token::Question) - }; + let has_question = + if self.prev_token.kind == TokenKind::Ident(kw::Return, IdentIsRaw::No) { + // we are using noexpect here because we don't expect a `?` directly after a `return` + // which could be suggested otherwise + self.eat_noexpect(&token::Question) + } else { + self.eat(&token::Question) + }; if has_question { // `expr?` e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e)); continue; } - let has_dot = if self.prev_token.kind == TokenKind::Ident(kw::Return, false) { + let has_dot = if self.prev_token.kind == TokenKind::Ident(kw::Return, IdentIsRaw::No) { // we are using noexpect here because we don't expect a `.` directly after a `return` // which could be suggested otherwise self.eat_noexpect(&token::Dot) + } else if self.token.kind == TokenKind::RArrow && self.may_recover() { + // Recovery for `expr->suffix`. + self.bump(); + let span = self.prev_token.span; + self.dcx().emit_err(errors::ExprRArrowCall { span }); + true } else { self.eat(&token::Dot) }; @@ -992,14 +1022,58 @@ impl<'a> Parser<'a> { } } - fn parse_dot_suffix_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> { + pub fn parse_dot_suffix_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> { + // At this point we've consumed something like `expr.` and `self.token` holds the token + // after the dot. match self.token.uninterpolate().kind { token::Ident(..) => self.parse_dot_suffix(base, lo), token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => { - Ok(self.parse_expr_tuple_field_access(lo, base, symbol, suffix, None)) + let ident_span = self.token.span; + self.bump(); + Ok(self.mk_expr_tuple_field_access(lo, ident_span, base, symbol, suffix)) } token::Literal(token::Lit { kind: token::Float, symbol, suffix }) => { - Ok(self.parse_expr_tuple_field_access_float(lo, base, symbol, suffix)) + Ok(match self.break_up_float(symbol, self.token.span) { + // 1e2 + DestructuredFloat::Single(sym, _sp) => { + // `foo.1e2`: a single complete dot access, fully consumed. We end up with + // the `1e2` token in `self.prev_token` and the following token in + // `self.token`. + let ident_span = self.token.span; + self.bump(); + self.mk_expr_tuple_field_access(lo, ident_span, base, sym, suffix) + } + // 1. + DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => { + // `foo.1.`: a single complete dot access and the start of another. + // We end up with the `sym` (`1`) token in `self.prev_token` and a dot in + // `self.token`. + assert!(suffix.is_none()); + self.token = Token::new(token::Ident(sym, IdentIsRaw::No), ident_span); + self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing)); + self.mk_expr_tuple_field_access(lo, ident_span, base, sym, None) + } + // 1.2 | 1.2e3 + DestructuredFloat::MiddleDot( + sym1, + ident1_span, + _dot_span, + sym2, + ident2_span, + ) => { + // `foo.1.2` (or `foo.1.2e3`): two complete dot accesses. We end up with + // the `sym2` (`2` or `2e3`) token in `self.prev_token` and the following + // token in `self.token`. + let next_token2 = + Token::new(token::Ident(sym2, IdentIsRaw::No), ident2_span); + self.bump_with((next_token2, self.token_spacing)); + self.bump(); + let base1 = + self.mk_expr_tuple_field_access(lo, ident1_span, base, sym1, None); + self.mk_expr_tuple_field_access(lo, ident2_span, base1, sym2, suffix) + } + DestructuredFloat::Error => base, + }) } _ => { self.error_unexpected_after_dot(); @@ -1011,7 +1085,7 @@ impl<'a> Parser<'a> { fn error_unexpected_after_dot(&self) { let actual = pprust::token_to_string(&self.token); let span = self.token.span; - let sm = self.sess.source_map(); + let sm = self.psess.source_map(); let (span, actual) = match (&self.token.kind, self.subparser_name) { (token::Eof, Some(_)) if let Ok(actual) = sm.span_to_snippet(sm.next_point(span)) => { (span.shrink_to_hi(), actual.into()) @@ -1113,41 +1187,6 @@ impl<'a> Parser<'a> { } } - fn parse_expr_tuple_field_access_float( - &mut self, - lo: Span, - base: P<Expr>, - float: Symbol, - suffix: Option<Symbol>, - ) -> P<Expr> { - match self.break_up_float(float, self.token.span) { - // 1e2 - DestructuredFloat::Single(sym, _sp) => { - self.parse_expr_tuple_field_access(lo, base, sym, suffix, None) - } - // 1. - DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => { - assert!(suffix.is_none()); - self.token = Token::new(token::Ident(sym, false), ident_span); - let next_token = (Token::new(token::Dot, dot_span), self.token_spacing); - self.parse_expr_tuple_field_access(lo, base, sym, None, Some(next_token)) - } - // 1.2 | 1.2e3 - DestructuredFloat::MiddleDot(symbol1, ident1_span, dot_span, symbol2, ident2_span) => { - self.token = Token::new(token::Ident(symbol1, false), ident1_span); - // This needs to be `Spacing::Alone` to prevent regressions. - // See issue #76399 and PR #76285 for more details - let next_token1 = (Token::new(token::Dot, dot_span), Spacing::Alone); - let base1 = - self.parse_expr_tuple_field_access(lo, base, symbol1, None, Some(next_token1)); - let next_token2 = Token::new(token::Ident(symbol2, false), ident2_span); - self.bump_with((next_token2, self.token_spacing)); // `.` - self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None) - } - DestructuredFloat::Error => base, - } - } - /// Parse the field access used in offset_of, matched by `$(e:expr)+`. /// Currently returns a list of idents. However, it should be possible in /// future to also do array indices, which might be arbitrary expressions. @@ -1249,24 +1288,18 @@ impl<'a> Parser<'a> { Ok(fields.into_iter().collect()) } - fn parse_expr_tuple_field_access( + fn mk_expr_tuple_field_access( &mut self, lo: Span, + ident_span: Span, base: P<Expr>, field: Symbol, suffix: Option<Symbol>, - next_token: Option<(Token, Spacing)>, ) -> P<Expr> { - match next_token { - Some(next_token) => self.bump_with(next_token), - None => self.bump(), - } - let span = self.prev_token.span; - let field = ExprKind::Field(base, Ident::new(field, span)); if let Some(suffix) = suffix { - self.expect_no_tuple_index_suffix(span, suffix); + self.expect_no_tuple_index_suffix(ident_span, suffix); } - self.mk_expr(lo.to(span), field) + self.mk_expr(lo.to(ident_span), ExprKind::Field(base, Ident::new(field, ident_span))) } /// Parse a function call expression, `expr(...)`. @@ -1313,7 +1346,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 @@ -1336,11 +1369,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) => { @@ -1373,6 +1406,13 @@ impl<'a> Parser<'a> { return Ok(self.mk_await_expr(self_arg, lo)); } + // Post-fix match + if self.eat_keyword(kw::Match) { + let match_span = self.prev_token.span; + self.psess.gated_spans.gate(sym::postfix_match, match_span); + return self.parse_match_block(lo, match_span, self_arg, MatchKind::Postfix); + } + let fn_span_lo = self.token.span; let mut seg = self.parse_path_segment(PathStyle::Expr, None)?; self.check_trailing_angle_brackets(&seg, &[&token::OpenDelim(Delimiter::Parenthesis)]); @@ -1432,8 +1472,8 @@ impl<'a> Parser<'a> { this.parse_expr_closure().map_err(|mut err| { // If the input is something like `if a { 1 } else { 2 } | if a { 3 } else { 4 }` // then suggest parens around the lhs. - if let Some(sp) = this.sess.ambiguous_block_expr_parse.borrow().get(&lo) { - err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); + if let Some(sp) = this.psess.ambiguous_block_expr_parse.borrow().get(&lo) { + err.subdiagnostic(this.dcx(), ExprParenthesesNeeded::surrounding(*sp)); } err }) @@ -1557,7 +1597,7 @@ impl<'a> Parser<'a> { return Ok(self.recover_seq_parse_error(Delimiter::Parenthesis, lo, err)); } }; - let kind = if es.len() == 1 && !trailing_comma { + let kind = if es.len() == 1 && matches!(trailing_comma, Trailing::No) { // `(e)` is parenthesized `e`. ExprKind::Paren(es.into_iter().next().unwrap()) } else { @@ -1632,7 +1672,7 @@ impl<'a> Parser<'a> { && let Some(expr) = self.maybe_parse_struct_expr(&qself, &path) { if qself.is_some() { - self.sess.gated_spans.gate(sym::more_qualified_paths, path.span); + self.psess.gated_spans.gate(sym::more_qualified_paths, path.span); } return expr; } else { @@ -1682,13 +1722,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, @@ -1701,19 +1741,20 @@ impl<'a> Parser<'a> { let span = expr.span; let found_labeled_breaks = { - struct FindLabeledBreaksVisitor(bool); + struct FindLabeledBreaksVisitor; impl<'ast> Visitor<'ast> for FindLabeledBreaksVisitor { - fn visit_expr_post(&mut self, ex: &'ast Expr) { + type Result = ControlFlow<()>; + fn visit_expr(&mut self, ex: &'ast Expr) -> ControlFlow<()> { if let ExprKind::Break(Some(_label), _) = ex.kind { - self.0 = true; + ControlFlow::Break(()) + } else { + walk_expr(self, ex) } } } - let mut vis = FindLabeledBreaksVisitor(false); - vis.visit_expr(&expr); - vis.0 + FindLabeledBreaksVisitor.visit_expr(&expr).is_break() }; // Suggestion involves adding a labeled block. @@ -1757,27 +1798,28 @@ impl<'a> Parser<'a> { &self, ident: Ident, mk_lit_char: impl FnOnce(Symbol, Span) -> L, - err: impl FnOnce(&Self) -> DiagnosticBuilder<'a>, + err: impl FnOnce(&Self) -> Diag<'a>, ) -> L { assert!(could_be_unclosed_char_literal(ident)); - if let Some(diag) = self.dcx().steal_diagnostic(ident.span, StashKey::LifetimeIsChar) { - diag.with_span_suggestion_verbose( - ident.span.shrink_to_hi(), - "add `'` to close the char literal", - "'", - Applicability::MaybeIncorrect, - ) - .emit(); - } else { - err(self) - .with_span_suggestion_verbose( + self.dcx() + .try_steal_modify_and_emit_err(ident.span, StashKey::LifetimeIsChar, |err| { + err.span_suggestion_verbose( ident.span.shrink_to_hi(), "add `'` to close the char literal", "'", Applicability::MaybeIncorrect, - ) - .emit(); - } + ); + }) + .unwrap_or_else(|| { + err(self) + .with_span_suggestion_verbose( + ident.span.shrink_to_hi(), + "add `'` to close the char literal", + "'", + Applicability::MaybeIncorrect, + ) + .emit() + }); let name = ident.without_first_quote().name; mk_lit_char(name, ident.span) } @@ -1818,7 +1860,7 @@ impl<'a> Parser<'a> { let kind = ExprKind::Yeet(self.parse_expr_opt()?); let span = lo.to(self.prev_token.span); - self.sess.gated_spans.gate(sym::yeet_expr, span); + self.psess.gated_spans.gate(sym::yeet_expr, span); let expr = self.mk_expr(span, kind); self.maybe_recover_from_bad_qpath(expr) } @@ -1828,7 +1870,7 @@ impl<'a> Parser<'a> { let lo = self.prev_token.span; let kind = ExprKind::Become(self.parse_expr()?); let span = lo.to(self.prev_token.span); - self.sess.gated_spans.gate(sym::explicit_tail_calls, span); + self.psess.gated_spans.gate(sym::explicit_tail_calls, span); let expr = self.mk_expr(span, kind); self.maybe_recover_from_bad_qpath(expr) } @@ -1872,12 +1914,11 @@ impl<'a> Parser<'a> { | ExprKind::Block(_, None) ) { - self.sess.buffer_lint_with_diagnostic( + self.psess.buffer_lint( BREAK_WITH_LABEL_AND_LOOP, lo.to(expr.span), ast::CRATE_NODE_ID, - "this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression", - BuiltinLintDiagnostics::BreakWithLabelAndLoop(expr.span), + BuiltinLintDiag::BreakWithLabelAndLoop(expr.span), ); } @@ -1923,7 +1964,7 @@ impl<'a> Parser<'a> { let lo = self.prev_token.span; let kind = ExprKind::Yield(self.parse_expr_opt()?); let span = lo.to(self.prev_token.span); - self.sess.gated_spans.gate(sym::yield_expr, span); + self.psess.gated_spans.gate(sym::yield_expr, span); let expr = self.mk_expr(span, kind); self.maybe_recover_from_bad_qpath(expr) } @@ -1931,11 +1972,11 @@ impl<'a> Parser<'a> { /// Parse `builtin # ident(args,*)`. fn parse_expr_builtin(&mut self) -> PResult<'a, P<Expr>> { self.parse_builtin(|this, lo, ident| { - if ident.name == sym::offset_of { - return Ok(Some(this.parse_expr_offset_of(lo)?)); - } - - Ok(None) + Ok(match ident.name { + sym::offset_of => Some(this.parse_expr_offset_of(lo)?), + sym::type_ascribe => Some(this.parse_expr_type_ascribe(lo)?), + _ => None, + }) }) } @@ -1948,11 +1989,11 @@ impl<'a> Parser<'a> { self.bump(); // `builtin` self.bump(); // `#` - let Some((ident, false)) = self.token.ident() else { + let Some((ident, IdentIsRaw::No)) = self.token.ident() else { let err = self.dcx().create_err(errors::ExpectedBuiltinIdent { span: self.token.span }); return Err(err); }; - self.sess.gated_spans.gate(sym::builtin_syntax, ident.span); + self.psess.gated_spans.gate(sym::builtin_syntax, ident.span); self.bump(); self.expect(&TokenKind::OpenDelim(Delimiter::Parenthesis))?; @@ -1970,6 +2011,7 @@ impl<'a> Parser<'a> { ret } + /// Built-in macro for `offset_of!` expressions. pub(crate) fn parse_expr_offset_of(&mut self, lo: Span) -> PResult<'a, P<Expr>> { let container = self.parse_ty()?; self.expect(&TokenKind::Comma)?; @@ -1999,6 +2041,15 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields))) } + /// Built-in macro for type ascription expressions. + pub(crate) fn parse_expr_type_ascribe(&mut self, lo: Span) -> PResult<'a, P<Expr>> { + let expr = self.parse_expr()?; + self.expect(&token::Comma)?; + let ty = self.parse_ty()?; + let span = lo.to(self.token.span); + Ok(self.mk_expr(span, ExprKind::Type(expr, ty))) + } + /// Returns a string literal if the next token is a string literal. /// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind, /// and returns `None` if the next token is not literal at all. @@ -2035,16 +2086,6 @@ impl<'a> Parser<'a> { &mut self, mk_lit_char: impl FnOnce(Symbol, Span) -> L, ) -> 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) - { - let mut err = self - .dcx() - .create_err(errors::InvalidInterpolatedExpression { span: self.token.span }); - err.downgrade_to_delayed_bug(); - return Err(err); - } let token = self.token.clone(); let err = |self_: &Self| { let msg = format!("unexpected token: {}", super::token_descr(&token)); @@ -2140,12 +2181,12 @@ impl<'a> Parser<'a> { Err(err) => { let span = token.uninterpolated_span(); self.bump(); - report_lit_error(self.sess, err, lit, span); + let guar = report_lit_error(self.psess, err, lit, span); // Pack possible quotes and prefixes from the original literal into // the error literal's symbol so they can be pretty-printed faithfully. let suffixless_lit = token::Lit::new(lit.kind, lit.symbol, None); let symbol = Symbol::intern(&suffixless_lit.to_string()); - let lit = token::Lit::new(token::Err, symbol, lit.suffix); + let lit = token::Lit::new(token::Err(guar), symbol, lit.suffix); Some( MetaItemLit::from_token_lit(lit, span) .unwrap_or_else(|_| unreachable!()), @@ -2205,7 +2246,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, @@ -2214,7 +2255,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(); @@ -2233,7 +2274,7 @@ impl<'a> Parser<'a> { } if self.token.kind == token::Comma { - if !self.sess.source_map().is_multiline(prev_span.until(self.token.span)) { + if !self.psess.source_map().is_multiline(prev_span.until(self.token.span)) { return Ok(()); } let mut snapshot = self.create_snapshot_for_diagnostic(); @@ -2309,7 +2350,7 @@ impl<'a> Parser<'a> { let lifetime_defs = self.parse_late_bound_lifetime_defs()?; let span = lo.to(self.prev_token.span); - self.sess.gated_spans.gate(sym::closure_lifetime_binder, span); + self.psess.gated_spans.gate(sym::closure_lifetime_binder, span); ClosureBinder::For { span, generic_params: lifetime_defs } } else { @@ -2351,12 +2392,12 @@ impl<'a> Parser<'a> { match coroutine_kind { Some(CoroutineKind::Async { span, .. }) => { // Feature-gate `async ||` closures. - self.sess.gated_spans.gate(sym::async_closure, span); + self.psess.gated_spans.gate(sym::async_closure, span); } Some(CoroutineKind::Gen { span, .. }) | Some(CoroutineKind::AsyncGen { span, .. }) => { // Feature-gate `gen ||` and `async gen ||` closures. // FIXME(gen_blocks): This perhaps should be a different gate. - self.sess.gated_spans.gate(sym::gen_blocks, span); + self.psess.gated_spans.gate(sym::gen_blocks, span); } None => {} } @@ -2368,7 +2409,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; @@ -2483,7 +2527,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( @@ -2491,14 +2535,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), + block_span: self.psess.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; @@ -2518,14 +2562,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. @@ -2586,9 +2630,9 @@ impl<'a> Parser<'a> { CondChecker::new(self).visit_expr(&mut cond); - if let ExprKind::Let(_, _, _, None) = cond.kind { + if let ExprKind::Let(_, _, _, Recovered::No) = cond.kind { // Remove the last feature gating of a `let` expression since it's stable. - self.sess.gated_spans.ungate_last(sym::let_chains, cond.span); + self.psess.gated_spans.ungate_last(sym::let_chains, cond.span); } Ok(cond) @@ -2596,7 +2640,7 @@ impl<'a> Parser<'a> { /// Parses a `let $pat = $expr` pseudo-expression. fn parse_expr_let(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> { - let is_recovered = if !restrictions.contains(Restrictions::ALLOW_LET) { + let recovered = if !restrictions.contains(Restrictions::ALLOW_LET) { let err = errors::ExpectedExpressionFoundLet { span: self.token.span, reason: ForbiddenLetReason::OtherForbidden, @@ -2607,10 +2651,10 @@ impl<'a> Parser<'a> { // This was part of a closure, the that part of the parser recover. return Err(self.dcx().create_err(err)); } else { - Some(self.dcx().emit_err(err)) + Recovered::Yes(self.dcx().emit_err(err)) } } else { - None + Recovered::No }; self.bump(); // Eat `let` token let lo = self.prev_token.span; @@ -2631,7 +2675,7 @@ impl<'a> Parser<'a> { } let expr = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), None.into())?; let span = lo.to(expr.span); - Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, is_recovered))) + Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered))) } /// Parses an `else { ... }` expression (`else` token already eaten). @@ -2648,8 +2692,33 @@ impl<'a> Parser<'a> { let first_tok_span = self.token.span; match self.parse_expr() { Ok(cond) - // If it's not a free-standing expression, and is followed by a block, - // then it's very likely the condition to an `else if`. + // Try to guess the difference between a "condition-like" vs + // "statement-like" expression. + // + // We are seeing the following code, in which $cond is neither + // ExprKind::Block nor ExprKind::If (the 2 cases wherein this + // would be valid syntax). + // + // if ... { + // } else $cond + // + // If $cond is "condition-like" such as ExprKind::Binary, we + // want to suggest inserting `if`. + // + // if ... { + // } else if a == b { + // ^^ + // } + // + // If $cond is "statement-like" such as ExprKind::While then we + // want to suggest wrapping in braces. + // + // if ... { + // } else { + // ^ + // while true {} + // } + // ^ if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) && classify::expr_requires_semi_to_be_stmt(&cond) => { @@ -2683,23 +2752,20 @@ impl<'a> Parser<'a> { branch_span: Span, attrs: AttrWrapper, ) { - if attrs.is_empty() { - return; + if !attrs.is_empty() + && let [x0 @ xn] | [x0, .., xn] = &*attrs.take_for_recovery(self.psess) + { + let attributes = x0.span.to(xn.span); + let last = xn.span; + let ctx = if is_ctx_else { "else" } else { "if" }; + self.dcx().emit_err(errors::OuterAttributeNotAllowedOnIfElse { + last, + branch_span, + ctx_span, + ctx: ctx.to_string(), + attributes, + }); } - - let attrs: &[ast::Attribute] = &attrs.take_for_recovery(self.sess); - let (attributes, last) = match attrs { - [] => return, - [x0 @ xn] | [x0, .., xn] => (x0.span.to(xn.span), xn.span), - }; - let ctx = if is_ctx_else { "else" } else { "if" }; - self.dcx().emit_err(errors::OuterAttributeNotAllowedOnIfElse { - last, - branch_span, - ctx_span, - ctx: ctx.to_string(), - attributes, - }); } fn error_on_extra_if(&mut self, cond: &P<Expr>) -> PResult<'a, ()> { @@ -2751,7 +2817,7 @@ impl<'a> Parser<'a> { }; return if self.token.kind == token::CloseDelim(Delimiter::Parenthesis) { // We know for sure we have seen `for ($SOMETHING in $EXPR)`, so we recover the - // parser state and emit a targetted suggestion. + // parser state and emit a targeted suggestion. let span = vec![start_span, self.token.span]; let right = self.prev_token.span.between(self.look_ahead(1, |t| t.span)); self.bump(); // ) @@ -2784,7 +2850,7 @@ impl<'a> Parser<'a> { self.token.uninterpolated_span().at_least_rust_2018() && self.eat_keyword(kw::Await); if is_await { - self.sess.gated_spans.gate(sym::async_for_loop, self.prev_token.span); + self.psess.gated_spans.gate(sym::async_for_loop, self.prev_token.span); } let kind = if is_await { ForLoopKind::ForAwait } else { ForLoopKind::For }; @@ -2795,9 +2861,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), @@ -2885,8 +2952,20 @@ impl<'a> Parser<'a> { /// Parses a `match ... { ... }` expression (`match` token already eaten). fn parse_expr_match(&mut self) -> PResult<'a, P<Expr>> { let match_span = self.prev_token.span; - let lo = self.prev_token.span; let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; + + self.parse_match_block(match_span, match_span, scrutinee, MatchKind::Prefix) + } + + /// Parses the block of a `match expr { ... }` or a `expr.match { ... }` + /// expression. This is after the match token and scrutinee are eaten + fn parse_match_block( + &mut self, + lo: Span, + match_span: Span, + scrutinee: P<Expr>, + match_kind: MatchKind, + ) -> PResult<'a, P<Expr>> { if let Err(mut e) = self.expect(&token::OpenDelim(Delimiter::Brace)) { if self.token == token::Semi { e.span_suggestion_short( @@ -2922,14 +3001,14 @@ 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, }); return Ok(self.mk_expr_with_attrs( span, - ExprKind::Match(scrutinee, arms), + ExprKind::Match(scrutinee, arms, match_kind), attrs, )); } @@ -2937,7 +3016,7 @@ impl<'a> Parser<'a> { } let hi = self.token.span; self.bump(); - Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs)) + Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms, match_kind), attrs)) } /// Attempt to recover from match arm body with statements and no surrounding braces. @@ -2945,7 +3024,7 @@ impl<'a> Parser<'a> { &mut self, first_expr: &P<Expr>, arrow_span: Span, - ) -> Option<P<Expr>> { + ) -> Option<(Span, ErrorGuaranteed)> { if self.token.kind != token::Semi { return None; } @@ -2957,7 +3036,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(), @@ -2970,7 +3049,7 @@ impl<'a> Parser<'a> { errors::MatchArmBodyWithoutBracesSugg::UseComma { semicolon: semi_sp } }, }); - this.mk_expr_err(span) + (span, guar) }; // We might have either a `,` -> `;` typo, or a block without braces. We need // a more subtle parsing strategy. @@ -3044,7 +3123,7 @@ impl<'a> Parser<'a> { |x| { // Don't gate twice if !pat.contains_never_pattern() { - this.sess.gated_spans.gate(sym::never_patterns, pat.span); + this.psess.gated_spans.gate(sym::never_patterns, pat.span); } x }, @@ -3083,23 +3162,26 @@ impl<'a> Parser<'a> { err })?; - let require_comma = classify::expr_requires_semi_to_be_stmt(&expr) + let require_comma = !classify::expr_is_complete(&expr) && this.token != token::CloseDelim(Delimiter::Brace); if !require_comma { arm_body = Some(expr); this.eat(&token::Comma); - Ok(false) - } else if let Some(body) = this.parse_arm_body_missing_braces(&expr, arrow_span) { + Ok(Recovered::No) + } else if let Some((span, guar)) = + this.parse_arm_body_missing_braces(&expr, arrow_span) + { + let body = this.mk_expr_err(span, guar); arm_body = Some(body); - Ok(true) + Ok(Recovered::Yes(guar)) } else { let expr_span = expr.span; arm_body = Some(expr); this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)]) .map_err(|mut err| { if this.token == token::FatArrow { - let sm = this.sess.source_map(); + let sm = this.psess.source_map(); if let Ok(expr_lines) = sm.span_to_lines(expr_span) && let Ok(arm_start_lines) = sm.span_to_lines(arm_start_span) && arm_start_lines.lines[0].end_col @@ -3170,10 +3252,10 @@ impl<'a> Parser<'a> { .is_ok(); if pattern_follows && snapshot.check(&TokenKind::FatArrow) { err.cancel(); - this.dcx().emit_err(errors::MissingCommaAfterMatchArm { + let guar = this.dcx().emit_err(errors::MissingCommaAfterMatchArm { span: arm_span.shrink_to_hi(), }); - return Ok(true); + return Ok(Recovered::Yes(guar)); } Err(err) }); @@ -3223,10 +3305,10 @@ impl<'a> Parser<'a> { 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); + self.psess.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); + self.psess.gated_spans.gate(sym::if_let_guard, span); } Ok(Some(cond)) } @@ -3317,7 +3399,7 @@ impl<'a> Parser<'a> { Err(self.dcx().create_err(errors::CatchAfterTry { span: self.prev_token.span })) } else { let span = span_lo.to(body.span); - self.sess.gated_spans.gate(sym::try_blocks, span); + self.psess.gated_spans.gate(sym::try_blocks, span); Ok(self.mk_expr_with_attrs(span, ExprKind::TryBlock(body), attrs)) } } @@ -3355,7 +3437,7 @@ impl<'a> Parser<'a> { // `async` blocks are stable } GenBlockKind::Gen | GenBlockKind::AsyncGen => { - self.sess.gated_spans.gate(sym::gen_blocks, lo.to(self.prev_token.span)); + self.psess.gated_spans.gate(sym::gen_blocks, lo.to(self.prev_token.span)); } } let capture_clause = self.parse_capture_clause()?; @@ -3431,16 +3513,22 @@ 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 Diagnostic, span: Span| { - recover_async = true; - errors::AsyncBlockIn2015 { span }.add_to_diagnostic(e); - errors::HelpUseLatestEdition::new().add_to_diagnostic(e); + let async_block_err = |e: &mut Diag<'_>, span: Span| { + errors::AsyncBlockIn2015 { span }.add_to_diag(e); + errors::HelpUseLatestEdition::new().add_to_diag(e); }; while self.token != token::CloseDelim(close_delim) { @@ -3463,9 +3551,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); @@ -3497,7 +3610,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 @@ -3509,18 +3625,19 @@ 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 Ok(f) = parsed_field.or_else(|guar| field_ident(self, guar).ok_or(guar)) + { // Only include the field if there's no parse error for the field name. fields.push(f); } @@ -3530,8 +3647,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", @@ -3543,13 +3659,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 '{'. @@ -3560,39 +3681,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 (is_raw || !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; @@ -3634,7 +3734,7 @@ impl<'a> Parser<'a> { /// Parses `ident (COLON expr)?`. fn parse_expr_field(&mut self) -> PResult<'a, ExprField> { let attrs = self.parse_outer_attributes()?; - self.recover_diff_marker(); + self.recover_vcs_conflict_marker(); self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; @@ -3716,8 +3816,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) } @@ -3754,18 +3854,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) - } - - /// Create expression span ensuring the span of the parent node - /// is larger than the span of lhs and rhs, including the attributes. - fn mk_expr_sp(&self, lhs: &P<Expr>, lhs_span: Span, rhs_span: Span) -> Span { - lhs.attrs - .iter() - .find(|a| a.style == AttrStyle::Outer) - .map_or(lhs_span, |a| a.span) - .to(rhs_span) + pub(super) fn mk_expr_err(&self, span: Span, guar: ErrorGuaranteed) -> P<Expr> { + self.mk_expr(span, ExprKind::Err(guar)) } fn collect_tokens_for_expr( @@ -3843,17 +3933,18 @@ impl MutVisitor for CondChecker<'_> { let span = e.span; match e.kind { - ExprKind::Let(_, _, _, ref mut is_recovered @ None) => { + ExprKind::Let(_, _, _, ref mut recovered @ Recovered::No) => { if let Some(reason) = self.forbid_let_reason { - *is_recovered = - Some(self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet { + *recovered = Recovered::Yes(self.parser.dcx().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); + self.parser.psess.gated_spans.gate(sym::let_chains, span); } } ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, _, _) => { @@ -3919,7 +4010,7 @@ impl MutVisitor for CondChecker<'_> { self.visit_expr(op); self.forbid_let_reason = forbid_let_reason; } - ExprKind::Let(_, _, _, Some(_)) + ExprKind::Let(_, _, _, Recovered::Yes(_)) | ExprKind::Array(_) | ExprKind::ConstBlock(_) | ExprKind::Lit(_) @@ -3927,7 +4018,7 @@ impl MutVisitor for CondChecker<'_> { | ExprKind::While(_, _, _) | ExprKind::ForLoop { .. } | ExprKind::Loop(_, _, _) - | ExprKind::Match(_, _) + | ExprKind::Match(_, _, _) | ExprKind::Closure(_) | ExprKind::Block(_, _) | ExprKind::Gen(_, _, _) @@ -3947,7 +4038,8 @@ 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. } } |
