diff options
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
-rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 509 |
1 files changed, 259 insertions, 250 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 725768c1f..a781748ef 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,26 +1,33 @@ -use super::diagnostics::{ - CatchAfterTry, CommaAfterBaseStruct, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, - ExpectedElseBlock, ExpectedExpressionFoundLet, FieldExpressionWithGeneric, - FloatLiteralRequiresIntegerPart, IfExpressionMissingCondition, IfExpressionMissingThenBlock, - IfExpressionMissingThenBlockSub, InvalidBlockMacroSegment, InvalidComparisonOperator, - InvalidComparisonOperatorSub, InvalidLogicalOperator, InvalidLogicalOperatorSub, - LeftArrowOperator, LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, - MalformedLoopLabel, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray, - NotAsNegationOperator, OuterAttributeNotAllowedOnIfElse, RequireColonAfterLabeledExpression, - SnapshotParser, TildeAsUnaryOperator, UnexpectedTokenAfterLabel, -}; +use super::diagnostics::SnapshotParser; use super::pat::{CommaRecoveryMode, RecoverColon, RecoverComma, PARAM_EXPECTED}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenExpectType, TokenType, TrailingToken, }; -use crate::maybe_recover_from_interpolated_ty_qpath; -use crate::parser::diagnostics::{ - IntLiteralTooLarge, InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, - InvalidIntLiteralWidth, InvalidNumLiteralBasePrefix, InvalidNumLiteralSuffix, - MissingCommaAfterMatchArm, +use crate::errors::{ + ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncMoveOrderIncorrect, + BinaryFloatLiteralNotSupported, BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct, + ComparisonInterpretedAsGeneric, ComparisonOrShiftInterpretedAsGenericSugg, + DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedExpressionFoundLet, + FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt, + HexadecimalFloatLiteralNotSupported, IfExpressionMissingCondition, + IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, IntLiteralTooLarge, + InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub, + InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, InvalidIntLiteralWidth, + InvalidInterpolatedExpression, InvalidLiteralSuffix, InvalidLiteralSuffixOnTupleIndex, + InvalidLogicalOperator, InvalidLogicalOperatorSub, InvalidNumLiteralBasePrefix, + InvalidNumLiteralSuffix, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator, + LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel, + MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm, + MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray, NoFieldsForFnCall, + NotAsNegationOperator, NotAsNegationOperatorSub, OctalFloatLiteralNotSupported, + OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields, + RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere, + StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedTokenAfterLabel, + UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses, }; +use crate::maybe_recover_from_interpolated_ty_qpath; use core::mem; use rustc_ast::ptr::P; @@ -35,10 +42,13 @@ use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; use rustc_ast::{ClosureBinder, StmtKind}; use rustc_ast_pretty::pprust; -use rustc_errors::{Applicability, Diagnostic, PResult}; +use rustc_errors::{ + Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult, + StashKey, +}; +use rustc_session::errors::ExprParenthesesNeeded; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; use rustc_session::lint::BuiltinLintDiagnostics; -use rustc_session::SessionDiagnostic; use rustc_span::source_map::{self, Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Pos}; @@ -420,13 +430,11 @@ 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) { - let mut err = self.struct_span_err( - self.token.span, - &format!("expected expression, found `{}`", pprust::token_to_string(&self.token),), - ); - err.span_label(self.token.span, "expected expression"); - self.sess.expr_parentheses_needed(&mut err, lhs.span); - err.emit(); + self.sess.emit_err(FoundExprWouldBeStmt { + span: self.token.span, + token: self.token.clone(), + suggestion: ExprParenthesesNeeded::surrounding(lhs.span), + }); } /// Possibly translate the current token to an associative operator. @@ -577,21 +585,16 @@ impl<'a> Parser<'a> { make_it!(this, attrs, |this, _| this.parse_borrow_expr(lo)) } token::BinOp(token::Plus) if this.look_ahead(1, |tok| tok.is_numeric_lit()) => { - let mut err = this.struct_span_err(lo, "leading `+` is not supported"); - err.span_label(lo, "unexpected `+`"); + let mut err = + LeadingPlusNotSupported { span: lo, remove_plus: None, add_parentheses: None }; // 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) { - this.sess.expr_parentheses_needed(&mut err, *sp); + err.add_parentheses = Some(ExprParenthesesNeeded::surrounding(*sp)); } else { - err.span_suggestion_verbose( - lo, - "try removing the `+`", - "", - Applicability::MachineApplicable, - ); + err.remove_plus = Some(lo); } - err.emit(); + this.sess.emit_err(err); this.bump(); this.parse_prefix_expr(None) @@ -660,12 +663,23 @@ impl<'a> Parser<'a> { fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { // Emit the error... let negated_token = self.look_ahead(1, |t| t.clone()); + + let sub_diag = if negated_token.is_numeric_lit() { + NotAsNegationOperatorSub::SuggestNotBitwise + } else if negated_token.is_bool_lit() { + NotAsNegationOperatorSub::SuggestNotLogical + } else { + NotAsNegationOperatorSub::SuggestNotDefault + }; + self.sess.emit_err(NotAsNegationOperator { negated: negated_token.span, negated_desc: super::token_descr(&negated_token), // Span the `not` plus trailing whitespace to avoid // trailing whitespace after the `!` in our suggestion - not: self.sess.source_map().span_until_non_whitespace(lo.to(negated_token.span)), + sub: sub_diag( + self.sess.source_map().span_until_non_whitespace(lo.to(negated_token.span)), + ), }); // ...and recover! @@ -743,9 +757,34 @@ impl<'a> Parser<'a> { match self.parse_path(PathStyle::Expr) { Ok(path) => { - let (op_noun, op_verb) = match self.token.kind { - token::Lt => ("comparison", "comparing"), - token::BinOp(token::Shl) => ("shift", "shifting"), + let span_after_type = parser_snapshot_after_type.token.span; + let expr = mk_expr( + self, + lhs, + self.mk_ty(path.span, TyKind::Path(None, path.clone())), + ); + + let args_span = self.look_ahead(1, |t| t.span).to(span_after_type); + let suggestion = ComparisonOrShiftInterpretedAsGenericSugg { + left: expr.span.shrink_to_lo(), + right: expr.span.shrink_to_hi(), + }; + + match self.token.kind { + token::Lt => self.sess.emit_err(ComparisonInterpretedAsGeneric { + comparison: self.token.span, + r#type: path, + args: args_span, + suggestion, + }), + token::BinOp(token::Shl) => { + self.sess.emit_err(ShiftInterpretedAsGeneric { + shift: self.token.span, + r#type: path, + args: args_span, + suggestion, + }) + } _ => { // We can end up here even without `<` being the next token, for // example because `parse_ty_no_plus` returns `Err` on keywords, @@ -759,33 +798,7 @@ impl<'a> Parser<'a> { // Successfully parsed the type path leaving a `<` yet to parse. type_err.cancel(); - // Report non-fatal diagnostics, keep `x as usize` as an expression - // in AST and continue parsing. - let msg = format!( - "`<` is interpreted as a start of generic arguments for `{}`, not a {}", - pprust::path_to_string(&path), - op_noun, - ); - let span_after_type = parser_snapshot_after_type.token.span; - let expr = - mk_expr(self, lhs, self.mk_ty(path.span, TyKind::Path(None, path))); - - self.struct_span_err(self.token.span, &msg) - .span_label( - self.look_ahead(1, |t| t.span).to(span_after_type), - "interpreted as generic arguments", - ) - .span_label(self.token.span, format!("not interpreted as {op_noun}")) - .multipart_suggestion( - &format!("try {op_verb} the cast value"), - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ) - .emit(); - + // Keep `x as usize` as an expression in AST and continue parsing. expr } Err(path_err) => { @@ -832,7 +845,7 @@ impl<'a> Parser<'a> { ExprKind::Index(_, _) => "indexing", ExprKind::Try(_) => "`?`", ExprKind::Field(_, _) => "a field access", - ExprKind::MethodCall(_, _, _) => "a method call", + ExprKind::MethodCall(_, _, _, _) => "a method call", ExprKind::Call(_, _) => "a function call", ExprKind::Await(_) => "`.await`", ExprKind::Err => return Ok(with_postfix), @@ -1146,7 +1159,9 @@ impl<'a> Parser<'a> { } let span = self.prev_token.span; let field = ExprKind::Field(base, Ident::new(field, span)); - self.expect_no_suffix(span, "a tuple index", suffix); + if let Some(suffix) = suffix { + self.expect_no_tuple_index_suffix(span, suffix); + } self.mk_expr(lo.to(span), field) } @@ -1184,9 +1199,8 @@ impl<'a> Parser<'a> { ) -> Option<P<Expr>> { match (seq.as_mut(), snapshot) { (Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => { - let name = pprust::path_to_string(&path); snapshot.bump(); // `(` - match snapshot.parse_struct_fields(path, false, Delimiter::Parenthesis) { + match snapshot.parse_struct_fields(path.clone(), false, Delimiter::Parenthesis) { Ok((fields, ..)) if snapshot.eat(&token::CloseDelim(Delimiter::Parenthesis)) => { @@ -1196,29 +1210,25 @@ impl<'a> Parser<'a> { let close_paren = self.prev_token.span; let span = lo.to(self.prev_token.span); if !fields.is_empty() { - let replacement_err = self.struct_span_err( + let mut replacement_err = ParenthesesWithStructFields { span, - "invalid `struct` delimiters or `fn` call arguments", - ); - mem::replace(err, replacement_err).cancel(); - - err.multipart_suggestion( - &format!("if `{name}` is a struct, use braces as delimiters"), - vec![ - (open_paren, " { ".to_string()), - (close_paren, " }".to_string()), - ], - Applicability::MaybeIncorrect, - ); - err.multipart_suggestion( - &format!("if `{name}` is a function, use the arguments directly"), - fields - .into_iter() - .map(|field| (field.span.until(field.expr.span), String::new())) - .collect(), - Applicability::MaybeIncorrect, - ); - err.emit(); + r#type: path, + braces_for_struct: BracesForStructLiteral { + first: open_paren, + second: close_paren, + }, + no_fields_for_fn: NoFieldsForFnCall { + fields: fields + .into_iter() + .map(|field| field.span.until(field.expr.span)) + .collect(), + }, + } + .into_diagnostic(&self.sess.span_diagnostic); + replacement_err.emit(); + + let old_err = mem::replace(err, replacement_err); + old_err.cancel(); } else { err.emit(); } @@ -1259,12 +1269,10 @@ impl<'a> Parser<'a> { if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { // Method call `expr.f()` - let mut args = self.parse_paren_expr_seq()?; - args.insert(0, self_arg); - + let args = self.parse_paren_expr_seq()?; let fn_span = fn_span_lo.to(self.prev_token.span); let span = lo.to(self.prev_token.span); - Ok(self.mk_expr(span, ExprKind::MethodCall(segment, args, fn_span))) + Ok(self.mk_expr(span, ExprKind::MethodCall(segment, self_arg, args, fn_span))) } else { // Field access `expr.f` if let Some(args) = segment.args { @@ -1304,7 +1312,7 @@ impl<'a> Parser<'a> { // 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) = self.sess.ambiguous_block_expr_parse.borrow().get(&lo) { - self.sess.expr_parentheses_needed(&mut err, *sp); + err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } err }) @@ -1507,11 +1515,11 @@ impl<'a> Parser<'a> { /// Parse `'label: $expr`. The label is already parsed. fn parse_labeled_expr( &mut self, - label: Label, + label_: Label, mut consume_colon: bool, ) -> PResult<'a, P<Expr>> { - let lo = label.ident.span; - let label = Some(label); + let lo = label_.ident.span; + let label = Some(label_); let ate_colon = self.eat(&token::Colon); let expr = if self.eat_keyword(kw::While) { self.parse_while_expr(label, lo) @@ -1524,18 +1532,35 @@ impl<'a> Parser<'a> { { self.parse_block_expr(label, lo, BlockCheckMode::Default) } else if !ate_colon + && (matches!(self.token.kind, token::CloseDelim(_) | token::Comma) + || self.token.is_op()) + { + let lit = self.recover_unclosed_char(label_.ident, |self_| { + self_.sess.create_err(UnexpectedTokenAfterLabel { + span: self_.token.span, + remove_label: None, + enclose_in_block: None, + }) + }); + consume_colon = false; + Ok(self.mk_expr(lo, ExprKind::Lit(lit))) + } else if !ate_colon && (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(UnexpectedTokenAfterLabel(self.token.span)); + self.sess.emit_err(UnexpectedTokenAfterLabel { + span: self.token.span, + remove_label: None, + enclose_in_block: None, + }); consume_colon = false; Ok(self.mk_expr_err(lo)) } else { - // FIXME: use UnexpectedTokenAfterLabel, needs multipart suggestions - let msg = "expected `while`, `for`, `loop` or `{` after a label"; - - let mut err = self.struct_span_err(self.token.span, msg); - err.span_label(self.token.span, msg); + let mut err = UnexpectedTokenAfterLabel { + span: self.token.span, + remove_label: None, + enclose_in_block: None, + }; // Continue as an expression in an effort to recover on `'label: non_block_expr`. let expr = self.parse_expr().map(|expr| { @@ -1562,28 +1587,15 @@ impl<'a> Parser<'a> { // If there are no breaks that may use this label, suggest removing the label and // recover to the unmodified expression. if !found_labeled_breaks { - let msg = "consider removing the label"; - err.span_suggestion_verbose( - lo.until(span), - msg, - "", - Applicability::MachineApplicable, - ); + err.remove_label = Some(lo.until(span)); return expr; } - let sugg_msg = "consider enclosing expression in a block"; - let suggestions = vec![ - (span.shrink_to_lo(), "{ ".to_owned()), - (span.shrink_to_hi(), " }".to_owned()), - ]; - - err.multipart_suggestion_verbose( - sugg_msg, - suggestions, - Applicability::MachineApplicable, - ); + err.enclose_in_block = Some(UnexpectedTokenAfterLabelSugg { + left: span.shrink_to_lo(), + right: span.shrink_to_hi(), + }); // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to suppress future errors about `break 'label`. let stmt = self.mk_stmt(span, StmtKind::Expr(expr)); @@ -1591,7 +1603,7 @@ impl<'a> Parser<'a> { self.mk_expr(span, ExprKind::Block(blk, label)) }); - err.emit(); + self.sess.emit_err(err); expr }?; @@ -1606,6 +1618,39 @@ impl<'a> Parser<'a> { Ok(expr) } + /// Emit an error when a char is parsed as a lifetime because of a missing quote + pub(super) fn recover_unclosed_char( + &mut self, + lifetime: Ident, + err: impl FnOnce(&mut Self) -> DiagnosticBuilder<'a, ErrorGuaranteed>, + ) -> ast::Lit { + if let Some(mut diag) = + self.sess.span_diagnostic.steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar) + { + diag.span_suggestion_verbose( + lifetime.span.shrink_to_hi(), + "add `'` to close the char literal", + "'", + Applicability::MaybeIncorrect, + ) + .emit(); + } else { + err(self) + .span_suggestion_verbose( + lifetime.span.shrink_to_hi(), + "add `'` to close the char literal", + "'", + Applicability::MaybeIncorrect, + ) + .emit(); + } + ast::Lit { + token_lit: token::Lit::new(token::LitKind::Char, lifetime.name, None), + kind: ast::LitKind::Char(lifetime.name.as_str().chars().next().unwrap_or('_')), + span: lifetime.span, + } + } + /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead. fn recover_do_catch(&mut self) -> PResult<'a, P<Expr>> { let lo = self.token.span; @@ -1662,19 +1707,13 @@ 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_labeled_expr(label.take().unwrap(), true)?; - self.struct_span_err( - lexpr.span, - "parentheses are required around this expression to avoid confusion with a labeled break expression", - ) - .multipart_suggestion( - "wrap the expression in parentheses", - vec![ - (lexpr.span.shrink_to_lo(), "(".to_string()), - (lexpr.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(LabeledLoopInBreak { + span: lexpr.span, + sub: WrapExpressionInParentheses { + left: lexpr.span.shrink_to_lo(), + right: lexpr.span.shrink_to_hi(), + }, + }); Some(lexpr) } else if self.token != token::OpenDelim(Delimiter::Brace) || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) @@ -1737,7 +1776,7 @@ impl<'a> Parser<'a> { } pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> { - self.parse_opt_lit().ok_or_else(|| { + self.parse_opt_lit().ok_or(()).or_else(|()| { if let token::Interpolated(inner) = &self.token.kind { let expr = match inner.as_ref() { token::NtExpr(expr) => Some(expr), @@ -1746,16 +1785,25 @@ impl<'a> Parser<'a> { }; if let Some(expr) = expr { if matches!(expr.kind, ExprKind::Err) { - let mut err = self - .diagnostic() - .struct_span_err(self.token.span, "invalid interpolated expression"); + let mut err = InvalidInterpolatedExpression { span: self.token.span } + .into_diagnostic(&self.sess.span_diagnostic); err.downgrade_to_delayed_bug(); - return err; + return Err(err); } } } - let msg = format!("unexpected token: {}", super::token_descr(&self.token)); - self.struct_span_err(self.token.span, &msg) + let token = self.token.clone(); + let err = |self_: &mut Self| { + let msg = format!("unexpected token: {}", super::token_descr(&token)); + self_.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() { + let lt = self.expect_lifetime(); + Ok(self.recover_unclosed_char(lt.ident, err)) + } else { + Err(err(self)) + } }) } @@ -1780,7 +1828,10 @@ impl<'a> Parser<'a> { }); if let Some(token) = &recovered { self.bump(); - self.error_float_lits_must_have_int_part(&token); + self.sess.emit_err(FloatLiteralRequiresIntegerPart { + span: token.span, + correct: pprust::token_to_string(token).into_owned(), + }); } } @@ -1808,13 +1859,6 @@ impl<'a> Parser<'a> { } } - fn error_float_lits_must_have_int_part(&self, token: &Token) { - self.sess.emit_err(FloatLiteralRequiresIntegerPart { - span: token.span, - correct: pprust::token_to_string(token).into_owned(), - }); - } - fn report_lit_error(&self, err: LitError, lit: token::Lit, span: Span) { // Checks if `s` looks like i32 or u1234 etc. fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { @@ -1843,11 +1887,13 @@ impl<'a> Parser<'a> { // by lexer, so here we don't report it the second time. LitError::LexerError => {} LitError::InvalidSuffix => { - self.expect_no_suffix( - span, - &format!("{} {} literal", kind.article(), kind.descr()), - suffix, - ); + if let Some(suffix) = suffix { + self.sess.emit_err(InvalidLiteralSuffix { + span, + kind: format!("{}", kind.descr()), + suffix, + }); + } } LitError::InvalidIntSuffix => { let suf = suffix.expect("suffix error with no suffix"); @@ -1873,15 +1919,12 @@ impl<'a> Parser<'a> { } } LitError::NonDecimalFloat(base) => { - let descr = match base { - 16 => "hexadecimal", - 8 => "octal", - 2 => "binary", + match base { + 16 => self.sess.emit_err(HexadecimalFloatLiteralNotSupported { span }), + 8 => self.sess.emit_err(OctalFloatLiteralNotSupported { span }), + 2 => self.sess.emit_err(BinaryFloatLiteralNotSupported { span }), _ => unreachable!(), }; - self.struct_span_err(span, &format!("{descr} float literal is not supported")) - .span_label(span, "not supported") - .emit(); } LitError::IntTooLarge => { self.sess.emit_err(IntLiteralTooLarge { span }); @@ -1889,38 +1932,17 @@ impl<'a> Parser<'a> { } } - pub(super) fn expect_no_suffix(&self, sp: Span, kind: &str, suffix: Option<Symbol>) { - if let Some(suf) = suffix { - let mut err = if kind == "a tuple index" - && [sym::i32, sym::u32, sym::isize, sym::usize].contains(&suf) - { - // #59553: warn instead of reject out of hand to allow the fix to percolate - // through the ecosystem when people fix their macros - let mut err = self - .sess - .span_diagnostic - .struct_span_warn(sp, &format!("suffixes on {kind} are invalid")); - err.note(&format!( - "`{}` is *temporarily* accepted on tuple index fields as it was \ - incorrectly accepted on stable for a few releases", - suf, - )); - err.help( - "on proc macros, you'll want to use `syn::Index::from` or \ - `proc_macro::Literal::*_unsuffixed` for code that will desugar \ - to tuple field access", - ); - err.note( - "see issue #60210 <https://github.com/rust-lang/rust/issues/60210> \ - for more information", - ); - err - } else { - self.struct_span_err(sp, &format!("suffixes on {kind} are invalid")) - .forget_guarantee() - }; - err.span_label(sp, format!("invalid suffix `{suf}`")); - err.emit(); + pub(super) fn expect_no_tuple_index_suffix(&self, span: Span, suffix: Symbol) { + 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(InvalidLiteralSuffixOnTupleIndex { + span, + suffix, + exception: Some(()), + }); + } else { + self.sess.emit_err(InvalidLiteralSuffixOnTupleIndex { span, suffix, exception: None }); } } @@ -1954,14 +1976,13 @@ impl<'a> Parser<'a> { let mut snapshot = self.create_snapshot_for_diagnostic(); match snapshot.parse_array_or_repeat_expr(Delimiter::Brace) { Ok(arr) => { - let hi = snapshot.prev_token.span; - self.struct_span_err(arr.span, "this is a block expression, not an array") - .multipart_suggestion( - "to make an array, use square brackets instead of curly braces", - vec![(lo, "[".to_owned()), (hi, "]".to_owned())], - Applicability::MaybeIncorrect, - ) - .emit(); + self.sess.emit_err(ArrayBracketsInsteadOfSpaces { + span: arr.span, + sub: ArrayBracketsInsteadOfSpacesSugg { + left: lo, + right: snapshot.prev_token.span, + }, + }); self.restore_snapshot(snapshot); Some(self.mk_expr_err(arr.span)) @@ -2088,6 +2109,12 @@ impl<'a> Parser<'a> { if self.token.kind == TokenKind::Semi && matches!(self.token_cursor.frame.delim_sp, Some((Delimiter::Parenthesis, _))) + // HACK: This is needed so we can detect whether we're inside a macro, + // where regular assumptions about what tokens can follow other tokens + // don't necessarily apply. + && self.may_recover() + // FIXME(Nilstrieb): Remove this check once `may_recover` actually stops recovery + && self.subparser_name.is_none() { // It is likely that the closure body is a block but where the // braces have been removed. We will recover and eat the next @@ -2124,7 +2151,8 @@ 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(self.incorrect_move_async_order_found(move_async_span)) + Err(AsyncMoveOrderIncorrect { span: move_async_span } + .into_diagnostic(&self.sess.span_diagnostic)) } else { Ok(CaptureBy::Value) } @@ -2208,7 +2236,7 @@ impl<'a> Parser<'a> { }, ExprKind::Block(_, None) => { self.sess.emit_err(IfExpressionMissingCondition { - if_span: self.sess.source_map().next_point(lo), + if_span: lo.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())) @@ -2505,39 +2533,22 @@ impl<'a> Parser<'a> { self.bump(); // `;` let mut stmts = vec![self.mk_stmt(first_expr.span, ast::StmtKind::Expr(first_expr.clone()))]; - let err = |this: &mut Parser<'_>, stmts: Vec<ast::Stmt>| { + let err = |this: &Parser<'_>, stmts: Vec<ast::Stmt>| { let span = stmts[0].span.to(stmts[stmts.len() - 1].span); - let mut err = this.struct_span_err(span, "`match` arm body without braces"); - let (these, s, are) = - if stmts.len() > 1 { ("these", "s", "are") } else { ("this", "", "is") }; - err.span_label( - span, - &format!( - "{these} statement{s} {are} not surrounded by a body", - these = these, - s = s, - are = are - ), - ); - err.span_label(arrow_span, "while parsing the `match` arm starting here"); - if stmts.len() > 1 { - err.multipart_suggestion( - &format!("surround the statement{s} with a body"), - vec![ - (span.shrink_to_lo(), "{ ".to_string()), - (span.shrink_to_hi(), " }".to_string()), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_suggestion( - semi_sp, - "use a comma to end a `match` arm expression", - ",", - Applicability::MachineApplicable, - ); - } - err.emit(); + + this.sess.emit_err(MatchArmBodyWithoutBraces { + statements: span, + arrow: arrow_span, + num_statements: stmts.len(), + sub: if stmts.len() > 1 { + MatchArmBodyWithoutBracesSugg::AddBraces { + left: span.shrink_to_lo(), + right: span.shrink_to_hi(), + } + } else { + MatchArmBodyWithoutBracesSugg::UseComma { semicolon: semi_sp } + }, + }); this.mk_expr_err(span) }; // We might have either a `,` -> `;` typo, or a block without braces. We need @@ -2826,23 +2837,19 @@ impl<'a> Parser<'a> { let expr = self.parse_struct_expr(qself.cloned(), 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.error_struct_lit_not_allowed_here(path.span, expr.span); + self.sess.emit_err(StructLiteralNotAllowedHere { + span: expr.span, + sub: StructLiteralNotAllowedHereSugg { + left: path.span.shrink_to_lo(), + right: expr.span.shrink_to_hi(), + }, + }); } return Some(expr); } None } - fn error_struct_lit_not_allowed_here(&self, lo: Span, sp: Span) { - self.struct_span_err(sp, "struct literals are not allowed here") - .multipart_suggestion( - "surround the struct literal with parentheses", - vec![(lo.shrink_to_lo(), "(".to_string()), (sp.shrink_to_hi(), ")".to_string())], - Applicability::MachineApplicable, - ) - .emit(); - } - pub(super) fn parse_struct_fields( &mut self, pth: ast::Path, @@ -3137,6 +3144,8 @@ impl<'a> Parser<'a> { && this.token.kind == token::Semi { TrailingToken::Semi + } else if this.token.kind == token::Gt { + TrailingToken::Gt } else { // FIXME - pass this through from the place where we know // we need a comma, rather than assuming that `#[attr] expr,` |