about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_error_messages/locales/en-US/mir_build.ftl61
-rw-r--r--compiler/rustc_middle/src/thir.rs7
-rw-r--r--compiler/rustc_mir_build/src/errors.rs226
-rw-r--r--compiler/rustc_mir_build/src/lib.rs1
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs191
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs188
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs30
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/usefulness.rs42
-rw-r--r--tests/ui/consts/const-match-check.eval1.stderr2
-rw-r--r--tests/ui/consts/const-match-check.eval2.stderr2
-rw-r--r--tests/ui/consts/const-match-check.matchck.stderr8
-rw-r--r--tests/ui/consts/const-pattern-irrefutable.rs18
-rw-r--r--tests/ui/consts/const-pattern-irrefutable.stderr19
-rw-r--r--tests/ui/consts/const_let_refutable.stderr2
-rw-r--r--tests/ui/empty/empty-never-array.rs3
-rw-r--r--tests/ui/empty/empty-never-array.stderr14
-rw-r--r--tests/ui/error-codes/E0005.stderr13
-rw-r--r--tests/ui/error-codes/E0297.stderr7
-rw-r--r--tests/ui/feature-gates/feature-gate-exhaustive-patterns.stderr13
-rw-r--r--tests/ui/for/for-loop-refutable-pattern-error-message.stderr2
-rw-r--r--tests/ui/issues/issue-15381.rs3
-rw-r--r--tests/ui/issues/issue-15381.stderr2
-rw-r--r--tests/ui/never_type/exhaustive_patterns.stderr8
-rw-r--r--tests/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr2
-rw-r--r--tests/ui/pattern/usefulness/issue-31561.rs3
-rw-r--r--tests/ui/pattern/usefulness/issue-31561.stderr16
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs38
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr58
-rw-r--r--tests/ui/pattern/usefulness/refutable-pattern-errors.rs6
-rw-r--r--tests/ui/pattern/usefulness/refutable-pattern-errors.stderr6
-rw-r--r--tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs3
-rw-r--r--tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr2
-rw-r--r--tests/ui/recursion/recursive-types-are-not-uninhabited.stderr13
-rw-r--r--tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs5
-rw-r--r--tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr5
-rw-r--r--tests/ui/uninhabited/uninhabited-irrefutable.rs4
-rw-r--r--tests/ui/uninhabited/uninhabited-irrefutable.stderr14
-rw-r--r--tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr13
39 files changed, 566 insertions, 486 deletions
diff --git a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl
index aacaafeede6..a082c0b61fa 100644
--- a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl
@@ -303,3 +303,64 @@ mir_build_multiple_mut_borrows = cannot borrow value as mutable more than once a
     .mutable_borrow = another mutable borrow, by `{$name_mut}`, occurs here
     .immutable_borrow = also borrowed as immutable, by `{$name_immut}`, here
     .moved = also moved into `{$name_moved}` here
+
+mir_build_union_pattern = cannot use unions in constant patterns
+
+mir_build_type_not_structural =
+     to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]`
+
+mir_build_unsized_pattern = cannot use unsized non-slice type `{$non_sm_ty}` in constant patterns
+
+mir_build_invalid_pattern = `{$non_sm_ty}` cannot be used in patterns
+
+mir_build_float_pattern = floating-point types cannot be used in patterns
+
+mir_build_pointer_pattern = function pointers and unsized pointers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details.
+
+mir_build_indirect_structural_match =
+    to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]`
+
+mir_build_nontrivial_structural_match =
+    to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]`
+
+mir_build_overlapping_range_endpoints = multiple patterns overlap on their endpoints
+    .range = ... with this range
+    .note = you likely meant to write mutually exclusive ranges
+
+mir_build_non_exhaustive_omitted_pattern = some variants are not matched explicitly
+    .help = ensure that all variants are matched explicitly by adding the suggested match arms
+    .note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found
+
+mir_build_uncovered = {$count ->
+        [1] pattern `{$witness_1}`
+        [2] patterns `{$witness_1}` and `{$witness_2}`
+        [3] patterns `{$witness_1}`, `{$witness_2}` and `{$witness_3}`
+        *[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and {$remainder} more
+    } not covered
+
+mir_build_pattern_not_covered = refutable pattern in {$origin}
+    .pattern_ty = the matched value is of type `{$pattern_ty}`
+
+mir_build_inform_irrefutable = `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+
+mir_build_more_information = for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+
+mir_build_res_defined_here = {$res} defined here
+
+mir_build_adt_defined_here = `{$ty}` defined here
+
+mir_build_variant_defined_here = not covered
+
+mir_build_interpreted_as_const = introduce a variable instead
+
+mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as {$article} {$res} pattern, not a new variable
+
+mir_build_suggest_if_let = you might want to use `if let` to ignore the {$count ->
+        [one] variant that isn't
+        *[other] variants that aren't
+    } matched
+
+mir_build_suggest_let_else = you might want to use `let else` to handle the {$count ->
+        [one] variant that isn't
+        *[other] variants that aren't
+    } matched
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index ac903010c8d..5f320708c84 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -9,6 +9,7 @@
 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/thir.html
 
 use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
+use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::RangeEnd;
@@ -575,6 +576,12 @@ impl<'tcx> Pat<'tcx> {
     }
 }
 
+impl<'tcx> IntoDiagnosticArg for Pat<'tcx> {
+    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+        format!("{}", self).into_diagnostic_arg()
+    }
+}
+
 #[derive(Clone, Debug, HashStable)]
 pub struct Ascription<'tcx> {
     pub annotation: CanonicalUserTypeAnnotation<'tcx>,
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 233eecbd5b4..06523b0a1de 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -1,9 +1,13 @@
+use crate::thir::pattern::deconstruct_pat::DeconstructedPat;
 use crate::thir::pattern::MatchCheckCtxt;
 use rustc_errors::Handler;
 use rustc_errors::{
-    error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, MultiSpan,
+    error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+    IntoDiagnostic, MultiSpan, SubdiagnosticMessage,
 };
+use rustc_hir::def::Res;
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
+use rustc_middle::thir::Pat;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::{symbol::Ident, Span};
 
@@ -624,3 +628,223 @@ pub enum MultipleMutBorrowOccurence {
         name_moved: Ident,
     },
 }
