about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_error_messages/locales/en-US/parser.ftl33
-rw-r--r--compiler/rustc_parse/src/errors.rs99
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs17
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs37
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs10
-rw-r--r--compiler/rustc_parse/src/parser/item.rs22
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs32
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs94
-rw-r--r--src/test/ui/let-else/let-else-brace-before-else.stderr8
9 files changed, 192 insertions, 160 deletions
diff --git a/compiler/rustc_error_messages/locales/en-US/parser.ftl b/compiler/rustc_error_messages/locales/en-US/parser.ftl
index b93c5e75913..36b03a2db77 100644
--- a/compiler/rustc_error_messages/locales/en-US/parser.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/parser.ftl
@@ -188,7 +188,8 @@ parser_parentheses_with_struct_fields = invalid `struct` delimiters or `fn` call
     .suggestion_no_fields_for_fn = if `{$name}` is a function, use the arguments directly
 
 parser_labeled_loop_in_break = parentheses are required around this expression to avoid confusion with a labeled break expression
-    .suggestion = wrap the expression in parentheses
+
+parser_sugg_wrap_expression_in_parentheses = wrap the expression in parentheses
 
 parser_array_brackets_instead_of_braces = this is a block expression, not an array
     .suggestion = to make an array, use square brackets instead of curly braces
