diff options
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 382 |
1 files changed, 242 insertions, 140 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 509cef9826b..0b24e784126 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::{CoroutineKind, GenBlockKind, Pat, Path, PathSegment}; +use ast::{CoroutineKind, ForLoopKind, GenBlockKind, Pat, Path, PathSegment}; use core::mem; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; @@ -26,8 +26,7 @@ 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, ErrorGuaranteed, IntoDiagnostic, - PResult, StashKey, + AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, PResult, StashKey, }; use rustc_macros::Subdiagnostic; use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded}; @@ -238,7 +237,7 @@ impl<'a> Parser<'a> { } .into(); let invalid = format!("{sugg}="); - self.sess.emit_err(errors::InvalidComparisonOperator { + self.dcx().emit_err(errors::InvalidComparisonOperator { span: sp, invalid: invalid.clone(), sub: errors::InvalidComparisonOperatorSub::Correctable { @@ -256,7 +255,7 @@ impl<'a> Parser<'a> { && self.prev_token.span.hi() == self.token.span.lo() { let sp = op.span.to(self.token.span); - self.sess.emit_err(errors::InvalidComparisonOperator { + self.dcx().emit_err(errors::InvalidComparisonOperator { span: sp, invalid: "<>".into(), sub: errors::InvalidComparisonOperatorSub::Correctable { @@ -274,7 +273,7 @@ impl<'a> Parser<'a> { && self.prev_token.span.hi() == self.token.span.lo() { let sp = op.span.to(self.token.span); - self.sess.emit_err(errors::InvalidComparisonOperator { + self.dcx().emit_err(errors::InvalidComparisonOperator { span: sp, invalid: "<=>".into(), sub: errors::InvalidComparisonOperatorSub::Spaceship(sp), @@ -371,7 +370,7 @@ impl<'a> Parser<'a> { self.mk_expr(span, aopexpr) } AssocOp::As | AssocOp::DotDot | AssocOp::DotDotEq => { - self.span_bug(span, "AssocOp should have been handled by special case") + self.dcx().span_bug(span, "AssocOp should have been handled by special case") } }; @@ -421,7 +420,7 @@ impl<'a> Parser<'a> { /// but the next token implies this should be parsed as an expression. /// For example: `if let Some(x) = x { x } else { 0 } / 2`. fn error_found_expr_would_be_stmt(&self, lhs: &Expr) { - self.sess.emit_err(errors::FoundExprWouldBeStmt { + self.dcx().emit_err(errors::FoundExprWouldBeStmt { span: self.token.span, token: self.token.clone(), suggestion: ExprParenthesesNeeded::surrounding(lhs.span), @@ -448,7 +447,7 @@ impl<'a> Parser<'a> { } (Some(op), _) => (op, self.token.span), (None, Some((Ident { name: sym::and, span }, false))) if self.may_recover() => { - self.sess.emit_err(errors::InvalidLogicalOperator { + self.dcx().emit_err(errors::InvalidLogicalOperator { span: self.token.span, incorrect: "and".into(), sub: errors::InvalidLogicalOperatorSub::Conjunction(self.token.span), @@ -456,7 +455,7 @@ impl<'a> Parser<'a> { (AssocOp::LAnd, span) } (None, Some((Ident { name: sym::or, span }, false))) if self.may_recover() => { - self.sess.emit_err(errors::InvalidLogicalOperator { + self.dcx().emit_err(errors::InvalidLogicalOperator { span: self.token.span, incorrect: "or".into(), sub: errors::InvalidLogicalOperatorSub::Disjunction(self.token.span), @@ -594,7 +593,7 @@ impl<'a> Parser<'a> { } else { err.remove_plus = Some(lo); } - this.sess.emit_err(err); + this.dcx().emit_err(err); this.bump(); this.parse_expr_prefix(None) @@ -637,7 +636,7 @@ impl<'a> Parser<'a> { /// Recover on `~expr` in favor of `!expr`. fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { - self.sess.emit_err(errors::TildeAsUnaryOperator(lo)); + self.dcx().emit_err(errors::TildeAsUnaryOperator(lo)); self.parse_expr_unary(lo, UnOp::Not) } @@ -647,7 +646,7 @@ impl<'a> Parser<'a> { fn parse_expr_box(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { let (span, expr) = self.parse_expr_prefix_common(lo)?; let code = self.sess.source_map().span_to_snippet(span.with_lo(lo.hi())).unwrap(); - self.sess.emit_err(errors::BoxSyntaxRemoved { span, code: code.trim() }); + self.dcx().emit_err(errors::BoxSyntaxRemoved { span, code: code.trim() }); // So typechecking works, parse `box <expr>` as `::std::boxed::Box::new(expr)` let path = Path { span, @@ -688,7 +687,7 @@ impl<'a> Parser<'a> { errors::NotAsNegationOperatorSub::SuggestNotDefault }; - self.sess.emit_err(errors::NotAsNegationOperator { + self.dcx().emit_err(errors::NotAsNegationOperator { negated: negated_token.span, negated_desc: super::token_descr(&negated_token), // Span the `not` plus trailing whitespace to avoid @@ -751,7 +750,7 @@ impl<'a> Parser<'a> { match self.parse_expr_labeled(label, false) { Ok(expr) => { type_err.cancel(); - self.sess.emit_err(errors::MalformedLoopLabel { + self.dcx().emit_err(errors::MalformedLoopLabel { span: label.ident.span, correct_label: label.ident, }); @@ -783,7 +782,7 @@ impl<'a> Parser<'a> { match self.token.kind { token::Lt => { - self.sess.emit_err(errors::ComparisonInterpretedAsGeneric { + self.dcx().emit_err(errors::ComparisonInterpretedAsGeneric { comparison: self.token.span, r#type: path, args: args_span, @@ -791,7 +790,7 @@ impl<'a> Parser<'a> { }) } token::BinOp(token::Shl) => { - self.sess.emit_err(errors::ShiftInterpretedAsGeneric { + self.dcx().emit_err(errors::ShiftInterpretedAsGeneric { shift: self.token.span, r#type: path, args: args_span, @@ -858,7 +857,7 @@ impl<'a> Parser<'a> { _ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"), } ); - let mut err = self.struct_span_err(span, msg); + let mut err = self.dcx().struct_span_err(span, msg); let suggest_parens = |err: &mut Diagnostic| { let suggestions = vec![ @@ -899,7 +898,7 @@ impl<'a> Parser<'a> { } fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) { - self.sess.emit_err(errors::LifetimeInBorrowExpression { span, lifetime_span: lt_span }); + self.dcx().emit_err(errors::LifetimeInBorrowExpression { span, lifetime_span: lt_span }); } /// Parse `mut?` or `raw [ const | mut ]`. @@ -1013,7 +1012,7 @@ impl<'a> Parser<'a> { } _ => (span, actual), }; - self.sess.emit_err(errors::UnexpectedTokenAfterDot { span, actual }); + self.dcx().emit_err(errors::UnexpectedTokenAfterDot { span, actual }); } // We need an identifier or integer, but the next token is a float. @@ -1024,7 +1023,7 @@ impl<'a> Parser<'a> { // we should break everything including floats into more basic proc-macro style // tokens in the lexer (probably preferable). // See also `TokenKind::break_two_token_op` which does similar splitting of `>>` into `>`. - fn break_up_float(&mut self, float: Symbol) -> DestructuredFloat { + fn break_up_float(&self, float: Symbol, span: Span) -> DestructuredFloat { #[derive(Debug)] enum FloatComponent { IdentLike(String), @@ -1054,7 +1053,6 @@ impl<'a> Parser<'a> { // With proc macros the span can refer to anything, the source may be too short, // or too long, or non-ASCII. It only makes sense to break our span into components // if its underlying text is identical to our float literal. - let span = self.token.span; let can_take_span_apart = || self.span_to_snippet(span).as_deref() == Ok(float_str).as_deref(); @@ -1116,7 +1114,7 @@ impl<'a> Parser<'a> { float: Symbol, suffix: Option<Symbol>, ) -> P<Expr> { - match self.break_up_float(float) { + 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) @@ -1144,40 +1142,105 @@ impl<'a> Parser<'a> { } } - fn parse_field_name_maybe_tuple(&mut self) -> PResult<'a, ThinVec<Ident>> { - let token::Literal(token::Lit { kind: token::Float, symbol, suffix }) = self.token.kind - else { - return Ok(thin_vec![self.parse_field_name()?]); - }; - Ok(match self.break_up_float(symbol) { - // 1e2 - DestructuredFloat::Single(sym, sp) => { - self.bump(); - thin_vec![Ident::new(sym, sp)] - } - // 1. - DestructuredFloat::TrailingDot(sym, sym_span, dot_span) => { - assert!(suffix.is_none()); - // Analogous to `Self::break_and_eat` - self.break_last_token = true; - // This might work, in cases like `1. 2`, and might not, - // in cases like `offset_of!(Ty, 1.)`. It depends on what comes - // after the float-like token, and therefore we have to make - // the other parts of the parser think that there is a dot literal. - self.token = Token::new(token::Ident(sym, false), sym_span); - self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing)); - thin_vec![Ident::new(sym, sym_span)] - } - // 1.2 | 1.2e3 - DestructuredFloat::MiddleDot(symbol1, ident1_span, _dot_span, symbol2, ident2_span) => { - self.bump(); - thin_vec![Ident::new(symbol1, ident1_span), Ident::new(symbol2, ident2_span)] + /// 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. + fn parse_floating_field_access(&mut self) -> PResult<'a, P<[Ident]>> { + let mut fields = Vec::new(); + let mut trailing_dot = None; + + loop { + // This is expected to use a metavariable $(args:expr)+, but the builtin syntax + // could be called directly. Calling `parse_expr` allows this function to only + // consider `Expr`s. + let expr = self.parse_expr()?; + let mut current = &expr; + let start_idx = fields.len(); + loop { + match current.kind { + ExprKind::Field(ref left, right) => { + // Field access is read right-to-left. + fields.insert(start_idx, right); + trailing_dot = None; + current = left; + } + // Parse this both to give helpful error messages and to + // verify it can be done with this parser setup. + ExprKind::Index(ref left, ref _right, span) => { + self.dcx().emit_err(errors::ArrayIndexInOffsetOf(span)); + current = left; + } + ExprKind::Lit(token::Lit { + kind: token::Float | token::Integer, + symbol, + suffix, + }) => { + if let Some(suffix) = suffix { + self.expect_no_tuple_index_suffix(current.span, suffix); + } + match self.break_up_float(symbol, current.span) { + // 1e2 + DestructuredFloat::Single(sym, sp) => { + trailing_dot = None; + fields.insert(start_idx, Ident::new(sym, sp)); + } + // 1. + DestructuredFloat::TrailingDot(sym, sym_span, dot_span) => { + assert!(suffix.is_none()); + trailing_dot = Some(dot_span); + fields.insert(start_idx, Ident::new(sym, sym_span)); + } + // 1.2 | 1.2e3 + DestructuredFloat::MiddleDot( + symbol1, + span1, + _dot_span, + symbol2, + span2, + ) => { + trailing_dot = None; + fields.insert(start_idx, Ident::new(symbol2, span2)); + fields.insert(start_idx, Ident::new(symbol1, span1)); + } + DestructuredFloat::Error => { + trailing_dot = None; + fields.insert(start_idx, Ident::new(symbol, self.prev_token.span)); + } + } + break; + } + ExprKind::Path(None, Path { ref segments, .. }) => { + match &segments[..] { + [PathSegment { ident, args: None, .. }] => { + trailing_dot = None; + fields.insert(start_idx, *ident) + } + _ => { + self.dcx().emit_err(errors::InvalidOffsetOf(current.span)); + break; + } + } + break; + } + _ => { + self.dcx().emit_err(errors::InvalidOffsetOf(current.span)); + break; + } + } } - DestructuredFloat::Error => { - self.bump(); - thin_vec![Ident::new(symbol, self.prev_token.span)] + + if matches!(self.token.kind, token::CloseDelim(..) | token::Comma) { + break; + } else if trailing_dot.is_none() { + // This loop should only repeat if there is a trailing dot. + self.dcx().emit_err(errors::InvalidOffsetOf(self.token.span)); + break; } - }) + } + if let Some(dot) = trailing_dot { + self.dcx().emit_err(errors::InvalidOffsetOf(dot)); + } + Ok(fields.into_iter().collect()) } fn parse_expr_tuple_field_access( @@ -1255,21 +1318,21 @@ impl<'a> Parser<'a> { // that of the open delim in `TokenTreesReader::parse_token_tree`, even if they are different. self.span_to_snippet(close_paren).is_ok_and(|snippet| snippet == ")") { - let mut replacement_err = errors::ParenthesesWithStructFields { - span, - r#type: path, - braces_for_struct: errors::BracesForStructLiteral { - first: open_paren, - second: close_paren, - }, - no_fields_for_fn: errors::NoFieldsForFnCall { - fields: fields - .into_iter() - .map(|field| field.span.until(field.expr.span)) - .collect(), - }, - } - .into_diagnostic(self.diagnostic()); + let mut replacement_err = + self.dcx().create_err(errors::ParenthesesWithStructFields { + span, + r#type: path, + braces_for_struct: errors::BracesForStructLiteral { + first: open_paren, + second: close_paren, + }, + no_fields_for_fn: errors::NoFieldsForFnCall { + fields: fields + .into_iter() + .map(|field| field.span.until(field.expr.span)) + .collect(), + }, + }); replacement_err.emit(); let old_err = mem::replace(err, replacement_err); @@ -1330,7 +1393,7 @@ impl<'a> Parser<'a> { } else { // Field access `expr.f` if let Some(args) = seg.args { - self.sess.emit_err(errors::FieldExpressionWithGeneric(args.span())); + self.dcx().emit_err(errors::FieldExpressionWithGeneric(args.span())); } let span = lo.to(self.prev_token.span); @@ -1558,7 +1621,7 @@ impl<'a> Parser<'a> { let (span, kind) = if self.eat(&token::Not) { // MACRO INVOCATION expression if qself.is_some() { - self.sess.emit_err(errors::MacroInvocationWithQualifiedPath(path.span)); + self.dcx().emit_err(errors::MacroInvocationWithQualifiedPath(path.span)); } let lo = path.span; let mac = P(MacCall { path, args: self.parse_delim_args()? }); @@ -1604,7 +1667,7 @@ impl<'a> Parser<'a> { { let (lit, _) = self.recover_unclosed_char(label_.ident, Parser::mk_token_lit_char, |self_| { - self_.sess.create_err(errors::UnexpectedTokenAfterLabel { + self_.dcx().create_err(errors::UnexpectedTokenAfterLabel { span: self_.token.span, remove_label: None, enclose_in_block: None, @@ -1616,7 +1679,7 @@ 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.sess.emit_err(errors::UnexpectedTokenAfterLabel { + self.dcx().emit_err(errors::UnexpectedTokenAfterLabel { span: self.token.span, remove_label: None, enclose_in_block: None, @@ -1671,12 +1734,12 @@ impl<'a> Parser<'a> { self.mk_expr(span, ExprKind::Block(blk, label)) }); - self.sess.emit_err(err); + self.dcx().emit_err(err); expr }?; if !ate_colon && consume_colon { - self.sess.emit_err(errors::RequireColonAfterLabeledExpression { + self.dcx().emit_err(errors::RequireColonAfterLabeledExpression { span: expr.span, label: lo, label_end: lo.shrink_to_hi(), @@ -1691,10 +1754,9 @@ impl<'a> Parser<'a> { &self, lifetime: Ident, mk_lit_char: impl FnOnce(Symbol, Span) -> L, - err: impl FnOnce(&Self) -> DiagnosticBuilder<'a, ErrorGuaranteed>, + err: impl FnOnce(&Self) -> DiagnosticBuilder<'a>, ) -> L { - if let Some(mut diag) = - self.diagnostic().steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar) + if let Some(mut diag) = self.dcx().steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar) { diag.span_suggestion_verbose( lifetime.span.shrink_to_hi(), @@ -1725,7 +1787,7 @@ impl<'a> Parser<'a> { self.bump(); // `catch` let span = lo.to(self.prev_token.span); - self.sess.emit_err(errors::DoCatchSyntaxRemoved { span }); + self.dcx().emit_err(errors::DoCatchSyntaxRemoved { span }); self.parse_try_block(lo) } @@ -1785,7 +1847,7 @@ impl<'a> Parser<'a> { // The value expression can be a labeled loop, see issue #86948, e.g.: // `loop { break 'label: loop { break 'label 42; }; }` let lexpr = self.parse_expr_labeled(label, true)?; - self.sess.emit_err(errors::LabeledLoopInBreak { + self.dcx().emit_err(errors::LabeledLoopInBreak { span: lexpr.span, sub: errors::WrapExpressionInParentheses { left: lexpr.span.shrink_to_lo(), @@ -1802,7 +1864,7 @@ impl<'a> Parser<'a> { && matches!( expr.kind, ExprKind::While(_, _, None) - | ExprKind::ForLoop(_, _, _, None) + | ExprKind::ForLoop { label: None, .. } | ExprKind::Loop(_, None, _) | ExprKind::Block(_, None) ) @@ -1884,8 +1946,7 @@ impl<'a> Parser<'a> { self.bump(); // `#` let Some((ident, false)) = self.token.ident() else { - let err = errors::ExpectedBuiltinIdent { span: self.token.span } - .into_diagnostic(self.diagnostic()); + 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); @@ -1895,8 +1956,10 @@ impl<'a> Parser<'a> { let ret = if let Some(res) = parse(self, lo, ident)? { Ok(res) } else { - let err = errors::UnknownBuiltinConstruct { span: lo.to(ident.span), name: ident.name } - .into_diagnostic(self.diagnostic()); + let err = self.dcx().create_err(errors::UnknownBuiltinConstruct { + span: lo.to(ident.span), + name: ident.name, + }); return Err(err); }; self.expect(&TokenKind::CloseDelim(Delimiter::Parenthesis))?; @@ -1908,15 +1971,29 @@ impl<'a> Parser<'a> { let container = self.parse_ty()?; self.expect(&TokenKind::Comma)?; - let seq_sep = SeqSep { sep: Some(token::Dot), trailing_sep_allowed: false }; - let (fields, _trailing, _recovered) = self.parse_seq_to_before_end( - &TokenKind::CloseDelim(Delimiter::Parenthesis), - seq_sep, - Parser::parse_field_name_maybe_tuple, - )?; - let fields = fields.into_iter().flatten().collect::<Vec<_>>(); + let fields = self.parse_floating_field_access()?; + let trailing_comma = self.eat_noexpect(&TokenKind::Comma); + + if let Err(mut e) = + self.expect_one_of(&[], &[TokenKind::CloseDelim(Delimiter::Parenthesis)]) + { + if trailing_comma { + e.note("unexpected third argument to offset_of"); + } else { + e.note("offset_of expects dot-separated field and variant names"); + } + e.emit(); + } + + // Eat tokens until the macro call ends. + if self.may_recover() { + while !matches!(self.token.kind, token::CloseDelim(..) | token::Eof) { + self.bump(); + } + } + let span = lo.to(self.token.span); - Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields.into()))) + Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields))) } /// Returns a string literal if the next token is a string literal. @@ -1959,15 +2036,16 @@ impl<'a> Parser<'a> { && let token::NtExpr(e) | token::NtLiteral(e) = &nt.0 && matches!(e.kind, ExprKind::Err) { - let mut err = errors::InvalidInterpolatedExpression { span: self.token.span } - .into_diagnostic(self.diagnostic()); + 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)); - self_.struct_span_err(token.span, msg) + self_.dcx().struct_span_err(token.span, msg) }; // On an error path, eagerly consider a lifetime to be an unclosed character lit if self.token.is_lifetime() { @@ -2018,7 +2096,7 @@ impl<'a> Parser<'a> { }); if let Some(token) = &recovered { self.bump(); - self.sess.emit_err(errors::FloatLiteralRequiresIntegerPart { + self.dcx().emit_err(errors::FloatLiteralRequiresIntegerPart { span: token.span, correct: pprust::token_to_string(token).into_owned(), }); @@ -2077,13 +2155,13 @@ impl<'a> Parser<'a> { if [sym::i32, sym::u32, sym::isize, sym::usize].contains(&suffix) { // #59553: warn instead of reject out of hand to allow the fix to percolate // through the ecosystem when people fix their macros - self.sess.emit_warning(errors::InvalidLiteralSuffixOnTupleIndex { + self.dcx().emit_warning(errors::InvalidLiteralSuffixOnTupleIndex { span, suffix, exception: Some(()), }); } else { - self.sess.emit_err(errors::InvalidLiteralSuffixOnTupleIndex { + self.dcx().emit_err(errors::InvalidLiteralSuffixOnTupleIndex { span, suffix, exception: None, @@ -2121,7 +2199,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.sess.emit_err(errors::ArrayBracketsInsteadOfSpaces { + self.dcx().emit_err(errors::ArrayBracketsInsteadOfSpaces { span: arr.span, sub: errors::ArrayBracketsInsteadOfSpacesSugg { left: lo, @@ -2169,10 +2247,10 @@ impl<'a> Parser<'a> { .span_to_snippet(snapshot.token.span) .is_ok_and(|snippet| snippet == "]") => { - return Err(errors::MissingSemicolonBeforeArray { + return Err(self.dcx().create_err(errors::MissingSemicolonBeforeArray { open_delim: open_delim_span, semicolon: prev_span.shrink_to_hi(), - }.into_diagnostic(self.diagnostic())); + })); } Ok(_) => (), Err(err) => err.cancel(), @@ -2195,7 +2273,7 @@ impl<'a> Parser<'a> { } if self.token.is_whole_block() { - self.sess.emit_err(errors::InvalidBlockMacroSegment { + self.dcx().emit_err(errors::InvalidBlockMacroSegment { span: self.token.span, context: lo.to(self.token.span), wrap: errors::WrapInExplicitBlock { @@ -2319,8 +2397,9 @@ impl<'a> Parser<'a> { // Check for `move async` and recover if self.check_keyword(kw::Async) { let move_async_span = self.token.span.with_lo(self.prev_token.span.data().lo); - Err(errors::AsyncMoveOrderIncorrect { span: move_async_span } - .into_diagnostic(self.diagnostic())) + Err(self + .dcx() + .create_err(errors::AsyncMoveOrderIncorrect { span: move_async_span })) } else { Ok(CaptureBy::Value { move_kw: move_kw_span }) } @@ -2398,7 +2477,7 @@ impl<'a> Parser<'a> { ExprKind::Binary(Spanned { span: binop_span, .. }, _, right) if let ExprKind::Block(_, None) = right.kind => { - self.sess.emit_err(errors::IfExpressionMissingThenBlock { + this.dcx().emit_err(errors::IfExpressionMissingThenBlock { if_span: lo, missing_then_block_sub: errors::IfExpressionMissingThenBlockSub::UnfinishedCondition( @@ -2409,7 +2488,7 @@ impl<'a> Parser<'a> { std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi())) } ExprKind::Block(_, None) => { - self.sess.emit_err(errors::IfExpressionMissingCondition { + this.dcx().emit_err(errors::IfExpressionMissingCondition { if_span: lo.shrink_to_hi(), block_span: self.sess.source_map().start_point(cond_span), }); @@ -2433,7 +2512,7 @@ impl<'a> Parser<'a> { let let_else_sub = matches!(cond.kind, ExprKind::Let(..)) .then(|| errors::IfExpressionLetSomeSub { if_span: lo.until(cond_span) }); - self.sess.emit_err(errors::IfExpressionMissingThenBlock { + self.dcx().emit_err(errors::IfExpressionMissingThenBlock { if_span: lo, missing_then_block_sub: errors::IfExpressionMissingThenBlockSub::AddThenBlock( cond_span.shrink_to_hi(), @@ -2444,6 +2523,7 @@ impl<'a> Parser<'a> { } } else { let attrs = self.parse_outer_attributes()?; // For recovery. + let maybe_fatarrow = self.token.clone(); let block = if self.check(&token::OpenDelim(Delimiter::Brace)) { self.parse_block()? } else { @@ -2468,6 +2548,15 @@ impl<'a> Parser<'a> { "you likely meant to continue parsing the let-chain starting here", ); } else { + // Look for usages of '=>' where '>=' might be intended + if maybe_fatarrow.kind == token::FatArrow { + err.span_suggestion( + maybe_fatarrow.span, + "you might have meant to write a \"greater than or equal to\" comparison", + ">=", + Applicability::MaybeIncorrect, + ); + } err.span_note( cond_span, "the `if` expression is missing a block after this condition", @@ -2510,9 +2599,9 @@ impl<'a> Parser<'a> { }; if self.prev_token.kind == token::BinOp(token::Or) { // This was part of a closure, the that part of the parser recover. - return Err(err.into_diagnostic(self.diagnostic())); + return Err(self.dcx().create_err(err)); } else { - Some(self.sess.emit_err(err)) + Some(self.dcx().emit_err(err)) } } else { None @@ -2526,7 +2615,7 @@ impl<'a> Parser<'a> { CommaRecoveryMode::LikelyTuple, )?; if self.token == token::EqEq { - self.sess.emit_err(errors::ExpectedEqForLetExpr { + self.dcx().emit_err(errors::ExpectedEqForLetExpr { span: self.token.span, sugg_span: self.token.span, }); @@ -2558,7 +2647,7 @@ impl<'a> Parser<'a> { if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) && classify::expr_requires_semi_to_be_stmt(&cond) => { - self.sess.emit_err(errors::ExpectedElseBlock { + self.dcx().emit_err(errors::ExpectedElseBlock { first_tok_span, first_tok, else_span, @@ -2598,7 +2687,7 @@ impl<'a> Parser<'a> { [x0 @ xn] | [x0, .., xn] => (x0.span.to(xn.span), xn.span), }; let ctx = if is_ctx_else { "else" } else { "if" }; - self.sess.emit_err(errors::OuterAttributeNotAllowedOnIfElse { + self.dcx().emit_err(errors::OuterAttributeNotAllowedOnIfElse { last, branch_span, ctx_span, @@ -2612,7 +2701,7 @@ impl<'a> Parser<'a> { && let BinOpKind::And = binop && let ExprKind::If(cond, ..) = &right.kind { - Err(self.sess.create_err(errors::UnexpectedIfWithIf( + Err(self.dcx().create_err(errors::UnexpectedIfWithIf( binop_span.shrink_to_hi().to(cond.span.shrink_to_lo()), ))) } else { @@ -2661,7 +2750,7 @@ impl<'a> Parser<'a> { let right = self.prev_token.span.between(self.look_ahead(1, |t| t.span)); self.bump(); // ) err.cancel(); - self.sess.emit_err(errors::ParenthesesInForHead { + self.dcx().emit_err(errors::ParenthesesInForHead { span, // With e.g. `for (x) in y)` this would replace `(x) in y)` // with `x) in y)` which is syntactically invalid. @@ -2683,27 +2772,36 @@ impl<'a> Parser<'a> { Ok((pat, expr)) } - /// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten). + /// Parses `for await? <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten). fn parse_expr_for(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> { + let is_await = + 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); + } + + let kind = if is_await { ForLoopKind::ForAwait } else { ForLoopKind::For }; + let (pat, expr) = self.parse_for_head()?; // Recover from missing expression in `for` loop if matches!(expr.kind, ExprKind::Block(..)) && !matches!(self.token.kind, token::OpenDelim(Delimiter::Brace)) && self.may_recover() { - self.sess + self.dcx() .emit_err(errors::MissingExpressionInForLoop { span: expr.span.shrink_to_lo() }); let err_expr = self.mk_expr(expr.span, ExprKind::Err); let block = self.mk_block(thin_vec![], BlockCheckMode::Default, self.prev_token.span); return Ok(self.mk_expr( lo.to(self.prev_token.span), - ExprKind::ForLoop(pat, err_expr, block, opt_label), + ExprKind::ForLoop { pat, iter: err_expr, body: block, label: opt_label, kind }, )); } let (attrs, loop_block) = self.parse_inner_attrs_and_block()?; - let kind = ExprKind::ForLoop(pat, expr, loop_block, opt_label); + let kind = ExprKind::ForLoop { pat, iter: expr, body: loop_block, label: opt_label, kind }; self.recover_loop_else("for", lo)?; @@ -2716,7 +2814,7 @@ impl<'a> Parser<'a> { let else_span = self.token.span; self.bump(); let else_clause = self.parse_expr_else()?; - self.sess.emit_err(errors::LoopElseNotSupported { + self.dcx().emit_err(errors::LoopElseNotSupported { span: else_span.to(else_clause.span), loop_kind, loop_kw, @@ -2735,7 +2833,7 @@ impl<'a> Parser<'a> { (self.prev_token.span.between(self.token.span), errors::MissingInInForLoopSub::AddIn) }; - self.sess.emit_err(errors::MissingInInForLoop { span, sub: sub(span) }); + self.dcx().emit_err(errors::MissingInInForLoop { span, sub: sub(span) }); } /// Parses a `while` or `while let` expression (`while` token already eaten). @@ -2843,7 +2941,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.sess.emit_err(errors::MatchArmBodyWithoutBraces { + this.dcx().emit_err(errors::MatchArmBodyWithoutBraces { statements: span, arrow: arrow_span, num_statements: stmts.len(), @@ -2917,7 +3015,13 @@ impl<'a> Parser<'a> { let is_almost_fat_arrow = TokenKind::FatArrow .similar_tokens() .is_some_and(|similar_tokens| similar_tokens.contains(&this.token.kind)); - let mut result = if !is_fat_arrow && !is_almost_fat_arrow { + + // this avoids the compiler saying that a `,` or `}` was expected even though + // the pattern isn't a never pattern (and thus an arm body is required) + let armless = (!is_fat_arrow && !is_almost_fat_arrow && pat.could_be_never_pattern()) + || matches!(this.token.kind, token::Comma | token::CloseDelim(Delimiter::Brace)); + + let mut result = if armless { // A pattern without a body, allowed for never patterns. arm_body = None; this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)]).map( @@ -3050,7 +3154,7 @@ impl<'a> Parser<'a> { .is_ok(); if pattern_follows && snapshot.check(&TokenKind::FatArrow) { err.cancel(); - this.sess.emit_err(errors::MissingCommaAfterMatchArm { + this.dcx().emit_err(errors::MissingCommaAfterMatchArm { span: arm_span.shrink_to_hi(), }); return Ok(true); @@ -3139,7 +3243,7 @@ impl<'a> Parser<'a> { 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 { + self.dcx().emit_err(errors::ParenthesesInMatchPat { span: vec![left, right], sugg: errors::ParenthesesInMatchPatSugg { left, right }, }); @@ -3194,8 +3298,7 @@ impl<'a> Parser<'a> { fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, P<Expr>> { let (attrs, body) = self.parse_inner_attrs_and_block()?; if self.eat_keyword(kw::Catch) { - Err(errors::CatchAfterTry { span: self.prev_token.span } - .into_diagnostic(self.diagnostic())) + 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); @@ -3294,7 +3397,7 @@ impl<'a> Parser<'a> { let expr = self.parse_expr_struct(qself.clone(), path.clone(), true); if let (Ok(expr), false) = (&expr, struct_allowed) { // This is a struct literal, but we don't can't accept them here. - self.sess.emit_err(errors::StructLiteralNotAllowedHere { + self.dcx().emit_err(errors::StructLiteralNotAllowedHere { span: expr.span, sub: errors::StructLiteralNotAllowedHereSugg { left: path.span.shrink_to_lo(), @@ -3478,7 +3581,7 @@ impl<'a> Parser<'a> { if self.token != token::Comma { return; } - self.sess.emit_err(errors::CommaAfterBaseStruct { + self.dcx().emit_err(errors::CommaAfterBaseStruct { span: span.to(self.prev_token.span), comma: self.token.span, }); @@ -3491,7 +3594,7 @@ impl<'a> Parser<'a> { { // recover from typo of `...`, suggest `..` let span = self.prev_token.span; - self.sess.emit_err(errors::MissingDotDot { token_span: span, sugg_span: span }); + self.dcx().emit_err(errors::MissingDotDot { token_span: span, sugg_span: span }); return true; } false @@ -3504,7 +3607,7 @@ impl<'a> Parser<'a> { let label = format!("'{}", ident.name); let ident = Ident { name: Symbol::intern(&label), span: ident.span }; - self.sess.emit_err(errors::ExpectedLabelFoundIdent { + self.dcx().emit_err(errors::ExpectedLabelFoundIdent { span: ident.span, start: ident.span.shrink_to_lo(), }); @@ -3532,12 +3635,11 @@ impl<'a> Parser<'a> { || t == &token::CloseDelim(Delimiter::Parenthesis) }); if is_wrong { - return Err(errors::ExpectedStructField { + return Err(this.dcx().create_err(errors::ExpectedStructField { span: this.look_ahead(1, |t| t.span), ident_span: this.token.span, token: this.look_ahead(1, |t| t.clone()), - } - .into_diagnostic(&self.sess.span_diagnostic)); + })); } let (ident, expr) = if is_shorthand { // Mimic `x: x` for the `x` field shorthand. @@ -3573,18 +3675,18 @@ impl<'a> Parser<'a> { return; } - self.sess.emit_err(errors::EqFieldInit { + self.dcx().emit_err(errors::EqFieldInit { span: self.token.span, eq: field_name.span.shrink_to_hi().to(self.token.span), }); } fn err_dotdotdot_syntax(&self, span: Span) { - self.sess.emit_err(errors::DotDotDot { span }); + self.dcx().emit_err(errors::DotDotDot { span }); } fn err_larrow_operator(&self, span: Span) { - self.sess.emit_err(errors::LeftArrowOperator { span }); + self.dcx().emit_err(errors::LeftArrowOperator { span }); } fn mk_assign_op(&self, binop: BinOp, lhs: P<Expr>, rhs: P<Expr>) -> ExprKind { @@ -3721,7 +3823,7 @@ impl MutVisitor for CondChecker<'_> { 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 { + Some(self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet { span, reason, missing_let: self.missing_let, @@ -3800,7 +3902,7 @@ impl MutVisitor for CondChecker<'_> { | ExprKind::Lit(_) | ExprKind::If(_, _, _) | ExprKind::While(_, _, _) - | ExprKind::ForLoop(_, _, _, _) + | ExprKind::ForLoop { .. } | ExprKind::Loop(_, _, _) | ExprKind::Match(_, _) | ExprKind::Closure(_) |