+
+#[derive(Diagnostic)]
+#[diag(mir_build_union_pattern)]
+pub struct UnionPattern {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_type_not_structural)]
+pub struct TypeNotStructural<'tcx> {
+    #[primary_span]
+    pub span: Span,
+    pub non_sm_ty: Ty<'tcx>,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_invalid_pattern)]
+pub struct InvalidPattern<'tcx> {
+    #[primary_span]
+    pub span: Span,
+    pub non_sm_ty: Ty<'tcx>,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_unsized_pattern)]
+pub struct UnsizedPattern<'tcx> {
+    #[primary_span]
+    pub span: Span,
+    pub non_sm_ty: Ty<'tcx>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_float_pattern)]
+pub struct FloatPattern;
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_pointer_pattern)]
+pub struct PointerPattern;
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_indirect_structural_match)]
+pub struct IndirectStructuralMatch<'tcx> {
+    pub non_sm_ty: Ty<'tcx>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_nontrivial_structural_match)]
+pub struct NontrivialStructuralMatch<'tcx> {
+    pub non_sm_ty: Ty<'tcx>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_overlapping_range_endpoints)]
+#[note]
+pub struct OverlappingRangeEndpoints<'tcx> {
+    #[label(range)]
+    pub range: Span,
+    #[subdiagnostic]
+    pub overlap: Vec<Overlap<'tcx>>,
+}
+
+pub struct Overlap<'tcx> {
+    pub span: Span,
+    pub range: Pat<'tcx>,
+}
+
+impl<'tcx> AddToDiagnostic for Overlap<'tcx> {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
+        let Overlap { span, range } = self;
+
+        // FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]`
+        // does not support `#[subdiagnostic(eager)]`...
+        let message = format!("this range overlaps on `{range}`...");
+        diag.span_label(span, message);
+    }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_non_exhaustive_omitted_pattern)]
+#[help]
+#[note]
+pub(crate) struct NonExhaustiveOmittedPattern<'tcx> {
+    pub scrut_ty: Ty<'tcx>,
+    #[subdiagnostic]
+    pub uncovered: Uncovered<'tcx>,
+}
+
+#[derive(Subdiagnostic)]
+#[label(mir_build_uncovered)]
+pub(crate) struct Uncovered<'tcx> {
+    #[primary_span]
+    span: Span,
+    count: usize,
+    witness_1: Pat<'tcx>,
+    witness_2: Pat<'tcx>,
+    witness_3: Pat<'tcx>,
+    remainder: usize,
+}
+
+impl<'tcx> Uncovered<'tcx> {
+    pub fn new<'p>(
+        span: Span,
+        cx: &MatchCheckCtxt<'p, 'tcx>,
+        witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
+    ) -> Self {
+        let witness_1 = witnesses.get(0).unwrap().to_pat(cx);
+        Self {
+            span,
+            count: witnesses.len(),
+            // Substitute dummy values if witnesses is smaller than 3. These will never be read.
+            witness_2: witnesses.get(1).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()),
+            witness_3: witnesses.get(2).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()),
+            witness_1,
+            remainder: witnesses.len().saturating_sub(3),
+        }
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_pattern_not_covered, code = "E0005")]
+pub(crate) struct PatternNotCovered<'s, 'tcx> {
+    #[primary_span]
+    pub span: Span,
+    pub origin: &'s str,
+    #[subdiagnostic]
+    pub uncovered: Uncovered<'tcx>,
+    #[subdiagnostic]
+    pub inform: Option<Inform>,
+    #[subdiagnostic]
+    pub interpreted_as_const: Option<InterpretedAsConst>,
+    #[subdiagnostic]
+    pub adt_defined_here: Option<AdtDefinedHere<'tcx>>,
+    #[note(pattern_ty)]
+    pub _p: (),
+    pub pattern_ty: Ty<'tcx>,
+    #[subdiagnostic]
+    pub let_suggestion: Option<SuggestLet>,
+    #[subdiagnostic]
+    pub res_defined_here: Option<ResDefinedHere>,
+}
+
+#[derive(Subdiagnostic)]
+#[note(mir_build_inform_irrefutable)]
+#[note(mir_build_more_information)]
+pub struct Inform;
+
+pub struct AdtDefinedHere<'tcx> {
+    pub adt_def_span: Span,
+    pub ty: Ty<'tcx>,
+    pub variants: Vec<Variant>,
+}
+
+pub struct Variant {
+    pub span: Span,
+}
+
+impl<'tcx> AddToDiagnostic for AdtDefinedHere<'tcx> {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
+        diag.set_arg("ty", self.ty);
+        let mut spans = MultiSpan::from(self.adt_def_span);
+
+        for Variant { span } in self.variants {
+            spans.push_span_label(span, rustc_errors::fluent::mir_build_variant_defined_here);
+        }
+
+        diag.span_note(spans, rustc_errors::fluent::mir_build_adt_defined_here);
+    }
+}
+
+#[derive(Subdiagnostic)]
+#[label(mir_build_res_defined_here)]
+pub struct ResDefinedHere {
+    #[primary_span]
+    pub def_span: Span,
+    pub res: Res,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    mir_build_interpreted_as_const,
+    code = "{variable}_var",
+    applicability = "maybe-incorrect"
+)]
+#[label(mir_build_confused)]
+pub struct InterpretedAsConst {
+    #[primary_span]
+    pub span: Span,
+    pub article: &'static str,
+    pub variable: String,
+    pub res: Res,
+}
+
+#[derive(Subdiagnostic)]
+pub enum SuggestLet {
+    #[multipart_suggestion(mir_build_suggest_if_let, applicability = "has-placeholders")]
+    If {
+        #[suggestion_part(code = "if ")]
+        start_span: Span,
+        #[suggestion_part(code = " {{ todo!() }}")]
+        semi_span: Span,
+        count: usize,
+    },
+    #[suggestion(
+        mir_build_suggest_let_else,
+        code = " else {{ todo!() }}",
+        applicability = "has-placeholders"
+    )]
+    Else {
+        #[primary_span]
+        end_span: Span,
+        count: usize,
+    },
+}
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index 2b05e92fdcf..fb7ae6f1d24 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -10,6 +10,7 @@
 #![feature(let_chains)]
 #![feature(min_specialization)]
 #![feature(once_cell)]
+#![feature(try_blocks)]
 #![recursion_limit = "256"]
 
 #[macro_use]
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index e7ee0d9e908..e13c0662ef8 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -9,8 +9,7 @@ use crate::errors::*;
 use rustc_arena::TypedArena;
 use rustc_ast::Mutability;
 use rustc_errors::{
-    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
-    MultiSpan,
+    struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
 };
 use rustc_hir as hir;
 use rustc_hir::def::*;
@@ -378,8 +377,8 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
 
         let pattern = self.lower_pattern(&mut cx, pat, &mut false);
         let pattern_ty = pattern.ty();
-        let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }];
-        let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty);
+        let arm = MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false };
+        let report = compute_match_usefulness(&cx, &[arm], pat.hir_id, pattern_ty);
 
         // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
         // only care about exhaustiveness here.
@@ -390,145 +389,73 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
             return;
         }
 
-        let joined_patterns = joined_uncovered_patterns(&cx, &witnesses);
-
-        let mut bindings = vec![];
-
-        let mut err = struct_span_err!(
-            self.tcx.sess,
-            pat.span,
-            E0005,
-            "refutable pattern in {}: {} not covered",
-            origin,
-            joined_patterns
-        );
-        let suggest_if_let = match &pat.kind {
-            hir::PatKind::Path(hir::QPath::Resolved(None, path))
-                if path.segments.len() == 1 && path.segments[0].args.is_none() =>
+        let (inform, interpreted_as_const, res_defined_here,let_suggestion) =
+            if let hir::PatKind::Path(hir::QPath::Resolved(
+                None,
+                hir::Path {
+                    segments: &[hir::PathSegment { args: None, res, ident, .. }],
+                    ..
+                },
+            )) = &pat.kind
             {
-                const_not_var(&mut err, cx.tcx, pat, path);
-                false
-            }
-            _ => {
-                pat.walk(&mut |pat: &hir::Pat<'_>| {
-                    match pat.kind {
-                        hir::PatKind::Binding(_, _, ident, _) => {
-                            bindings.push(ident);
+                (
+                    None,
+                    Some(InterpretedAsConst {
+                        span: pat.span,
+                        article: res.article(),
+                        variable: ident.to_string().to_lowercase(),
+                        res,
+                    }),
+                    try {
+                        ResDefinedHere {
+                            def_span: cx.tcx.hir().res_span(res)?,
+                            res,
                         }
-                        _ => {}
+                    },
+                    None,
+                )
+            } else if let Some(span) = sp && self.tcx.sess.source_map().is_span_accessible(span) {
+                let mut bindings = vec![];
+                pat.walk_always(&mut |pat: &hir::Pat<'_>| {
+                    if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
+                        bindings.push(ident);
                     }
-                    true
                 });
-
-                err.span_label(pat.span, pattern_not_covered_label(&witnesses, &joined_patterns));
-                true
-            }
-        };
-
-        if let (Some(span), true) = (sp, suggest_if_let) {
-            err.note(
-                "`let` bindings require an \"irrefutable pattern\", like a `struct` or \
-                 an `enum` with only one variant",
-            );
-            if self.tcx.sess.source_map().is_span_accessible(span) {
                 let semi_span = span.shrink_to_hi().with_lo(span.hi() - BytePos(1));
                 let start_span = span.shrink_to_lo();
                 let end_span = semi_span.shrink_to_lo();
-                err.multipart_suggestion(
-                    &format!(
-                        "you might want to use `if let` to ignore the variant{} that {} matched",
-                        pluralize!(witnesses.len()),
-                        match witnesses.len() {
-                            1 => "isn't",
-                            _ => "aren't",
-                        },
-                    ),
-                    vec![
-                        match &bindings[..] {
-                            [] => (start_span, "if ".to_string()),
-                            [binding] => (start_span, format!("let {} = if ", binding)),
-                            bindings => (
-                                start_span,
-                                format!(
-                                    "let ({}) = if ",
-                                    bindings
-                                        .iter()
-                                        .map(|ident| ident.to_string())
-                                        .collect::<Vec<_>>()
-                                        .join(", ")
-                                ),
-                            ),
-                        },
-                        match &bindings[..] {
-                            [] => (semi_span, " { todo!() }".to_string()),
-                            [binding] => {
-                                (end_span, format!(" {{ {} }} else {{ todo!() }}", binding))
-                            }
-                            bindings => (
-                                end_span,
-                                format!(
-                                    " {{ ({}) }} else {{ todo!() }}",
-                                    bindings
-                                        .iter()
-                                        .map(|ident| ident.to_string())
-                                        .collect::<Vec<_>>()
-                                        .join(", ")
-                                ),
-                            ),
-                        },
-                    ],
-                    Applicability::HasPlaceholders,
-                );
-                if !bindings.is_empty() {
-                    err.span_suggestion_verbose(
-                        semi_span.shrink_to_lo(),
-                        &format!(
-                            "alternatively, you might want to use \
-                             let else to handle the variant{} that {} matched",
-                            pluralize!(witnesses.len()),
-                            match witnesses.len() {
-                                1 => "isn't",
-                                _ => "aren't",
-                            },
-                        ),
-                        " else { todo!() }",
-                        Applicability::HasPlaceholders,
-                    );
-                }
-            }
-            err.note(
-                "for more information, visit \
-                 https://doc.rust-lang.org/book/ch18-02-refutability.html",
-            );
-        }
+                let count = witnesses.len();
 
-        adt_defined_here(&cx, &mut err, pattern_ty, &witnesses);
-        err.note(&format!("the matched value is of type `{}`", pattern_ty));
-        err.emit();
-    }
-}
+                let let_suggestion = if bindings.is_empty() {SuggestLet::If{start_span, semi_span, count}} else{ SuggestLet::Else{end_span, count }};
+                (sp.map(|_|Inform), None, None, Some(let_suggestion))
+            } else{
+                (sp.map(|_|Inform), None, None,  None)
+            };
 
