about summary refs log tree commit diff
path: root/compiler/rustc_parse/src/parser/expr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs403
1 files changed, 168 insertions, 235 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index ae77961b7bc..11301f03e48 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1,27 +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, NotAsNegationOperatorSub, 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;
@@ -38,6 +44,7 @@ use rustc_ast::{ClosureBinder, StmtKind};
 use rustc_ast_pretty::pprust;
 use rustc_errors::IntoDiagnostic;
 use rustc_errors::{Applicability, Diagnostic, PResult};
+use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
 use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_span::source_map::{self, Span, Spanned};
@@ -421,13 +428,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.
@@ -578,21 +583,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)
@@ -755,9 +755,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,
@@ -771,33 +796,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) => {
@@ -1158,7 +1157,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)
     }
 
@@ -1196,9 +1197,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)) =>
                     {
@@ -1208,29 +1208,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();
                         }
@@ -1314,7 +1310,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
             })
@@ -1537,15 +1533,19 @@ 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(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| {
@@ -1572,28 +1572,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));
@@ -1601,7 +1588,7 @@ impl<'a> Parser<'a> {
                 self.mk_expr(span, ExprKind::Block(blk, label))
             });
 
-            err.emit();
+            self.sess.emit_err(err);
             expr
         }?;
 
@@ -1672,19 +1659,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)
@@ -1756,9 +1737,8 @@ 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;
                     }
@@ -1790,7 +1770,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(),
+                });
             }
         }
 
@@ -1818,13 +1801,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 {
@@ -1853,11 +1829,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");
@@ -1883,15 +1861,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 });
@@ -1899,38 +1874,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 });
         }
     }
 
@@ -1964,14 +1918,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))
@@ -2134,7 +2087,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)
             }
@@ -2515,39 +2469,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
@@ -2836,23 +2773,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,