@@ -231,3 +232,33 @@ parser_mismatched_closing_delimiter = mismatched closing delimiter: `{$delimiter
     .label_unmatched = mismatched closing delimiter
     .label_opening_candidate = closing delimiter possibly meant for this
     .label_unclosed = unclosed delimiter
+
+parser_incorrect_visibility_restriction = incorrect visibility restriction
+    .help = some possible visibility restrictions are:
+            `pub(crate)`: visible only on the current crate
+            `pub(super)`: visible only in the current module's parent
+            `pub(in path::to::module)`: visible only on the specified path
+    .suggestion = make this visible only to module `{$inner_str}` with `in`
+
+parser_assignment_else_not_allowed = <assignment> ... else {"{"} ... {"}"} is not allowed
+
+parser_expected_statement_after_outer_attr = expected statement after outer attribute
+
+parser_doc_comment_does_not_document_anything = found a documentation comment that doesn't document anything
+    .help = doc comments must come before what they document, maybe a comment was intended with `//`?
+    .suggestion = missing comma here
+
+parser_const_let_mutually_exclusive = `const` and `let` are mutually exclusive
+    .suggestion = remove `let`
+
+parser_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else`
+parser_invalid_curly_in_let_else = right curly brace `{"}"}` before `else` in a `let...else` statement not allowed
+
+parser_compound_assignment_expression_in_let = can't reassign to an uninitialized variable
+    .suggestion = initialize the variable
+    .help = if you meant to overwrite, remove the `let` binding
+
+parser_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes
+    .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
+
+parser_invalid_meta_item = expected unsuffixed literal or identifier, found `{$token}`
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index dc7a39457a0..968d0be5b42 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -624,16 +624,19 @@ pub(crate) struct LabeledLoopInBreak {
     #[primary_span]
     pub span: Span,
     #[subdiagnostic]
-    pub sub: LabeledLoopInBreakSub,
+    pub sub: WrapExpressionInParentheses,
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(parser::suggestion, applicability = "machine-applicable")]
-pub(crate) struct LabeledLoopInBreakSub {
+#[multipart_suggestion(
+    parser::sugg_wrap_expression_in_parentheses,
+    applicability = "machine-applicable"
+)]
+pub(crate) struct WrapExpressionInParentheses {
     #[suggestion_part(code = "(")]
-    pub first: Span,
+    pub left: Span,
     #[suggestion_part(code = ")")]
-    pub second: Span,
+    pub right: Span,
 }
 
 #[derive(Diagnostic)]
@@ -781,3 +784,89 @@ pub(crate) struct MismatchedClosingDelimiter {
     #[label(parser::label_unclosed)]
     pub unclosed: Option<Span>,
 }
+
+#[derive(Diagnostic)]
+#[diag(parser::incorrect_visibility_restriction, code = "E0704")]
+#[help]
+pub(crate) struct IncorrectVisibilityRestriction {
+    #[primary_span]
+    #[suggestion(code = "in {inner_str}", applicability = "machine-applicable")]
+    pub span: Span,
+    pub inner_str: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser::assignment_else_not_allowed)]
+pub(crate) struct AssignmentElseNotAllowed {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser::expected_statement_after_outer_attr)]
+pub(crate) struct ExpectedStatementAfterOuterAttr {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser::doc_comment_does_not_document_anything, code = "E0585")]
+#[help]
+pub(crate) struct DocCommentDoesNotDocumentAnything {
+    #[primary_span]
+    pub span: Span,
+    #[suggestion(code = ",", applicability = "machine-applicable")]
+    pub missing_comma: Option<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser::const_let_mutually_exclusive)]
+pub(crate) struct ConstLetMutuallyExclusive {
+    #[primary_span]
+    #[suggestion(code = "const", applicability = "maybe-incorrect")]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser::invalid_expression_in_let_else)]
+pub(crate) struct InvalidExpressionInLetElse {
+    #[primary_span]
+    pub span: Span,
+    pub operator: &'static str,
+    #[subdiagnostic]
+    pub sugg: WrapExpressionInParentheses,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser::invalid_curly_in_let_else)]
+pub(crate) struct InvalidCurlyInLetElse {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub sugg: WrapExpressionInParentheses,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser::compound_assignment_expression_in_let)]
+#[help]
+pub(crate) struct CompoundAssignmentExpressionInLet {
+    #[primary_span]
+    #[suggestion_short(code = "=", applicability = "maybe-incorrect")]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser::suffixed_literal_in_attribute)]
+#[help]
+pub(crate) struct SuffixedLiteralInAttribute {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser::invalid_meta_item)]
+pub(crate) struct InvalidMetaItem {
+    #[primary_span]
+    pub span: Span,
+    pub token: String,
+}
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 5fd69b15ecc..1966345f19f 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -1,9 +1,11 @@
+use crate::errors::{InvalidMetaItem, SuffixedLiteralInAttribute};
+
 use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle};
 use rustc_ast as ast;
 use rustc_ast::attr;
 use rustc_ast::token::{self, Delimiter, Nonterminal};
 use rustc_ast_pretty::pprust;
-use rustc_errors::{error_code, Diagnostic, PResult};
+use rustc_errors::{error_code, Diagnostic, IntoDiagnostic, PResult};
 use rustc_span::{sym, BytePos, Span};
 use std::convert::TryInto;
 
@@ -337,12 +339,7 @@ impl<'a> Parser<'a> {
         debug!("checking if {:?} is unusuffixed", lit);
 
         if !lit.kind.is_unsuffixed() {
-            self.struct_span_err(lit.span, "suffixed literals are not allowed in attributes")
-                .help(
-                    "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \
-                    use an unsuffixed version (`1`, `1.0`, etc.)",
-                )
-                .emit();
+            self.sess.emit_err(SuffixedLiteralInAttribute { span: lit.span });
         }
 
         Ok(lit)
@@ -435,9 +432,9 @@ impl<'a> Parser<'a> {
             Err(err) => err.cancel(),
         }
 
-        let found = pprust::token_to_string(&self.token);
-        let msg = format!("expected unsuffixed literal or identifier, found `{found}`");
-        Err(self.struct_span_err(self.token.span, &msg))
+        let token = pprust::token_to_string(&self.token).to_string();
+        Err(InvalidMetaItem { span: self.token.span, token }
+            .into_diagnostic(&self.sess.span_diagnostic))
     }
 }
 
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 0fdde291757..326b2553eaf 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -55,34 +55,6 @@ pub(super) fn dummy_arg(ident: Ident) -> Param {
     }
 }
 
-pub enum Error {
-    UselessDocComment,
-}
-
-impl Error {
-    fn span_err(
-        self,
-        sp: impl Into<MultiSpan>,
-        handler: &Handler,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        match self {
-            Error::UselessDocComment => {
-                let mut err = struct_span_err!(
-                    handler,
-                    sp,
-                    E0585,
-                    "found a documentation comment that doesn't document anything",
-                );
-                err.help(
-                    "doc comments must come before what they document, maybe a comment was \
-                          intended with `//`?",
-                );
-                err
-            }
-        }
-    }
-}
-
 pub(super) trait RecoverQPath: Sized + 'static {
     const PATH_STYLE: PathStyle = PathStyle::Expr;
     fn to_ty(&self) -> Option<P<Ty>>;