-/// A path pattern was interpreted as a constant, not a new variable.
-/// This caused an irrefutable match failure in e.g. `let`.
-fn const_not_var(err: &mut Diagnostic, tcx: TyCtxt<'_>, pat: &Pat<'_>, path: &hir::Path<'_>) {
-    let descr = path.res.descr();
-    err.span_label(
-        pat.span,
-        format!("interpreted as {} {} pattern, not a new variable", path.res.article(), descr,),
-    );
+        let adt_defined_here = try {
+            let ty = pattern_ty.peel_refs();
+            let ty::Adt(def, _) = ty.kind() else { None? };
+            let adt_def_span = cx.tcx.hir().get_if_local(def.did())?.ident()?.span;
+            let mut variants = vec![];
 
-    err.span_suggestion(
-        pat.span,
-        "introduce a variable instead",
-        format!("{}_var", path.segments[0].ident).to_lowercase(),
-        // Cannot use `MachineApplicable` as it's not really *always* correct
-        // because there may be such an identifier in scope or the user maybe
-        // really wanted to match against the constant. This is quite unlikely however.
-        Applicability::MaybeIncorrect,
-    );
+            for span in maybe_point_at_variant(&cx, *def, witnesses.iter().take(5)) {
+                variants.push(Variant { span });
+            }
+            AdtDefinedHere { adt_def_span, ty, variants }
+        };
 
-    if let Some(span) = tcx.hir().res_span(path.res) {
-        err.span_label(span, format!("{} defined here", descr));
+        self.tcx.sess.emit_err(PatternNotCovered {
+            span: pat.span,
+            origin,
+            uncovered: Uncovered::new(pat.span, &cx, witnesses),
+            inform,
+            interpreted_as_const,
+            _p: (),
+            pattern_ty,
+            let_suggestion,
+            res_defined_here,
+            adt_defined_here,
+        });
     }
 }
 
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index 6470efab2e9..7f3519945c3 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -1,11 +1,9 @@
-use rustc_errors::DelayDm;
 use rustc_hir as hir;
 use rustc_index::vec::Idx;
 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
 use rustc_middle::mir::{self, Field};
 use rustc_middle::thir::{FieldPat, Pat, PatKind};
-use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::lint;
 use rustc_span::Span;
 use rustc_trait_selection::traits::predicate_for_trait_def;
@@ -15,6 +13,10 @@ use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation};
 use std::cell::Cell;
 
 use super::PatCtxt;
+use crate::errors::{
+    FloatPattern, IndirectStructuralMatch, InvalidPattern, NontrivialStructuralMatch,
+    PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern,
+};
 
 impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
     /// Converts an evaluated constant to a pattern (if possible).
@@ -105,47 +107,6 @@ impl<'tcx> ConstToPat<'tcx> {
         self.infcx.tcx
     }
 
-    fn adt_derive_msg(&self, adt_def: AdtDef<'tcx>) -> String {
-        let path = self.tcx().def_path_str(adt_def.did());
-        format!(
-            "to use a constant of type `{}` in a pattern, \
-            `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
-            path, path,
-        )
-    }
-
-    fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> {
-        traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| {
-            with_no_trimmed_paths!(match non_sm_ty.kind() {
-                ty::Adt(adt, _) => self.adt_derive_msg(*adt),
-                ty::Dynamic(..) => {
-                    "trait objects cannot be used in patterns".to_string()
-                }
-                ty::Alias(ty::Opaque, ..) => {
-                    "opaque types cannot be used in patterns".to_string()
-                }
-                ty::Closure(..) => {
-                    "closures cannot be used in patterns".to_string()
-                }
-                ty::Generator(..) | ty::GeneratorWitness(..) => {
-                    "generators cannot be used in patterns".to_string()
-                }
-                ty::Float(..) => {
-                    "floating-point numbers cannot be used in patterns".to_string()
-                }
-                ty::FnPtr(..) => {
-                    "function pointers cannot be used in patterns".to_string()
-                }
-                ty::RawPtr(..) => {
-                    "raw pointers cannot be used in patterns".to_string()
-                }
-                _ => {
-                    bug!("use of a value of `{non_sm_ty}` inside a pattern")
-                }
-            })
-        })
-    }
-
     fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
         ty.is_structural_eq_shallow(self.infcx.tcx)
     }
@@ -176,7 +137,8 @@ impl<'tcx> ConstToPat<'tcx> {
             // If we were able to successfully convert the const to some pat,
             // double-check that all types in the const implement `Structural`.
 
-            let structural = self.search_for_structural_match_violation(cv.ty());
+            let structural =
+                traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty());
             debug!(
                 "search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
                 cv.ty(),
@@ -194,17 +156,18 @@ impl<'tcx> ConstToPat<'tcx> {
                 return inlined_const_as_pat;
             }
 
-            if let Some(msg) = structural {
+            if let Some(non_sm_ty) = structural {
                 if !self.type_may_have_partial_eq_impl(cv.ty()) {
-                    // span_fatal avoids ICE from resolution of non-existent method (rare case).
-                    self.tcx().sess.span_fatal(self.span, &msg);
+                    // fatal avoids ICE from resolution of non-existent method (rare case).
+                    self.tcx()
+                        .sess
+                        .emit_fatal(TypeNotStructural { span: self.span, non_sm_ty: non_sm_ty });
                 } else if mir_structural_match_violation && !self.saw_const_match_lint.get() {
-                    self.tcx().struct_span_lint_hir(
+                    self.tcx().emit_spanned_lint(
                         lint::builtin::INDIRECT_STRUCTURAL_MATCH,
                         self.id,
                         self.span,
-                        msg,
-                        |lint| lint,
+                        IndirectStructuralMatch { non_sm_ty },
                     );
                 } else {
                     debug!(
@@ -278,12 +241,11 @@ impl<'tcx> ConstToPat<'tcx> {
         let kind = match cv.ty().kind() {
             ty::Float(_) => {
                 if self.include_lint_checks {
-                    tcx.struct_span_lint_hir(
+                    tcx.emit_spanned_lint(
                         lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
                         id,
                         span,
-                        "floating-point types cannot be used in patterns",
-                        |lint| lint,
+                        FloatPattern,
                     );
                 }
                 PatKind::Constant { value: cv }
@@ -291,29 +253,22 @@ impl<'tcx> ConstToPat<'tcx> {
             ty::Adt(adt_def, _) if adt_def.is_union() => {
                 // Matching on union fields is unsafe, we can't hide it in constants
                 self.saw_const_match_error.set(true);
-                let msg = "cannot use unions in constant patterns";
-                if self.include_lint_checks {
-                    tcx.sess.span_err(span, msg);
-                } else {
-                    tcx.sess.delay_span_bug(span, msg);
-                }
+                let err = UnionPattern { span };
+                tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
                 PatKind::Wild
             }
             ty::Adt(..)
                 if !self.type_may_have_partial_eq_impl(cv.ty())
                     // FIXME(#73448): Find a way to bring const qualification into parity with
                     // `search_for_structural_match_violation` and then remove this condition.
-                    && self.search_for_structural_match_violation(cv.ty()).is_some() =>
+
+                    // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
+                    // could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
+                    && let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty()) =>
             {
-                // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
-                // could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
-                let msg = self.search_for_structural_match_violation(cv.ty()).unwrap();
                 self.saw_const_match_error.set(true);
-                if self.include_lint_checks {
-                    tcx.sess.span_err(self.span, &msg);
-                } else {
-                    tcx.sess.delay_span_bug(self.span, &msg);
-                }
+                let err = TypeNotStructural { span, non_sm_ty };
+                tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
                 PatKind::Wild
             }
             // If the type is not structurally comparable, just emit the constant directly,
@@ -331,19 +286,11 @@ impl<'tcx> ConstToPat<'tcx> {
                     && !self.saw_const_match_lint.get()
                 {
                     self.saw_const_match_lint.set(true);
-                    tcx.struct_span_lint_hir(
+                    tcx.emit_spanned_lint(
                         lint::builtin::INDIRECT_STRUCTURAL_MATCH,
                         id,
                         span,
-                        DelayDm(|| {
-                            format!(
-                                "to use a constant of type `{}` in a pattern, \
-                                 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
-                                cv.ty(),
-                                cv.ty(),
-                            )
-                        }),
-                        |lint| lint,
+                        IndirectStructuralMatch { non_sm_ty: cv.ty() },
                     );
                 }
                 // Since we are behind a reference, we can just bubble the error up so we get a
@@ -357,18 +304,9 @@ impl<'tcx> ConstToPat<'tcx> {
                     adt_def,
                     cv.ty()
                 );
-                let path = tcx.def_path_str(adt_def.did());
-                let msg = format!(
-                    "to use a constant of type `{}` in a pattern, \
-                     `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
-                    path, path,
-                );
                 self.saw_const_match_error.set(true);
-                if self.include_lint_checks {
-                    tcx.sess.span_err(span, &msg);
-                } else {
-                    tcx.sess.delay_span_bug(span, &msg);
-                }
+                let err = TypeNotStructural { span, non_sm_ty: cv.ty() };
+                tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
                 PatKind::Wild
             }
             ty::Adt(adt_def, substs) if adt_def.is_enum() => {
@@ -401,12 +339,8 @@ impl<'tcx> ConstToPat<'tcx> {
                 // These are not allowed and will error elsewhere anyway.
                 ty::Dynamic(..) => {
                     self.saw_const_match_error.set(true);
-                    let msg = format!("`{}` cannot be used in patterns", cv.ty());
-                    if self.include_lint_checks {
-                        tcx.sess.span_err(span, &msg);
-                    } else {
-                        tcx.sess.delay_span_bug(span, &msg);
-                    }
+                    let err = InvalidPattern { span, non_sm_ty: cv.ty() };
+                    tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
                     PatKind::Wild
                 }
                 // `&str` is represented as `ConstValue::Slice`, let's keep using this
@@ -471,32 +405,26 @@ impl<'tcx> ConstToPat<'tcx> {
                 // this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a
                 // reference. This makes the rest of the matching logic simpler as it doesn't have
                 // to figure out how to get a reference again.
-                ty::Adt(adt_def, _) if !self.type_marked_structural(*pointee_ty) => {
+                ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => {
                     if self.behind_reference.get() {
                         if self.include_lint_checks
                             && !self.saw_const_match_error.get()
                             && !self.saw_const_match_lint.get()
                         {
-                            self.saw_const_match_lint.set(true);
-                            let msg = self.adt_derive_msg(adt_def);
-                            self.tcx().struct_span_lint_hir(
+                           self.saw_const_match_lint.set(true);
+                           tcx.emit_spanned_lint(
                                 lint::builtin::INDIRECT_STRUCTURAL_MATCH,
                                 self.id,
-                                self.span,
-                                msg,
-                                |lint| lint,
+                                span,
+                                IndirectStructuralMatch { non_sm_ty: *pointee_ty },
                             );
                         }
                         PatKind::Constant { value: cv }
                     } else {
                         if !self.saw_const_match_error.get() {
                             self.saw_const_match_error.set(true);
-                            let msg = self.adt_derive_msg(adt_def);
-                            if self.include_lint_checks {
-                                tcx.sess.span_err(span, &msg);
-                            } else {
-                                tcx.sess.delay_span_bug(span, &msg);
-                            }
+                            let err = TypeNotStructural { span, non_sm_ty: *pointee_ty };
+                            tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
                         }
                         PatKind::Wild
                     }
@@ -508,12 +436,10 @@ impl<'tcx> ConstToPat<'tcx> {
                     if !pointee_ty.is_sized(tcx, param_env) {
                         // `tcx.deref_mir_constant()` below will ICE with an unsized type
                         // (except slices, which are handled in a separate arm above).
-                        let msg = format!("cannot use unsized non-slice type `{}` in constant patterns", pointee_ty);
-                        if self.include_lint_checks {
-                            tcx.sess.span_err(span, &msg);
-                        } else {
-                            tcx.sess.delay_span_bug(span, &msg);
-                        }
+
+                        let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
+                        tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
+
                         PatKind::Wild
                     } else {
                         let old = self.behind_reference.replace(true);
@@ -545,27 +471,19 @@ impl<'tcx> ConstToPat<'tcx> {
                     && !self.saw_const_match_lint.get()
                 {
                     self.saw_const_match_lint.set(true);
-                    let msg = "function pointers and unsized pointers in patterns behave \
-                        unpredictably and should not be relied upon. \
-                        See https://github.com/rust-lang/rust/issues/70861 for details.";
-                    tcx.struct_span_lint_hir(
+                    tcx.emit_spanned_lint(
                         lint::builtin::POINTER_STRUCTURAL_MATCH,
                         id,
                         span,
-                        msg,
-                        |lint| lint,
+                        PointerPattern
                     );
                 }
                 PatKind::Constant { value: cv }
             }
             _ => {
                 self.saw_const_match_error.set(true);
-                let msg = format!("`{}` cannot be used in patterns", cv.ty());
-                if self.include_lint_checks {
-                    tcx.sess.span_err(span, &msg);
-                } else {
-                    tcx.sess.delay_span_bug(span, &msg);
-                }
+                let err = InvalidPattern { span, non_sm_ty: cv.ty() };
+                    tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
                 PatKind::Wild
             }
         };
@@ -576,21 +494,17 @@ impl<'tcx> ConstToPat<'tcx> {
             && mir_structural_match_violation
             // FIXME(#73448): Find a way to bring const qualification into parity with
             // `search_for_structural_match_violation` and then remove this condition.
-            && self.search_for_structural_match_violation(cv.ty()).is_some()
-        {
-            self.saw_const_match_lint.set(true);
+
             // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
             // could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
-            let msg = self.search_for_structural_match_violation(cv.ty()).unwrap().replace(
-                "in a pattern,",
-                "in a pattern, the constant's initializer must be trivial or",
-            );
-            tcx.struct_span_lint_hir(
+            && let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty())
+        {
+            self.saw_const_match_lint.set(true);
+            tcx.emit_spanned_lint(
                 lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH,
                 id,
                 span,
-                msg,
-                |lint| lint,
+                NontrivialStructuralMatch {non_sm_ty}
             );
         }
 
diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
index a95349d7670..17b3c475f83 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
@@ -67,6 +67,7 @@ use self::SliceKind::*;
 
 use super::compare_const_vals;
 use super::usefulness::{MatchCheckCtxt, PatCtxt};
+use crate::errors::{Overlap, OverlappingRangeEndpoints};
 
 /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
 fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
@@ -96,7 +97,7 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
 /// `IntRange` is never used to encode an empty range or a "range" that wraps
 /// around the (offset) space: i.e., `range.lo <= range.hi`.
 #[derive(Clone, PartialEq, Eq)]
-pub(super) struct IntRange {
+pub(crate) struct IntRange {
     range: RangeInclusive<u128>,
     /// Keeps the bias used for encoding the range. It depends on the type of the range and
     /// possibly the pointer size of the current architecture. The algorithm ensures we never
@@ -284,32 +285,21 @@ impl IntRange {
             return;
         }
 
-        let overlaps: Vec<_> = pats
+        let overlap: Vec<_> = pats
             .filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span())))
             .filter(|(range, _)| self.suspicious_intersection(range))
-            .map(|(range, span)| (self.intersection(&range).unwrap(), span))
+            .map(|(range, span)| Overlap {
+                range: self.intersection(&range).unwrap().to_pat(pcx.cx.tcx, pcx.ty),
+                span,
+            })
             .collect();
 
-        if !overlaps.is_empty() {
-            pcx.cx.tcx.struct_span_lint_hir(
+        if !overlap.is_empty() {
+            pcx.cx.tcx.emit_spanned_lint(
                 lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
                 hir_id,
                 pcx.span,
-                "multiple patterns overlap on their endpoints",
-                |lint| {
-                    for (int_range, span) in overlaps {
-                        lint.span_label(
-                            span,
-                            &format!(
-                                "this range overlaps on `{}`...",
-                                int_range.to_pat(pcx.cx.tcx, pcx.ty)
-                            ),
-                        );
-                    }
-                    lint.span_label(pcx.span, "... with this range");
-                    lint.note("you likely meant to write mutually exclusive ranges");
-                    lint
-                },
+                OverlappingRangeEndpoints { overlap, range: pcx.span },
             );
         }
     }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 7d4353c5292..3a6ef87c9c6 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -2,7 +2,7 @@
 
 mod check_match;
 mod const_to_pat;
-mod deconstruct_pat;
+pub(crate) mod deconstruct_pat;
 mod usefulness;
 
 pub(crate) use self::check_match::check_match;
diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
index 8f80cb95e58..be66d0d4765 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
@@ -291,9 +291,8 @@
 
 use self::ArmType::*;
 use self::Usefulness::*;
-
-use super::check_match::{joined_uncovered_patterns, pattern_not_covered_label};
 use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard};
+use crate::errors::{NonExhaustiveOmittedPattern, Uncovered};
 
 use rustc_data_structures::captures::Captures;
 
@@ -743,31 +742,6 @@ impl<'p, 'tcx> Witness<'p, 'tcx> {
     }
 }
 
-/// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
-/// is not exhaustive enough.
-///
-/// NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
-fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>(
-    cx: &MatchCheckCtxt<'p, 'tcx>,
-    scrut_ty: Ty<'tcx>,
-    sp: Span,
-    hir_id: HirId,
-    witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
-) {
-    cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, "some variants are not matched explicitly", |lint| {
-        let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
-        lint.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
-        lint.help(
-            "ensure that all variants are matched explicitly by adding the suggested match arms",
-        );
-        lint.note(&format!(
-            "the matched value is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found",
-            scrut_ty,
-        ));
-        lint
-    });
-}
-
 /// Algorithm from <http://moscova.inria.fr/~maranget/papers/warn/index.html>.
 /// The algorithm from the paper has been modified to correctly handle empty
 /// types. The changes are:
@@ -913,7 +887,19 @@ fn is_useful<'p, 'tcx>(
                         .collect::<Vec<_>>()
                 };
 
-                lint_non_exhaustive_omitted_patterns(pcx.cx, pcx.ty, pcx.span, hir_id, patterns);
+                // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
+                // is not exhaustive enough.
+                //
+                // NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
+                cx.tcx.emit_spanned_lint(
+                    NON_EXHAUSTIVE_OMITTED_PATTERNS,
+                    hir_id,
+                    pcx.span,
+                    NonExhaustiveOmittedPattern {
+                        scrut_ty: pcx.ty,
+                        uncovered: Uncovered::new(pcx.span, pcx.cx, patterns),
+                    },
+                );
             }
 
             ret.extend(usefulness);
diff --git a/tests/ui/consts/const-match-check.eval1.stderr b/tests/ui/consts/const-match-check.eval1.stderr
index 6e61dbbd8ee..1caf1617e21 100644
--- a/tests/ui/consts/const-match-check.eval1.stderr
+++ b/tests/ui/consts/const-match-check.eval1.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/const-match-check.rs:25:15
    |
 LL |     A = { let 0 = 0; 0 },
diff --git a/tests/ui/consts/const-match-check.eval2.stderr b/tests/ui/consts/const-match-check.eval2.stderr
index 1b3b6e06c3d..f038ba1c8ed 100644
--- a/tests/ui/consts/const-match-check.eval2.stderr
+++ b/tests/ui/consts/const-match-check.eval2.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/const-match-check.rs:31:24
    |
 LL |     let x: [i32; { let 0 = 0; 0 }] = [];
diff --git a/tests/ui/consts/const-match-check.matchck.stderr b/tests/ui/consts/const-match-check.matchck.stderr
index bc8edfa7af9..b1921f8a41e 100644
--- a/tests/ui/consts/const-match-check.matchck.stderr
+++ b/tests/ui/consts/const-match-check.matchck.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/const-match-check.rs:4:22
    |
 LL | const X: i32 = { let 0 = 0; 0 };
@@ -12,7 +12,7 @@ help: you might want to use `if let` to ignore the variants that aren't matched
 LL | const X: i32 = { if let 0 = 0 { todo!() } 0 };
    |                  ++           ~~~~~~~~~~~
 
-error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/const-match-check.rs:8:23
    |
 LL | static Y: i32 = { let 0 = 0; 0 };
@@ -26,7 +26,7 @@ help: you might want to use `if let` to ignore the variants that aren't matched
 LL | static Y: i32 = { if let 0 = 0 { todo!() } 0 };
    |                   ++           ~~~~~~~~~~~
 
-error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/const-match-check.rs:13:26
    |
 LL |     const X: i32 = { let 0 = 0; 0 };
@@ -40,7 +40,7 @@ help: you might want to use `if let` to ignore the variants that aren't matched
 LL |     const X: i32 = { if let 0 = 0 { todo!() } 0 };
    |                      ++           ~~~~~~~~~~~
 
-error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/const-match-check.rs:19:26
    |
 LL |     const X: i32 = { let 0 = 0; 0 };
diff --git a/tests/ui/consts/const-pattern-irrefutable.rs b/tests/ui/consts/const-pattern-irrefutable.rs
index 2105c12a168..61bdf57ffdb 100644
--- a/tests/ui/consts/const-pattern-irrefutable.rs
+++ b/tests/ui/consts/const-pattern-irrefutable.rs
@@ -9,8 +9,20 @@ use foo::d;
 const a: u8 = 2;
 
 fn main() {
-    let a = 4; //~ ERROR refutable pattern in local binding: `0_u8..=1_u8` and `3_u8..=u8::MAX
-    let c = 4; //~ ERROR refutable pattern in local binding: `0_u8..=1_u8` and `3_u8..=u8::MAX
-    let d = 4; //~ ERROR refutable pattern in local binding: `0_u8..=1_u8` and `3_u8..=u8::MAX
+    let a = 4;
+    //~^ ERROR refutable pattern in local binding
+    //~| patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered
+    //~| missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable
+    //~| HELP introduce a variable instead
+    let c = 4;
+    //~^ ERROR refutable pattern in local binding
+    //~| patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered
+    //~| missing patterns are not covered because `c` is interpreted as a constant pattern, not a new variable
+    //~| HELP introduce a variable instead
+    let d = 4;
+    //~^ ERROR refutable pattern in local binding
+    //~| patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered
+    //~| missing patterns are not covered because `d` is interpreted as a constant pattern, not a new variable
+    //~| HELP introduce a variable instead
     fn f() {} // Check that the `NOTE`s still work with an item here (cf. issue #35115).
 }
diff --git a/tests/ui/consts/const-pattern-irrefutable.stderr b/tests/ui/consts/const-pattern-irrefutable.stderr
index a2b8f072c6e..c156ea1610c 100644
--- a/tests/ui/consts/const-pattern-irrefutable.stderr
+++ b/tests/ui/consts/const-pattern-irrefutable.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in local binding: `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/const-pattern-irrefutable.rs:12:9
    |
 LL | const a: u8 = 2;
@@ -7,13 +7,14 @@ LL | const a: u8 = 2;
 LL |     let a = 4;
    |         ^
    |         |
-   |         interpreted as a constant pattern, not a new variable
+   |         patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered
+   |         missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable
    |         help: introduce a variable instead: `a_var`
    |
    = note: the matched value is of type `u8`
 
-error[E0005]: refutable pattern in local binding: `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered
-  --> $DIR/const-pattern-irrefutable.rs:13:9
+error[E0005]: refutable pattern in local binding
+  --> $DIR/const-pattern-irrefutable.rs:17:9
    |
 LL |     pub const b: u8 = 2;
    |     --------------- constant defined here
@@ -21,13 +22,14 @@ LL |     pub const b: u8 = 2;
 LL |     let c = 4;
    |         ^
    |         |
-   |         interpreted as a constant pattern, not a new variable
+   |         patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered
+   |         missing patterns are not covered because `c` is interpreted as a constant pattern, not a new variable
    |         help: introduce a variable instead: `c_var`
    |
    = note: the matched value is of type `u8`
 
-error[E0005]: refutable pattern in local binding: `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered
-  --> $DIR/const-pattern-irrefutable.rs:14:9
+error[E0005]: refutable pattern in local binding
+  --> $DIR/const-pattern-irrefutable.rs:22:9
    |
 LL |     pub const d: u8 = 2;
    |     --------------- constant defined here
@@ -35,7 +37,8 @@ LL |     pub const d: u8 = 2;
 LL |     let d = 4;
    |         ^
    |         |
-   |         interpreted as a constant pattern, not a new variable
+   |         patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered
+   |         missing patterns are not covered because `d` is interpreted as a constant pattern, not a new variable
    |         help: introduce a variable instead: `d_var`
    |
    = note: the matched value is of type `u8`
diff --git a/tests/ui/consts/const_let_refutable.stderr b/tests/ui/consts/const_let_refutable.stderr
index d7e8c048f7d..d6119028f5b 100644
--- a/tests/ui/consts/const_let_refutable.stderr
+++ b/tests/ui/consts/const_let_refutable.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in function argument: `&[]`, `&[_]` and `&[_, _, _, ..]` not covered
+error[E0005]: refutable pattern in function argument
   --> $DIR/const_let_refutable.rs:3:16
    |
 LL | const fn slice(&[a, b]: &[i32]) -> i32 {
diff --git a/tests/ui/empty/empty-never-array.rs b/tests/ui/empty/empty-never-array.rs
index 3de2b1a78a3..fd93346101d 100644
--- a/tests/ui/empty/empty-never-array.rs
+++ b/tests/ui/empty/empty-never-array.rs
@@ -8,7 +8,8 @@ enum Helper<T, U> {
 
 fn transmute<T, U>(t: T) -> U {
     let Helper::U(u) = Helper::T(t, []);
-    //~^ ERROR refutable pattern in local binding: `Helper::T(_, _)` not covered
+    //~^ ERROR refutable pattern in local binding
+    //~| `Helper::T(_, _)` not covered
     u
 }
 
diff --git a/tests/ui/empty/empty-never-array.stderr b/tests/ui/empty/empty-never-array.stderr
index adf78274368..a488e484b2b 100644
--- a/tests/ui/empty/empty-never-array.stderr
+++ b/tests/ui/empty/empty-never-array.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in local binding: `Helper::T(_, _)` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/empty-never-array.rs:10:9
    |
 LL |     let Helper::U(u) = Helper::T(t, []);
@@ -7,18 +7,14 @@ LL |     let Helper::U(u) = Helper::T(t, []);
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
 note: `Helper<T, U>` defined here
-  --> $DIR/empty-never-array.rs:4:5
+  --> $DIR/empty-never-array.rs:3:6
    |
 LL | enum Helper<T, U> {
-   |      ------
+   |      ^^^^^^
 LL |     T(T, [!; 0]),
-   |     ^ not covered
+   |     - not covered
    = note: the matched value is of type `Helper<T, U>`
-help: you might want to use `if let` to ignore the variant that isn't matched
-   |
-LL |     let u = if let Helper::U(u) = Helper::T(t, []) { u } else { todo!() };
-   |     ++++++++++                                     ++++++++++++++++++++++
-help: alternatively, you might want to use let else to handle the variant that isn't matched
+help: you might want to use `let else` to handle the variant that isn't matched
    |
 LL |     let Helper::U(u) = Helper::T(t, []) else { todo!() };
    |                                         ++++++++++++++++
diff --git a/tests/ui/error-codes/E0005.stderr b/tests/ui/error-codes/E0005.stderr
index 0f179259356..4692b66413d 100644
--- a/tests/ui/error-codes/E0005.stderr
+++ b/tests/ui/error-codes/E0005.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in local binding: `None` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/E0005.rs:3:9
    |
 LL |     let Some(y) = x;
@@ -6,17 +6,8 @@ LL |     let Some(y) = x;
    |
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
-note: `Option<i32>` defined here
-  --> $SRC_DIR/core/src/option.rs:LL:COL
-  ::: $SRC_DIR/core/src/option.rs:LL:COL
-   |
-   = note: not covered
    = note: the matched value is of type `Option<i32>`
-help: you might want to use `if let` to ignore the variant that isn't matched
-   |
-LL |     let y = if let Some(y) = x { y } else { todo!() };
-   |     ++++++++++                 ++++++++++++++++++++++
-help: alternatively, you might want to use let else to handle the variant that isn't matched
+help: you might want to use `let else` to handle the variant that isn't matched
    |
 LL |     let Some(y) = x else { todo!() };
    |                     ++++++++++++++++
diff --git a/tests/ui/error-codes/E0297.stderr b/tests/ui/error-codes/E0297.stderr
index 903422f3b9b..293028f5f68 100644
--- a/tests/ui/error-codes/E0297.stderr
+++ b/tests/ui/error-codes/E0297.stderr
@@ -1,14 +1,9 @@
-error[E0005]: refutable pattern in `for` loop binding: `None` not covered
+error[E0005]: refutable pattern in `for` loop binding
   --> $DIR/E0297.rs:4:9
    |
 LL |     for Some(x) in xs {}
    |         ^^^^^^^ pattern `None` not covered
    |
-note: `Option<i32>` defined here
-  --> $SRC_DIR/core/src/option.rs:LL:COL
-  ::: $SRC_DIR/core/src/option.rs:LL:COL
-   |
-   = note: not covered
    = note: the matched value is of type `Option<i32>`
 
 error: aborting due to previous error
diff --git a/tests/ui/feature-gates/feature-gate-exhaustive-patterns.stderr b/tests/ui/feature-gates/feature-gate-exhaustive-patterns.stderr
index e253e4791e8..49e7ab6082c 100644
--- a/tests/ui/feature-gates/feature-gate-exhaustive-patterns.stderr
+++ b/tests/ui/feature-gates/feature-gate-exhaustive-patterns.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in local binding: `Err(_)` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/feature-gate-exhaustive-patterns.rs:8:9
    |
 LL |     let Ok(_x) = foo();
@@ -6,17 +6,8 @@ LL |     let Ok(_x) = foo();
    |
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
-note: `Result<u32, !>` defined here
-  --> $SRC_DIR/core/src/result.rs:LL:COL
-  ::: $SRC_DIR/core/src/result.rs:LL:COL
-   |
-   = note: not covered
    = note: the matched value is of type `Result<u32, !>`
-help: you might want to use `if let` to ignore the variant that isn't matched
-   |
-LL |     let _x = if let Ok(_x) = foo() { _x } else { todo!() };
-   |     +++++++++++                    +++++++++++++++++++++++
-help: alternatively, you might want to use let else to handle the variant that isn't matched
+help: you might want to use `let else` to handle the variant that isn't matched
    |
 LL |     let Ok(_x) = foo() else { todo!() };
    |                        ++++++++++++++++
diff --git a/tests/ui/for/for-loop-refutable-pattern-error-message.stderr b/tests/ui/for/for-loop-refutable-pattern-error-message.stderr
index 20b689aa5e0..49a82a6769d 100644
--- a/tests/ui/for/for-loop-refutable-pattern-error-message.stderr
+++ b/tests/ui/for/for-loop-refutable-pattern-error-message.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in `for` loop binding: `&i32::MIN..=0_i32` and `&2_i32..=i32::MAX` not covered
+error[E0005]: refutable pattern in `for` loop binding
   --> $DIR/for-loop-refutable-pattern-error-message.rs:2:9
    |
 LL |     for &1 in [1].iter() {}
diff --git a/tests/ui/issues/issue-15381.rs b/tests/ui/issues/issue-15381.rs
index 392fb1b24dd..23b266bef1d 100644
--- a/tests/ui/issues/issue-15381.rs
+++ b/tests/ui/issues/issue-15381.rs
@@ -2,7 +2,8 @@ fn main() {
     let values: Vec<u8> = vec![1,2,3,4,5,6,7,8];
 
     for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) {
-        //~^ ERROR refutable pattern in `for` loop binding: `&[]`, `&[_]`, `&[_, _]` and 1 more not
+        //~^ ERROR refutable pattern in `for` loop binding
+        //~| patterns `&[]`, `&[_]`, `&[_, _]` and 1 more not covered
         println!("y={}", y);
     }
 }
diff --git a/tests/ui/issues/issue-15381.stderr b/tests/ui/issues/issue-15381.stderr
index c4667ce1c8b..085958411cc 100644
--- a/tests/ui/issues/issue-15381.stderr
+++ b/tests/ui/issues/issue-15381.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in `for` loop binding: `&[]`, `&[_]`, `&[_, _]` and 1 more not covered
+error[E0005]: refutable pattern in `for` loop binding
   --> $DIR/issue-15381.rs:4:9
    |
 LL |     for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) {
diff --git a/tests/ui/never_type/exhaustive_patterns.stderr b/tests/ui/never_type/exhaustive_patterns.stderr
index e41baf86218..40c7c1d1067 100644
--- a/tests/ui/never_type/exhaustive_patterns.stderr
+++ b/tests/ui/never_type/exhaustive_patterns.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in local binding: `Either::B(_)` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/exhaustive_patterns.rs:20:9
    |
 LL |     let Either::A(()) = foo();
@@ -7,13 +7,13 @@ LL |     let Either::A(()) = foo();
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
 note: `Either<(), !>` defined here
-  --> $DIR/exhaustive_patterns.rs:12:5
+  --> $DIR/exhaustive_patterns.rs:10:6
    |
 LL | enum Either<A, B> {
-   |      ------
+   |      ^^^^^^
 LL |     A(A),
 LL |     B(inner::Wrapper<B>),
-   |     ^ not covered
+   |     - not covered
    = note: the matched value is of type `Either<(), !>`
 help: you might want to use `if let` to ignore the variant that isn't matched
    |
diff --git a/tests/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr b/tests/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr
index 95b22ac0594..4adcf4feee9 100644
--- a/tests/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr
+++ b/tests/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `3_i32..=i32::MAX` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs:2:10
    |
 LL |     let (0 | (1 | 2)) = 0;
diff --git a/tests/ui/pattern/usefulness/issue-31561.rs b/tests/ui/pattern/usefulness/issue-31561.rs
index 5b878851a31..82414f0418b 100644
--- a/tests/ui/pattern/usefulness/issue-31561.rs
+++ b/tests/ui/pattern/usefulness/issue-31561.rs
@@ -6,5 +6,6 @@ enum Thing {
 
 fn main() {
     let Thing::Foo(y) = Thing::Foo(1);
-    //~^ ERROR refutable pattern in local binding: `Thing::Bar` and `Thing::Baz` not covered
+    //~^ ERROR refutable pattern in local binding
+    //~| `Thing::Bar` and `Thing::Baz` not covered
 }
diff --git a/tests/ui/pattern/usefulness/issue-31561.stderr b/tests/ui/pattern/usefulness/issue-31561.stderr
index 20f2f09500a..5367de5e513 100644
--- a/tests/ui/pattern/usefulness/issue-31561.stderr
+++ b/tests/ui/pattern/usefulness/issue-31561.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in local binding: `Thing::Bar` and `Thing::Baz` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/issue-31561.rs:8:9
    |
 LL |     let Thing::Foo(y) = Thing::Foo(1);
@@ -7,21 +7,17 @@ LL |     let Thing::Foo(y) = Thing::Foo(1);
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
 note: `Thing` defined here
-  --> $DIR/issue-31561.rs:3:5
+  --> $DIR/issue-31561.rs:1:6
    |
 LL | enum Thing {
-   |      -----
+   |      ^^^^^
 LL |     Foo(u8),
 LL |     Bar,
-   |     ^^^ not covered
+   |     --- not covered
 LL |     Baz
-   |     ^^^ not covered
+   |     --- not covered
    = note: the matched value is of type `Thing`
-help: you might want to use `if let` to ignore the variants that aren't matched
-   |
-LL |     let y = if let Thing::Foo(y) = Thing::Foo(1) { y } else { todo!() };
-   |     ++++++++++                                   ++++++++++++++++++++++
-help: alternatively, you might want to use let else to handle the variants that aren't matched
+help: you might want to use `let else` to handle the variants that aren't matched
    |
 LL |     let Thing::Foo(y) = Thing::Foo(1) else { todo!() };
    |                                       ++++++++++++++++
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs
index af42fc1aeb4..5145f769075 100644
--- a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs
+++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs
@@ -15,9 +15,6 @@ enum E {
     //~^ NOTE `E` defined here
     //~| NOTE `E` defined here
     //~| NOTE `E` defined here
-    //~| NOTE `E` defined here
-    //~| NOTE `E` defined here
-    //~| NOTE `E` defined here
     //~| NOTE  not covered
     //~| NOTE  not covered
     //~| NOTE  not covered
@@ -41,37 +38,41 @@ fn by_val(e: E) {
         E::A => {}
     }
 
-    let E::A = e; //~ ERROR refutable pattern in local binding: `E::B` and `E::C` not covered
-    //~^ NOTE patterns `E::B` and `E::C` not covered
+    let E::A = e;
+    //~^ ERROR refutable pattern in local binding
+    //~| patterns `E::B` and `E::C` not covered
     //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with
     //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
     //~| NOTE the matched value is of type `E`
 }
 
 fn by_ref_once(e: &E) {
-    match e { //~ ERROR non-exhaustive patterns: `&E::B` and `&E::C` not covered
-    //~^ NOTE patterns `&E::B` and `&E::C` not covered
+    match e {
+    //~^ ERROR non-exhaustive patterns
+    //~| patterns `&E::B` and `&E::C` not covered
     //~| NOTE the matched value is of type `&E`
         E::A => {}
     }
 
-    let E::A = e; //~ ERROR refutable pattern in local binding: `&E::B` and `&E::C` not covered
-    //~^ NOTE patterns `&E::B` and `&E::C` not covered
+    let E::A = e;
+    //~^ ERROR refutable pattern in local binding
+    //~| patterns `&E::B` and `&E::C` not covered
     //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with
     //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
     //~| NOTE the matched value is of type `&E`
 }
 
 fn by_ref_thrice(e: & &mut &E) {
-    match e { //~ ERROR non-exhaustive patterns: `&&mut &E::B` and `&&mut &E::C` not covered
-    //~^ NOTE patterns `&&mut &E::B` and `&&mut &E::C` not covered
+    match e {
+    //~^ ERROR non-exhaustive patterns
+    //~| patterns `&&mut &E::B` and `&&mut &E::C` not covered
     //~| NOTE the matched value is of type `&&mut &E`
         E::A => {}
     }
 
     let E::A = e;
-    //~^ ERROR refutable pattern in local binding: `&&mut &E::B` and `&&mut &E::C` not covered
-    //~| NOTE patterns `&&mut &E::B` and `&&mut &E::C` not covered
+    //~^ ERROR refutable pattern in local binding
+    //~| patterns `&&mut &E::B` and `&&mut &E::C` not covered
     //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with
     //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
     //~| NOTE the matched value is of type `&&mut &E`
@@ -83,20 +84,21 @@ enum Opt {
     Some(u8),
     None,
     //~^ NOTE `Opt` defined here
-    //~| NOTE `Opt` defined here
     //~| NOTE not covered
     //~| NOTE not covered
 }
 
 fn ref_pat(e: Opt) {
-    match e {//~ ERROR non-exhaustive patterns: `Opt::None` not covered
-        //~^ NOTE pattern `Opt::None` not covered
+    match e {
+        //~^ ERROR non-exhaustive patterns
+        //~| pattern `Opt::None` not covered
         //~| NOTE the matched value is of type `Opt`
         Opt::Some(ref _x) => {}
     }
 
-    let Opt::Some(ref _x) = e; //~ ERROR refutable pattern in local binding: `Opt::None` not covered
-    //~^ NOTE the matched value is of type `Opt`
+    let Opt::Some(ref _x) = e;
+    //~^ ERROR refutable pattern in local binding
+    //~| NOTE the matched value is of type `Opt`
     //~| NOTE pattern `Opt::None` not covered
     //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with
     //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr
index 678c9b2ab58..769d4070fb5 100644
--- a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr
+++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr
@@ -1,5 +1,5 @@
 error[E0004]: non-exhaustive patterns: `E::B` and `E::C` not covered
-  --> $DIR/non-exhaustive-defined-here.rs:38:11
+  --> $DIR/non-exhaustive-defined-here.rs:35:11
    |
 LL |     match e1 {
    |           ^^ patterns `E::B` and `E::C` not covered
@@ -22,8 +22,8 @@ LL ~         E::A => {}
 LL +         E::B | E::C => todo!()
    |
 
-error[E0005]: refutable pattern in local binding: `E::B` and `E::C` not covered
-  --> $DIR/non-exhaustive-defined-here.rs:44:9
+error[E0005]: refutable pattern in local binding
+  --> $DIR/non-exhaustive-defined-here.rs:41:9
    |
 LL |     let E::A = e;
    |         ^^^^ patterns `E::B` and `E::C` not covered
@@ -31,16 +31,16 @@ LL |     let E::A = e;
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
 note: `E` defined here
-  --> $DIR/non-exhaustive-defined-here.rs:14:5
+  --> $DIR/non-exhaustive-defined-here.rs:6:6
    |
 LL | enum E {
-   |      -
+   |      ^
 ...
 LL |     B,
-   |     ^ not covered
+   |     - not covered
 ...
 LL |     C
-   |     ^ not covered
+   |     - not covered
    = note: the matched value is of type `E`
 help: you might want to use `if let` to ignore the variants that aren't matched
    |
@@ -48,7 +48,7 @@ LL |     if let E::A = e { todo!() }
    |     ++              ~~~~~~~~~~~
 
 error[E0004]: non-exhaustive patterns: `&E::B` and `&E::C` not covered
-  --> $DIR/non-exhaustive-defined-here.rs:52:11
+  --> $DIR/non-exhaustive-defined-here.rs:50:11
    |
 LL |     match e {
    |           ^ patterns `&E::B` and `&E::C` not covered
@@ -71,8 +71,8 @@ LL ~         E::A => {}
 LL +         &E::B | &E::C => todo!()
    |
 
-error[E0005]: refutable pattern in local binding: `&E::B` and `&E::C` not covered
-  --> $DIR/non-exhaustive-defined-here.rs:58:9
+error[E0005]: refutable pattern in local binding
+  --> $DIR/non-exhaustive-defined-here.rs:57:9
    |
 LL |     let E::A = e;
    |         ^^^^ patterns `&E::B` and `&E::C` not covered
@@ -80,16 +80,16 @@ LL |     let E::A = e;
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
 note: `E` defined here
-  --> $DIR/non-exhaustive-defined-here.rs:14:5
+  --> $DIR/non-exhaustive-defined-here.rs:6:6
    |
 LL | enum E {
-   |      -
+   |      ^
 ...
 LL |     B,
-   |     ^ not covered
+   |     - not covered
 ...
 LL |     C
-   |     ^ not covered
+   |     - not covered
    = note: the matched value is of type `&E`
 help: you might want to use `if let` to ignore the variants that aren't matched
    |
@@ -120,8 +120,8 @@ LL ~         E::A => {}
 LL +         &&mut &E::B | &&mut &E::C => todo!()
    |
 
-error[E0005]: refutable pattern in local binding: `&&mut &E::B` and `&&mut &E::C` not covered
-  --> $DIR/non-exhaustive-defined-here.rs:72:9
+error[E0005]: refutable pattern in local binding
+  --> $DIR/non-exhaustive-defined-here.rs:73:9
    |
 LL |     let E::A = e;
    |         ^^^^ patterns `&&mut &E::B` and `&&mut &E::C` not covered
@@ -129,16 +129,16 @@ LL |     let E::A = e;
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
 note: `E` defined here
-  --> $DIR/non-exhaustive-defined-here.rs:14:5
+  --> $DIR/non-exhaustive-defined-here.rs:6:6
    |
 LL | enum E {
-   |      -
+   |      ^
 ...
 LL |     B,
-   |     ^ not covered
+   |     - not covered
 ...
 LL |     C
-   |     ^ not covered
+   |     - not covered
    = note: the matched value is of type `&&mut &E`
 help: you might want to use `if let` to ignore the variants that aren't matched
    |
@@ -152,7 +152,7 @@ LL |     match e {
    |           ^ pattern `Opt::None` not covered
    |
 note: `Opt` defined here
-  --> $DIR/non-exhaustive-defined-here.rs:84:5
+  --> $DIR/non-exhaustive-defined-here.rs:85:5
    |
 LL | enum Opt {
    |      ---
@@ -166,8 +166,8 @@ LL ~         Opt::Some(ref _x) => {}
 LL +         Opt::None => todo!()
    |
 
-error[E0005]: refutable pattern in local binding: `Opt::None` not covered
-  --> $DIR/non-exhaustive-defined-here.rs:98:9
+error[E0005]: refutable pattern in local binding
+  --> $DIR/non-exhaustive-defined-here.rs:99:9
    |
 LL |     let Opt::Some(ref _x) = e;
    |         ^^^^^^^^^^^^^^^^^ pattern `Opt::None` not covered
@@ -175,19 +175,15 @@ LL |     let Opt::Some(ref _x) = e;
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
 note: `Opt` defined here
-  --> $DIR/non-exhaustive-defined-here.rs:84:5
+  --> $DIR/non-exhaustive-defined-here.rs:81:6
    |
 LL | enum Opt {
-   |      ---
+   |      ^^^
 ...
 LL |     None,
-   |     ^^^^ not covered
+   |     ---- not covered
    = note: the matched value is of type `Opt`
-help: you might want to use `if let` to ignore the variant that isn't matched
-   |
-LL |     let _x = if let Opt::Some(ref _x) = e { _x } else { todo!() };
-   |     +++++++++++                           +++++++++++++++++++++++
-help: alternatively, you might want to use let else to handle the variant that isn't matched
+help: you might want to use `let else` to handle the variant that isn't matched
    |
 LL |     let Opt::Some(ref _x) = e else { todo!() };
    |                               ++++++++++++++++
diff --git a/tests/ui/pattern/usefulness/refutable-pattern-errors.rs b/tests/ui/pattern/usefulness/refutable-pattern-errors.rs
index 7c9aa51e748..7a3e991d593 100644
--- a/tests/ui/pattern/usefulness/refutable-pattern-errors.rs
+++ b/tests/ui/pattern/usefulness/refutable-pattern-errors.rs
@@ -1,7 +1,9 @@
 fn func((1, (Some(1), 2..=3)): (isize, (Option<isize>, isize))) { }
-//~^ ERROR refutable pattern in function argument: `(_, _)` not covered
+//~^ ERROR refutable pattern in function argument
+//~| `(_, _)` not covered
 
 fn main() {
     let (1, (Some(1), 2..=3)) = (1, (None, 2));
-    //~^ ERROR refutable pattern in local binding: `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered
+    //~^ ERROR refutable pattern in local binding
+    //~| `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered
 }
diff --git a/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr b/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr
index d1dacc822e9..c518de47740 100644
--- a/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr
+++ b/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in function argument: `(_, _)` not covered
+error[E0005]: refutable pattern in function argument
   --> $DIR/refutable-pattern-errors.rs:1:9
    |
 LL | fn func((1, (Some(1), 2..=3)): (isize, (Option<isize>, isize))) { }
@@ -6,8 +6,8 @@ LL | fn func((1, (Some(1), 2..=3)): (isize, (Option<isize>, isize))) { }
    |
    = note: the matched value is of type `(isize, (Option<isize>, isize))`
 
-error[E0005]: refutable pattern in local binding: `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered
-  --> $DIR/refutable-pattern-errors.rs:5:9
+error[E0005]: refutable pattern in local binding
+  --> $DIR/refutable-pattern-errors.rs:6:9
    |
 LL |     let (1, (Some(1), 2..=3)) = (1, (None, 2));
    |         ^^^^^^^^^^^^^^^^^^^^^ patterns `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered
diff --git a/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs
index a2d9e1935de..17dc38ab25d 100644
--- a/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs
+++ b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs
@@ -1,5 +1,6 @@
 fn main() {
     let f = |3: isize| println!("hello");
-    //~^ ERROR refutable pattern in function argument: `_` not covered
+    //~^ ERROR refutable pattern in function argument
+    //~| `_` not covered
     f(4);
 }
diff --git a/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr
index c9d8cf43f95..55f0b2319fb 100644
--- a/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr
+++ b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in function argument: `_` not covered
+error[E0005]: refutable pattern in function argument
   --> $DIR/refutable-pattern-in-fn-arg.rs:2:14
    |
 LL |     let f = |3: isize| println!("hello");
diff --git a/tests/ui/recursion/recursive-types-are-not-uninhabited.stderr b/tests/ui/recursion/recursive-types-are-not-uninhabited.stderr
index 86ad6aa847c..1b4d80d9057 100644
--- a/tests/ui/recursion/recursive-types-are-not-uninhabited.stderr
+++ b/tests/ui/recursion/recursive-types-are-not-uninhabited.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in local binding: `Err(_)` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/recursive-types-are-not-uninhabited.rs:6:9
    |
 LL |     let Ok(x) = res;
@@ -6,17 +6,8 @@ LL |     let Ok(x) = res;
    |
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
-note: `Result<u32, &R<'_>>` defined here
-  --> $SRC_DIR/core/src/result.rs:LL:COL
-  ::: $SRC_DIR/core/src/result.rs:LL:COL
-   |
-   = note: not covered
    = note: the matched value is of type `Result<u32, &R<'_>>`
-help: you might want to use `if let` to ignore the variant that isn't matched
-   |
-LL |     let x = if let Ok(x) = res { x } else { todo!() };
-   |     ++++++++++                 ++++++++++++++++++++++
-help: alternatively, you might want to use let else to handle the variant that isn't matched
+help: you might want to use `let else` to handle the variant that isn't matched
    |
 LL |     let Ok(x) = res else { todo!() };
    |                     ++++++++++++++++
diff --git a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs
index ac819dce6db..15f08486f0f 100644
--- a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs
+++ b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs
@@ -1,7 +1,8 @@
 fn main() {
     let A = 3;
-    //~^ ERROR refutable pattern in local binding: `i32::MIN..=1_i32` and
-    //~| interpreted as a constant pattern, not a new variable
+    //~^ ERROR refutable pattern in local binding
+    //~| patterns `i32::MIN..=1_i32` and `3_i32..=i32::MAX` not covered
+    //~| missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable
     //~| HELP introduce a variable instead
     //~| SUGGESTION a_var
 
diff --git a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr
index 618bcaca14c..1c1cab25fbf 100644
--- a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr
+++ b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr
@@ -1,10 +1,11 @@
-error[E0005]: refutable pattern in local binding: `i32::MIN..=1_i32` and `3_i32..=i32::MAX` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/const-pat-non-exaustive-let-new-var.rs:2:9
    |
 LL |     let A = 3;
    |         ^
    |         |
-   |         interpreted as a constant pattern, not a new variable
+   |         patterns `i32::MIN..=1_i32` and `3_i32..=i32::MAX` not covered
+   |         missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable
    |         help: introduce a variable instead: `a_var`
 ...
 LL |     const A: i32 = 2;
diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.rs b/tests/ui/uninhabited/uninhabited-irrefutable.rs
index 1a0f3c5e550..4b001aca2d1 100644
--- a/tests/ui/uninhabited/uninhabited-irrefutable.rs
+++ b/tests/ui/uninhabited/uninhabited-irrefutable.rs
@@ -24,5 +24,7 @@ enum Foo {
 
 fn main() {
     let x: Foo = Foo::D(123, 456);
-    let Foo::D(_y, _z) = x; //~ ERROR refutable pattern in local binding: `Foo::A(_)` not covered
+    let Foo::D(_y, _z) = x;
+    //~^ ERROR refutable pattern in local binding
+    //~| `Foo::A(_)` not covered
 }
diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.stderr b/tests/ui/uninhabited/uninhabited-irrefutable.stderr
index 32f287a1818..8cafea555c1 100644
--- a/tests/ui/uninhabited/uninhabited-irrefutable.stderr
+++ b/tests/ui/uninhabited/uninhabited-irrefutable.stderr
@@ -1,4 +1,4 @@
-error[E0005]: refutable pattern in local binding: `Foo::A(_)` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/uninhabited-irrefutable.rs:27:9
    |
 LL |     let Foo::D(_y, _z) = x;
@@ -7,18 +7,14 @@ LL |     let Foo::D(_y, _z) = x;
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
 note: `Foo` defined here
-  --> $DIR/uninhabited-irrefutable.rs:19:5
+  --> $DIR/uninhabited-irrefutable.rs:18:6
    |
 LL | enum Foo {
-   |      ---
+   |      ^^^
 LL |     A(foo::SecretlyEmpty),
-   |     ^ not covered
+   |     - not covered
    = note: the matched value is of type `Foo`
-help: you might want to use `if let` to ignore the variant that isn't matched
-   |
-LL |     let (_y, _z) = if let Foo::D(_y, _z) = x { (_y, _z) } else { todo!() };
-   |     +++++++++++++++++                        +++++++++++++++++++++++++++++
-help: alternatively, you might want to use let else to handle the variant that isn't matched
+help: you might want to use `let else` to handle the variant that isn't matched
    |
 LL |     let Foo::D(_y, _z) = x else { todo!() };
    |                            ++++++++++++++++
diff --git a/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr
index d33a61ca848..466d7f2eadb 100644
--- a/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr
+++ b/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr
@@ -95,7 +95,7 @@ LL ~         Ok(x) => x,
 LL ~         Err(_) => todo!(),
    |
 
-error[E0005]: refutable pattern in local binding: `Err(_)` not covered
+error[E0005]: refutable pattern in local binding
   --> $DIR/uninhabited-matches-feature-gated.rs:37:9
    |
 LL |     let Ok(x) = x;
@@ -103,17 +103,8 @@ LL |     let Ok(x) = x;
    |
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
-note: `Result<u32, Void>` defined here
-  --> $SRC_DIR/core/src/result.rs:LL:COL
-  ::: $SRC_DIR/core/src/result.rs:LL:COL
-   |
-   = note: not covered
    = note: the matched value is of type `Result<u32, Void>`
-help: you might want to use `if let` to ignore the variant that isn't matched
-   |
-LL |     let x = if let Ok(x) = x { x } else { todo!() };
-   |     ++++++++++               ++++++++++++++++++++++
-help: alternatively, you might want to use let else to handle the variant that isn't matched
+help: you might want to use `let else` to handle the variant that isn't matched
    |
 LL |     let Ok(x) = x else { todo!() };
    |                   ++++++++++++++++