@@ -269,15 +241,6 @@ impl<'a> DerefMut for SnapshotParser<'a> {
 
 impl<'a> Parser<'a> {
     #[rustc_lint_diagnostics]
-    pub(super) fn span_err<S: Into<MultiSpan>>(
-        &self,
-        sp: S,
-        err: Error,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        err.span_err(sp, self.diagnostic())
-    }
-
-    #[rustc_lint_diagnostics]
     pub fn struct_span_err<S: Into<MultiSpan>>(
         &self,
         sp: S,
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 784a8ae4c2e..751951dcaae 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -17,14 +17,14 @@ use crate::errors::{
     InvalidIntLiteralWidth, InvalidInterpolatedExpression, InvalidLiteralSuffix,
     InvalidLiteralSuffixOnTupleIndex, InvalidLogicalOperator, InvalidLogicalOperatorSub,
     InvalidNumLiteralBasePrefix, InvalidNumLiteralSuffix, LabeledLoopInBreak,
-    LabeledLoopInBreakSub, LeadingPlusNotSupported, LeftArrowOperator, LifetimeInBorrowExpression,
+    LeadingPlusNotSupported, LeftArrowOperator, LifetimeInBorrowExpression,
     MacroInvocationWithQualifiedPath, MalformedLoopLabel, MatchArmBodyWithoutBraces,
     MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm, MissingInInForLoop,
     MissingInInForLoopSub, MissingSemicolonBeforeArray, NoFieldsForFnCall, NotAsNegationOperator,
     NotAsNegationOperatorSub, OctalFloatLiteralNotSupported, OuterAttributeNotAllowedOnIfElse,
     ParenthesesWithStructFields, RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric,
     StructLiteralNotAllowedHere, StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator,
-    UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg,
+    UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses,
 };
 use crate::maybe_recover_from_interpolated_ty_qpath;
 
@@ -1661,9 +1661,9 @@ impl<'a> Parser<'a> {
             let lexpr = self.parse_labeled_expr(label.take().unwrap(), true)?;
             self.sess.emit_err(LabeledLoopInBreak {
                 span: lexpr.span,
-                sub: LabeledLoopInBreakSub {
-                    first: lexpr.span.shrink_to_lo(),
-                    second: lexpr.span.shrink_to_hi(),
+                sub: WrapExpressionInParentheses {
+                    left: lexpr.span.shrink_to_lo(),
+                    right: lexpr.span.shrink_to_hi(),
                 },
             });
             Some(lexpr)
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 37d12f78397..b7454d7bfc1 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1,6 +1,6 @@
-use crate::errors::UseEmptyBlockNotSemi;
+use crate::errors::{DocCommentDoesNotDocumentAnything, UseEmptyBlockNotSemi};
 
-use super::diagnostics::{dummy_arg, ConsumeClosingDelim, Error};
+use super::diagnostics::{dummy_arg, ConsumeClosingDelim};
 use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
 use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken};
 
@@ -15,7 +15,7 @@ use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, Vari
 use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind};
 use rustc_ast::{MacArgs, MacCall, MacDelimiter};
 use rustc_ast_pretty::pprust;
-use rustc_errors::{struct_span_err, Applicability, PResult, StashKey};
+use rustc_errors::{struct_span_err, Applicability, IntoDiagnostic, PResult, StashKey};
 use rustc_span::edition::Edition;
 use rustc_span::lev_distance::lev_distance;
 use rustc_span::source_map::{self, Span};
@@ -1586,7 +1586,10 @@ impl<'a> Parser<'a> {
             token::CloseDelim(Delimiter::Brace) => {}
             token::DocComment(..) => {
                 let previous_span = self.prev_token.span;
-                let mut err = self.span_err(self.token.span, Error::UselessDocComment);
+                let mut err = DocCommentDoesNotDocumentAnything {
+                    span: self.token.span,
+                    missing_comma: None,
+                };
                 self.bump(); // consume the doc comment
                 let comma_after_doc_seen = self.eat(&token::Comma);
                 // `seen_comma` is always false, because we are inside doc block
@@ -1595,18 +1598,13 @@ impl<'a> Parser<'a> {
                     seen_comma = true;
                 }
                 if comma_after_doc_seen || self.token == token::CloseDelim(Delimiter::Brace) {
-                    err.emit();
+                    self.sess.emit_err(err);
                 } else {
                     if !seen_comma {
                         let sp = self.sess.source_map().next_point(previous_span);
-                        err.span_suggestion(
-                            sp,
-                            "missing comma here",
-                            ",",
-                            Applicability::MachineApplicable,
-                        );
+                        err.missing_comma = Some(sp);
                     }
-                    return Err(err);
+                    return Err(err.into_diagnostic(&self.sess.span_diagnostic));
                 }
             }
             _ => {
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index af6c0e34e2f..07fa909e71b 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -13,7 +13,6 @@ mod ty;
 use crate::lexer::UnmatchedBrace;
 pub use attr_wrapper::AttrWrapper;
 pub use diagnostics::AttemptLocalParseRecovery;
-use diagnostics::Error;
 pub(crate) use item::FnParseMode;
 pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
 pub use path::PathStyle;
@@ -32,8 +31,7 @@ use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::PResult;
 use rustc_errors::{
-    struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, IntoDiagnostic,
-    MultiSpan,
+    Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, IntoDiagnostic, MultiSpan,
 };
 use rustc_session::parse::ParseSess;
 use rustc_span::source_map::{Span, DUMMY_SP};
@@ -42,7 +40,10 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use std::ops::Range;
 use std::{cmp, mem, slice};
 
-use crate::errors::{MismatchedClosingDelimiter, NonStringAbiLiteral};
+use crate::errors::{
+    DocCommentDoesNotDocumentAnything, IncorrectVisibilityRestriction, MismatchedClosingDelimiter,
+    NonStringAbiLiteral,
+};
 
 bitflags::bitflags! {
     struct Restrictions: u8 {
@@ -521,9 +522,11 @@ impl<'a> Parser<'a> {
 
     fn ident_or_err(&mut self) -> PResult<'a, (Ident, /* is_raw */ bool)> {
         self.token.ident().ok_or_else(|| match self.prev_token.kind {
-            TokenKind::DocComment(..) => {
-                self.span_err(self.prev_token.span, Error::UselessDocComment)
+            TokenKind::DocComment(..) => DocCommentDoesNotDocumentAnything {
+                span: self.prev_token.span,
+                missing_comma: None,
             }
+            .into_diagnostic(&self.sess.span_diagnostic),
             _ => self.expected_ident_found(),
         })
     }
@@ -1347,23 +1350,8 @@ impl<'a> Parser<'a> {
         let path = self.parse_path(PathStyle::Mod)?;
         self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)`
 
-        let msg = "incorrect visibility restriction";
-        let suggestion = r##"some possible visibility restrictions are:
-`pub(crate)`: visible only on the current crate
-`pub(super)`: visible only in the current module's parent
-`pub(in path::to::module)`: visible only on the specified path"##;
-
         let path_str = pprust::path_to_string(&path);
-
-        struct_span_err!(self.sess.span_diagnostic, path.span, E0704, "{}", msg)
-            .help(suggestion)
-            .span_suggestion(
-                path.span,
-                &format!("make this visible only to module `{}` with `in`", path_str),
-                format!("in {}", path_str),
-                Applicability::MachineApplicable,
-            )
-            .emit();
+        self.sess.emit_err(IncorrectVisibilityRestriction { span: path.span, inner_str: path_str });
 
         Ok(())
     }
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 03ced75d763..155173f120c 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -1,5 +1,5 @@
 use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN;
-use super::diagnostics::{AttemptLocalParseRecovery, Error};
+use super::diagnostics::AttemptLocalParseRecovery;
 use super::expr::LhsExpr;
 use super::pat::RecoverComma;
 use super::path::PathStyle;
@@ -7,7 +7,12 @@ use super::TrailingToken;
 use super::{
     AttrWrapper, BlockMode, FnParseMode, ForceCollect, Parser, Restrictions, SemiColonMode,
 };
-use crate::errors::{InvalidVariableDeclaration, InvalidVariableDeclarationSub};
+use crate::errors::{
+    AssignmentElseNotAllowed, CompoundAssignmentExpressionInLet, ConstLetMutuallyExclusive,
+    DocCommentDoesNotDocumentAnything, ExpectedStatementAfterOuterAttr, InvalidCurlyInLetElse,
+    InvalidExpressionInLetElse, InvalidVariableDeclaration, InvalidVariableDeclarationSub,
+    WrapExpressionInParentheses,
+};
 use crate::maybe_whole;
 
 use rustc_ast as ast;
@@ -111,11 +116,7 @@ impl<'a> Parser<'a> {
                 let bl = self.parse_block()?;
                 // Destructuring assignment ... else.
                 // This is not allowed, but point it out in a nice way.
-                let mut err = self.struct_span_err(
-                    e.span.to(bl.span),
-                    "<assignment> ... else { ... } is not allowed",
-                );
-                err.emit();
+                self.sess.emit_err(AssignmentElseNotAllowed { span: e.span.to(bl.span) });
             }
             self.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
         } else {
@@ -201,9 +202,12 @@ impl<'a> Parser<'a> {
     fn error_outer_attrs(&self, attrs: &[Attribute]) {
         if let [.., last] = attrs {
             if last.is_doc_comment() {
-                self.span_err(last.span, Error::UselessDocComment).emit();
+                self.sess.emit_err(DocCommentDoesNotDocumentAnything {
+                    span: last.span,
+                    missing_comma: None,
+                });
             } else if attrs.iter().any(|a| a.style == AttrStyle::Outer) {
-                self.struct_span_err(last.span, "expected statement after outer attribute").emit();
+                self.sess.emit_err(ExpectedStatementAfterOuterAttr { span: last.span });
             }
         }
     }
@@ -254,17 +258,7 @@ impl<'a> Parser<'a> {
         let lo = self.prev_token.span;
 
         if self.token.is_keyword(kw::Const) && self.look_ahead(1, |t| t.is_ident()) {
-            self.struct_span_err(
-                lo.to(self.token.span),
-                "`const` and `let` are mutually exclusive",
-            )
-            .span_suggestion(
-                lo.to(self.token.span),
-                "remove `let`",
-                "const",
-                Applicability::MaybeIncorrect,
-            )
-            .emit();
+            self.sess.emit_err(ConstLetMutuallyExclusive { span: lo.to(self.token.span) });
             self.bump();
         }
 
@@ -362,44 +356,27 @@ impl<'a> Parser<'a> {
     fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
         if let ast::ExprKind::Binary(op, ..) = init.kind {
             if op.node.lazy() {
-                let suggs = vec![
-                    (init.span.shrink_to_lo(), "(".to_string()),
-                    (init.span.shrink_to_hi(), ")".to_string()),
-                ];
-                self.struct_span_err(
-                    init.span,
-                    &format!(
-                        "a `{}` expression cannot be directly assigned in `let...else`",
-                        op.node.to_string()
-                    ),
-                )
-                .multipart_suggestion(
-                    "wrap the expression in parentheses",
-                    suggs,
-                    Applicability::MachineApplicable,
-                )
-                .emit();
+                self.sess.emit_err(InvalidExpressionInLetElse {
+                    span: init.span,
+                    operator: op.node.to_string(),
+                    sugg: WrapExpressionInParentheses {
+                        left: init.span.shrink_to_lo(),
+                        right: init.span.shrink_to_hi(),
+                    },
+                });
             }
         }
     }
 
     fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
         if let Some(trailing) = classify::expr_trailing_brace(init) {
-            let err_span = trailing.span.with_lo(trailing.span.hi() - BytePos(1));
-            let suggs = vec![
-                (trailing.span.shrink_to_lo(), "(".to_string()),
-                (trailing.span.shrink_to_hi(), ")".to_string()),
-            ];
-            self.struct_span_err(
-                err_span,
-                "right curly brace `}` before `else` in a `let...else` statement not allowed",
-            )
-            .multipart_suggestion(
-                "try wrapping the expression in parentheses",
-                suggs,
-                Applicability::MachineApplicable,
-            )
-            .emit();
+            self.sess.emit_err(InvalidCurlyInLetElse {
+                span: trailing.span.with_lo(trailing.span.hi() - BytePos(1)),
+                sugg: WrapExpressionInParentheses {
+                    left: trailing.span.shrink_to_lo(),
+                    right: trailing.span.shrink_to_hi(),
+                },
+            });
         }
     }
 
@@ -408,18 +385,7 @@ impl<'a> Parser<'a> {
         let eq_consumed = match self.token.kind {
             token::BinOpEq(..) => {
                 // Recover `let x <op>= 1` as `let x = 1`
-                self.struct_span_err(
-                    self.token.span,
-                    "can't reassign to an uninitialized variable",
-                )
-                .span_suggestion_short(
-                    self.token.span,
-                    "initialize the variable",
-                    "=",
-                    Applicability::MaybeIncorrect,
-                )
-                .help("if you meant to overwrite, remove the `let` binding")
-                .emit();
+                self.sess.emit_err(CompoundAssignmentExpressionInLet { span: self.token.span });
                 self.bump();
                 true
             }
diff --git a/src/test/ui/let-else/let-else-brace-before-else.stderr b/src/test/ui/let-else/let-else-brace-before-else.stderr
index 51051bbd4d8..cb01e4c18a1 100644
--- a/src/test/ui/let-else/let-else-brace-before-else.stderr
+++ b/src/test/ui/let-else/let-else-brace-before-else.stderr
@@ -4,7 +4,7 @@ error: right curly brace `}` before `else` in a `let...else` statement not allow
 LL |     let Some(1) = { Some(1) } else {
    |                             ^
    |
-help: try wrapping the expression in parentheses
+help: wrap the expression in parentheses
    |
 LL |     let Some(1) = ({ Some(1) }) else {
    |                   +           +
@@ -15,7 +15,7 @@ error: right curly brace `}` before `else` in a `let...else` statement not allow
 LL |     let Some(1) = loop { break Some(1) } else {
    |                                        ^
    |
-help: try wrapping the expression in parentheses
+help: wrap the expression in parentheses
    |
 LL |     let Some(1) = (loop { break Some(1) }) else {
    |                   +                      +
@@ -26,7 +26,7 @@ error: right curly brace `}` before `else` in a `let...else` statement not allow
 LL |     let 2 = 1 + match 1 { n => n } else {
    |                                  ^
    |
-help: try wrapping the expression in parentheses
+help: wrap the expression in parentheses
    |
 LL |     let 2 = 1 + (match 1 { n => n }) else {
    |                 +                  +
@@ -37,7 +37,7 @@ error: right curly brace `}` before `else` in a `let...else` statement not allow
 LL |     let Some(1) = unsafe { unsafe_fn() } else {
    |                                        ^
    |
-help: try wrapping the expression in parentheses
+help: wrap the expression in parentheses
    |
 LL |     let Some(1) = (unsafe { unsafe_fn() }) else {
    |                   +                      +