about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNadrieril <nadrieril+git@gmail.com>2024-07-21 14:46:05 +0200
committerNadrieril <nadrieril+git@gmail.com>2024-07-24 08:02:55 +0200
commit64ac2b80822c33d69e6e61ea1eaf8a043bc35aad (patch)
treeb36de43b0fe67b8728eba2eb07095c4dbf4a12ec
parentc4d6a4a7e4d8d006f6d08345e91fb1cdf0fc7e7a (diff)
downloadrust-64ac2b80822c33d69e6e61ea1eaf8a043bc35aad.tar.gz
rust-64ac2b80822c33d69e6e61ea1eaf8a043bc35aad.zip
Explain why a given pattern is considered unreachable
-rw-r--r--compiler/rustc_mir_build/messages.ftl5
-rw-r--r--compiler/rustc_mir_build/src/errors.rs32
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs64
-rw-r--r--compiler/rustc_pattern_analysis/src/pat.rs17
-rw-r--r--compiler/rustc_pattern_analysis/src/rustc.rs2
-rw-r--r--compiler/rustc_pattern_analysis/src/usefulness.rs193
-rw-r--r--tests/ui/consts/packed_pattern.stderr4
-rw-r--r--tests/ui/consts/packed_pattern2.stderr4
-rw-r--r--tests/ui/error-codes/E0001.stderr6
-rw-r--r--tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs5
-rw-r--r--tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr226
-rw-r--r--tests/ui/pattern/usefulness/consts-opaque.stderr14
-rw-r--r--tests/ui/pattern/usefulness/empty-match-check-notes.exhaustive_patterns.stderr17
-rw-r--r--tests/ui/pattern/usefulness/empty-match-check-notes.normal.stderr17
-rw-r--r--tests/ui/pattern/usefulness/empty-match-check-notes.rs4
-rw-r--r--tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr87
-rw-r--r--tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr87
-rw-r--r--tests/ui/pattern/usefulness/empty-types.never_pats.stderr33
-rw-r--r--tests/ui/pattern/usefulness/empty-types.normal.stderr33
-rw-r--r--tests/ui/pattern/usefulness/explain-unreachable-pats.rs97
-rw-r--r--tests/ui/pattern/usefulness/explain-unreachable-pats.stderr92
-rw-r--r--tests/ui/pattern/usefulness/floats.stderr68
-rw-r--r--tests/ui/pattern/usefulness/impl-trait.stderr27
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/reachability.stderr119
-rw-r--r--tests/ui/pattern/usefulness/issue-12116.stderr4
-rw-r--r--tests/ui/pattern/usefulness/issue-12369.stderr6
-rw-r--r--tests/ui/pattern/usefulness/issue-13727.stderr4
-rw-r--r--tests/ui/pattern/usefulness/issue-30240-b.stderr4
-rw-r--r--tests/ui/pattern/usefulness/issue-31221.stderr4
-rw-r--r--tests/ui/pattern/usefulness/issue-57472.stderr8
-rw-r--r--tests/ui/pattern/usefulness/match-arm-statics.stderr18
-rw-r--r--tests/ui/pattern/usefulness/match-byte-array-patterns.stderr32
-rw-r--r--tests/ui/pattern/usefulness/match-ref-ice.stderr4
-rw-r--r--tests/ui/pattern/usefulness/match-vec-fixed.stderr8
-rw-r--r--tests/ui/pattern/usefulness/match-vec-unreachable.stderr12
-rw-r--r--tests/ui/pattern/usefulness/slice-pattern-const-2.stderr17
-rw-r--r--tests/ui/pattern/usefulness/slice-pattern-const-3.stderr17
-rw-r--r--tests/ui/pattern/usefulness/slice-pattern-const.stderr39
-rw-r--r--tests/ui/pattern/usefulness/slice-patterns-reachability.stderr27
-rw-r--r--tests/ui/pattern/usefulness/top-level-alternation.stderr47
-rw-r--r--tests/ui/reachable/unreachable-arm.stderr4
-rw-r--r--tests/ui/reachable/unreachable-loop-patterns.stderr1
-rw-r--r--tests/ui/reachable/unreachable-try-pattern.stderr3
-rw-r--r--tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr11
-rw-r--r--tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr1
-rw-r--r--tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.stderr4
-rw-r--r--tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr9
-rw-r--r--tests/ui/rfcs/rfc-2294-if-let-guard/warns.stderr4
-rw-r--r--tests/ui/uninhabited/uninhabited-patterns.stderr5
49 files changed, 1283 insertions, 263 deletions
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index 281f3ef6ef3..8dd337f0c52 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -327,7 +327,10 @@ mir_build_union_pattern = cannot use unions in constant patterns
 
 mir_build_unreachable_pattern = unreachable pattern
     .label = unreachable pattern
-    .catchall_label = matches any value
+    .unreachable_matches_no_values = this pattern matches no values because `{$ty}` is uninhabited
+    .unreachable_covered_by_catchall = matches any value
+    .unreachable_covered_by_one = matches all the values already
+    .unreachable_covered_by_many = matches some of the same values
 
 mir_build_unsafe_fn_safe_body = an unsafe function restricts its caller, but its body is safe by default
 mir_build_unsafe_not_inherited = items do not inherit unsafety from separate enclosing items
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index f6f443b64a6..cf2962d9fcf 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -582,11 +582,37 @@ pub(crate) struct NonConstPath {
 
 #[derive(LintDiagnostic)]
 #[diag(mir_build_unreachable_pattern)]
-pub(crate) struct UnreachablePattern {
+pub(crate) struct UnreachablePattern<'tcx> {
     #[label]
     pub(crate) span: Option<Span>,
-    #[label(mir_build_catchall_label)]
-    pub(crate) catchall: Option<Span>,
+    #[subdiagnostic]
+    pub(crate) matches_no_values: Option<UnreachableMatchesNoValues<'tcx>>,
+    #[label(mir_build_unreachable_covered_by_catchall)]
+    pub(crate) covered_by_catchall: Option<Span>,
+    #[label(mir_build_unreachable_covered_by_one)]
+    pub(crate) covered_by_one: Option<Span>,
+    #[subdiagnostic]
+    pub(crate) covered_by_many: Option<UnreachableCoveredByMany>,
+}
+
+#[derive(Subdiagnostic)]
+#[note(mir_build_unreachable_matches_no_values)]
+pub(crate) struct UnreachableMatchesNoValues<'tcx> {
+    pub(crate) ty: Ty<'tcx>,
+}
+
+pub(crate) struct UnreachableCoveredByMany(pub(crate) Vec<Span>);
+
+impl Subdiagnostic for UnreachableCoveredByMany {
+    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
+        self,
+        diag: &mut Diag<'_, G>,
+        _f: &F,
+    ) {
+        for span in self.0 {
+            diag.span_label(span, fluent::mir_build_unreachable_covered_by_many);
+        }
+    }
 }
 
 #[derive(Diagnostic)]
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 28cb994b8e2..3c476a7766c 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -16,8 +16,8 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
 use rustc_pattern_analysis::errors::Uncovered;
 use rustc_pattern_analysis::rustc::{
-    Constructor, DeconstructedPat, MatchArm, RevealedTy, RustcPatCtxt as PatCtxt, Usefulness,
-    UsefulnessReport, WitnessPat,
+    Constructor, DeconstructedPat, MatchArm, RedundancyExplanation, RevealedTy,
+    RustcPatCtxt as PatCtxt, Usefulness, UsefulnessReport, WitnessPat,
 };
 use rustc_session::lint::builtin::{
     BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
@@ -409,9 +409,9 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
             {
                 let mut redundant_subpats = redundant_subpats.clone();
                 // Emit lints in the order in which they occur in the file.
-                redundant_subpats.sort_unstable_by_key(|pat| pat.data().span);
-                for pat in redundant_subpats {
-                    report_unreachable_pattern(cx, arm.arm_data, pat.data().span, None)
+                redundant_subpats.sort_unstable_by_key(|(pat, _)| pat.data().span);
+                for (pat, explanation) in redundant_subpats {
+                    report_unreachable_pattern(cx, arm.arm_data, pat, &explanation)
                 }
             }
         }
@@ -910,26 +910,52 @@ fn report_irrefutable_let_patterns(
 fn report_unreachable_pattern<'p, 'tcx>(
     cx: &PatCtxt<'p, 'tcx>,
     hir_id: HirId,
-    span: Span,
-    catchall: Option<Span>,
+    pat: &DeconstructedPat<'p, 'tcx>,
+    explanation: &RedundancyExplanation<'p, 'tcx>,
 ) {
-    cx.tcx.emit_node_span_lint(
-        UNREACHABLE_PATTERNS,
-        hir_id,
-        span,
-        UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall },
-    );
+    let pat_span = pat.data().span;
+    let mut lint = UnreachablePattern {
+        span: Some(pat_span),
+        matches_no_values: None,
+        covered_by_catchall: None,
+        covered_by_one: None,
+        covered_by_many: None,
+    };
+    match explanation.covered_by.as_slice() {
+        [] => {
+            // Empty pattern; we report the uninhabited type that caused the emptiness.
+            lint.span = None; // Don't label the pattern itself
+            pat.walk(&mut |subpat| {
+                let ty = **subpat.ty();
+                if cx.is_uninhabited(ty) {
+                    lint.matches_no_values = Some(UnreachableMatchesNoValues { ty });
+                    false // No need to dig further.
+                } else if matches!(subpat.ctor(), Constructor::Ref | Constructor::UnionField) {
+                    false // Don't explore further since they are not by-value.
+                } else {
+                    true
+                }
+            });
+        }
+        [covering_pat] if pat_is_catchall(covering_pat) => {
+            lint.covered_by_catchall = Some(covering_pat.data().span);
+        }
+        [covering_pat] => {
+            lint.covered_by_one = Some(covering_pat.data().span);
+        }
+        covering_pats => {
+            let covering_spans = covering_pats.iter().map(|p| p.data().span).collect();
+            lint.covered_by_many = Some(UnreachableCoveredByMany(covering_spans));
+        }
+    }
+    cx.tcx.emit_node_span_lint(UNREACHABLE_PATTERNS, hir_id, pat_span, lint);
 }
 
 /// Report unreachable arms, if any.
 fn report_arm_reachability<'p, 'tcx>(cx: &PatCtxt<'p, 'tcx>, report: &UsefulnessReport<'p, 'tcx>) {
-    let mut catchall = None;
     for (arm, is_useful) in report.arm_usefulness.iter() {
-        if matches!(is_useful, Usefulness::Redundant) {
-            report_unreachable_pattern(cx, arm.arm_data, arm.pat.data().span, catchall)
-        }
-        if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
-            catchall = Some(arm.pat.data().span);
+        if let Usefulness::Redundant(explanation) = is_useful {
+            report_unreachable_pattern(cx, arm.arm_data, arm.pat, explanation)
         }
     }
 }
diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs
index e8d6c542c5f..a591c3c554b 100644
--- a/compiler/rustc_pattern_analysis/src/pat.rs
+++ b/compiler/rustc_pattern_analysis/src/pat.rs
@@ -11,7 +11,7 @@ use crate::{PatCx, PrivateUninhabitedField};
 use self::Constructor::*;
 
 /// A globally unique id to distinguish patterns.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub(crate) struct PatId(u32);
 impl PatId {
     fn new() -> Self {
@@ -147,6 +147,21 @@ impl<Cx: PatCx> fmt::Debug for DeconstructedPat<Cx> {
     }
 }
 
+/// Delegate to `uid`.
+impl<Cx: PatCx> PartialEq for DeconstructedPat<Cx> {
+    fn eq(&self, other: &Self) -> bool {
+        self.uid == other.uid
+    }
+}
+/// Delegate to `uid`.
+impl<Cx: PatCx> Eq for DeconstructedPat<Cx> {}
+/// Delegate to `uid`.
+impl<Cx: PatCx> std::hash::Hash for DeconstructedPat<Cx> {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        self.uid.hash(state);
+    }
+}
+
 /// Represents either a pattern obtained from user input or a wildcard constructed during the
 /// algorithm. Do not use `Wild` to represent a wildcard pattern comping from user input.
 ///
diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs
index dd815f7e0a1..910dd4c6c1a 100644
--- a/compiler/rustc_pattern_analysis/src/rustc.rs
+++ b/compiler/rustc_pattern_analysis/src/rustc.rs
@@ -32,6 +32,8 @@ pub type Constructor<'p, 'tcx> = crate::constructor::Constructor<RustcPatCtxt<'p
 pub type ConstructorSet<'p, 'tcx> = crate::constructor::ConstructorSet<RustcPatCtxt<'p, 'tcx>>;
 pub type DeconstructedPat<'p, 'tcx> = crate::pat::DeconstructedPat<RustcPatCtxt<'p, 'tcx>>;
 pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcPatCtxt<'p, 'tcx>>;
+pub type RedundancyExplanation<'p, 'tcx> =
+    crate::usefulness::RedundancyExplanation<'p, RustcPatCtxt<'p, 'tcx>>;
 pub type Usefulness<'p, 'tcx> = crate::usefulness::Usefulness<'p, RustcPatCtxt<'p, 'tcx>>;
 pub type UsefulnessReport<'p, 'tcx> =
     crate::usefulness::UsefulnessReport<'p, RustcPatCtxt<'p, 'tcx>>;
diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs
index 8486792b554..76dc338e71c 100644
--- a/compiler/rustc_pattern_analysis/src/usefulness.rs
+++ b/compiler/rustc_pattern_analysis/src/usefulness.rs
@@ -713,7 +713,7 @@ use self::PlaceValidity::*;
 use crate::constructor::{Constructor, ConstructorSet, IntRange};
 use crate::pat::{DeconstructedPat, PatId, PatOrWild, WitnessPat};
 use crate::{Captures, MatchArm, PatCx, PrivateUninhabitedField};
-use rustc_hash::FxHashSet;
+use rustc_hash::{FxHashMap, FxHashSet};
 use rustc_index::bit_set::BitSet;
 use smallvec::{smallvec, SmallVec};
 use std::fmt;
@@ -726,18 +726,81 @@ pub fn ensure_sufficient_stack<R>(f: impl FnOnce() -> R) -> R {
     f()
 }
 
+/// A pattern is a "branch" if it is the immediate child of an or-pattern, or if it is the whole
+/// pattern of a match arm. These are the patterns that can be meaningfully considered "redundant",
+/// since e.g. `0` in `(0, 1)` cannot be redundant on its own.
+///
+/// We track for each branch pattern whether it is useful, and if not why.
+struct BranchPatUsefulness<'p, Cx: PatCx> {
+    /// Whether this pattern is useful.
+    useful: bool,
+    /// A set of patterns that:
+    /// - come before this one in the match;
+    /// - intersect this one;
+    /// - at the end of the algorithm, if `!self.useful`, their union covers this pattern.
+    covered_by: FxHashSet<&'p DeconstructedPat<Cx>>,
+}
+
+impl<'p, Cx: PatCx> BranchPatUsefulness<'p, Cx> {
+    /// Update `self` with the usefulness information found in `row`.
+    fn update(&mut self, row: &MatrixRow<'p, Cx>, matrix: &Matrix<'p, Cx>) {
+        self.useful |= row.useful;
+        // This deserves an explanation: `intersects_at_least` does not contain all intersections
+        // because we skip irrelevant values (see the docs for `intersects_at_least` for an
+        // example). Yet we claim this suffices to build a covering set.
+        //
+        // Let `p` be our pattern. Assume it is found not useful. For a value `v`, if the value was
+        // relevant then we explored that value and found that there was another pattern `q` before
+        // `p` that matches it too. We therefore recorded an intersection with `q`. If `v` was
+        // irrelevant, we know there's another value `v2` that matches strictly fewer rows (while
+        // still matching our row) and is relevant. Since `p` is not useful, there must have been a
+        // `q` before `p` that matches `v2`, and we recorded that intersection. Since `v2` matches
+        // strictly fewer rows than `v`, `q` also matches `v`. In either case, we recorded in
+        // `intersects_at_least` a pattern that matches `v`. Hence using `intersects_at_least` is
+        // sufficient to build a covering set.
+        for row_id in row.intersects_at_least.iter() {
+            let row = &matrix.rows[row_id];
+            if row.useful && !row.is_under_guard {
+                if let PatOrWild::Pat(intersecting) = row.head() {
+                    self.covered_by.insert(intersecting);
+                }
+            }
+        }
+    }
+
+    /// Check whether this pattern is redundant, and if so explain why.
+    fn is_redundant(&self) -> Option<RedundancyExplanation<'p, Cx>> {
+        if self.useful {
+            None
+        } else {
+            // We avoid instability by sorting by `uid`. The order of `uid`s only depends on the
+            // pattern structure.
+            #[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))]
+            let mut covered_by: Vec<_> = self.covered_by.iter().copied().collect();
+            covered_by.sort_by_key(|pat| pat.uid); // sort to avoid instability
+            Some(RedundancyExplanation { covered_by })
+        }
+    }
+}
+
+impl<'p, Cx: PatCx> Default for BranchPatUsefulness<'p, Cx> {
+    fn default() -> Self {
+        Self { useful: Default::default(), covered_by: Default::default() }
+    }
+}
+
 /// Context that provides information for usefulness checking.
-struct UsefulnessCtxt<'a, Cx: PatCx> {
+struct UsefulnessCtxt<'a, 'p, Cx: PatCx> {
     /// The context for type information.
     tycx: &'a Cx,
-    /// Collect the patterns found useful during usefulness checking. This is used to lint
-    /// unreachable (sub)patterns.
-    useful_subpatterns: FxHashSet<PatId>,
+    /// Track information about the usefulness of branch patterns (see definition of "branch
+    /// pattern" at [`BranchPatUsefulness`]).
+    branch_usefulness: FxHashMap<PatId, BranchPatUsefulness<'p, Cx>>,
     complexity_limit: Option<usize>,
     complexity_level: usize,
 }
 
-impl<'a, Cx: PatCx> UsefulnessCtxt<'a, Cx> {
+impl<'a, 'p, Cx: PatCx> UsefulnessCtxt<'a, 'p, Cx> {
     fn increase_complexity_level(&mut self, complexity_add: usize) -> Result<(), Cx::Error> {
         self.complexity_level += complexity_add;
         if self
@@ -1051,14 +1114,40 @@ struct MatrixRow<'p, Cx: PatCx> {
     /// [`compute_exhaustiveness_and_usefulness`] if the arm is found to be useful.
     /// This is reset to `false` when specializing.
     useful: bool,
-    /// Tracks which rows above this one have an intersection with this one, i.e. such that there is
-    /// a value that matches both rows.
-    /// Note: Because of relevancy we may miss some intersections. The intersections we do find are
-    /// correct.
-    intersects: BitSet<usize>,
+    /// Tracks some rows above this one that have an intersection with this one, i.e. such that
+    /// there is a value that matches both rows.
+    /// Because of relevancy we may miss some intersections. The intersections we do find are
+    /// correct. In other words, this is an underapproximation of the real set of intersections.
+    ///
+    /// For example:
+    /// ```rust,ignore(illustrative)
+    /// match ... {
+    ///     (true, _, _) => {} // `intersects_at_least = []`
+    ///     (_, true, 0..=10) => {} // `intersects_at_least = []`
+    ///     (_, true, 5..15) => {} // `intersects_at_least = [1]`
+    /// }
+    /// ```
+    /// Here the `(true, true)` case is irrelevant. Since we skip it, we will not detect that row 0
+    /// intersects rows 1 and 2.
+    intersects_at_least: BitSet<usize>,
+    /// Whether the head pattern is a branch (see definition of "branch pattern" at
+    /// [`BranchPatUsefulness`])
+    head_is_branch: bool,
 }
 
 impl<'p, Cx: PatCx> MatrixRow<'p, Cx> {
+    fn new(arm: &MatchArm<'p, Cx>, arm_id: usize) -> Self {
+        MatrixRow {
+            pats: PatStack::from_pattern(arm.pat),
+            parent_row: arm_id,
+            is_under_guard: arm.has_guard,
+            useful: false,
+            intersects_at_least: BitSet::new_empty(0), // Initialized in `Matrix::push`.
+            // This pattern is a branch because it comes from a match arm.
+            head_is_branch: true,
+        }
+    }
+
     fn len(&self) -> usize {
         self.pats.len()
     }
@@ -1076,12 +1165,14 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> {
         &self,
         parent_row: usize,
     ) -> impl Iterator<Item = MatrixRow<'p, Cx>> + Captures<'_> {
+        let is_or_pat = self.pats.head().is_or_pat();
         self.pats.expand_or_pat().map(move |patstack| MatrixRow {
             pats: patstack,
             parent_row,
             is_under_guard: self.is_under_guard,
             useful: false,
-            intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`.
+            intersects_at_least: BitSet::new_empty(0), // Initialized in `Matrix::push`.
+            head_is_branch: is_or_pat,
         })
     }
 
@@ -1100,7 +1191,8 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> {
             parent_row,
             is_under_guard: self.is_under_guard,
             useful: false,
-            intersects: BitSet::new_empty(0), // Initialized in `Matrix::push`.
+            intersects_at_least: BitSet::new_empty(0), // Initialized in `Matrix::push`.
+            head_is_branch: false,
         })
     }
 }
@@ -1138,7 +1230,7 @@ struct Matrix<'p, Cx: PatCx> {
 impl<'p, Cx: PatCx> Matrix<'p, Cx> {
     /// Pushes a new row to the matrix. Internal method, prefer [`Matrix::new`].
     fn push(&mut self, mut row: MatrixRow<'p, Cx>) {
-        row.intersects = BitSet::new_empty(self.rows.len());
+        row.intersects_at_least = BitSet::new_empty(self.rows.len());
         self.rows.push(row);
     }
 
@@ -1156,14 +1248,7 @@ impl<'p, Cx: PatCx> Matrix<'p, Cx> {
             wildcard_row_is_relevant: true,
         };
         for (arm_id, arm) in arms.iter().enumerate() {
-            let v = MatrixRow {
-                pats: PatStack::from_pattern(arm.pat),
-                parent_row: arm_id,
-                is_under_guard: arm.has_guard,
-                useful: false,
-                intersects: BitSet::new_empty(0), // Initialized in `Matrix::push`.
-            };
-            matrix.push(v);
+            matrix.push(MatrixRow::new(arm, arm_id));
         }
         matrix
     }
@@ -1242,12 +1327,12 @@ impl<'p, Cx: PatCx> Matrix<'p, Cx> {
             let parent_row = &mut self.rows[parent_row_id];
             // A parent row is useful if any of its children is.
             parent_row.useful |= child_row.useful;
-            for child_intersection in child_row.intersects.iter() {
+            for child_intersection in child_row.intersects_at_least.iter() {
                 // Convert the intersecting ids into ids for the parent matrix.
                 let parent_intersection = specialized.rows[child_intersection].parent_row;
                 // Note: self-intersection can happen with or-patterns.
                 if parent_intersection != parent_row_id {
-                    parent_row.intersects.insert(parent_intersection);
+                    parent_row.intersects_at_least.insert(parent_intersection);
                 }
             }
         }
@@ -1544,7 +1629,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: PatCx>(
                 let overlaps_with: Vec<_> = prefixes
                     .iter()
                     .filter(|&&(other_child_row_id, _)| {
-                        child_row.intersects.contains(other_child_row_id)
+                        child_row.intersects_at_least.contains(other_child_row_id)
                     })
                     .map(|&(_, pat)| pat)
                     .collect();
@@ -1560,7 +1645,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: PatCx>(
                 let overlaps_with: Vec<_> = suffixes
                     .iter()
                     .filter(|&&(other_child_row_id, _)| {
-                        child_row.intersects.contains(other_child_row_id)
+                        child_row.intersects_at_least.contains(other_child_row_id)
                     })
                     .map(|&(_, pat)| pat)
                     .collect();
@@ -1603,8 +1688,8 @@ fn collect_non_contiguous_range_endpoints<'p, Cx: PatCx>(
 /// The core of the algorithm.
 ///
 /// This recursively computes witnesses of the non-exhaustiveness of `matrix` (if any). Also tracks
-/// usefulness of each row in the matrix (in `row.useful`). We track usefulness of each subpattern
-/// in `mcx.useful_subpatterns`.
+/// usefulness of each row in the matrix (in `row.useful`). We track usefulness of subpatterns in
+/// `mcx.branch_usefulness`.
 ///
 /// The input `Matrix` and the output `WitnessMatrix` together match the type exhaustively.
 ///
@@ -1616,7 +1701,7 @@ fn collect_non_contiguous_range_endpoints<'p, Cx: PatCx>(
 /// This is all explained at the top of the file.
 #[instrument(level = "debug", skip(mcx), ret)]
 fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>(
-    mcx: &mut UsefulnessCtxt<'a, Cx>,
+    mcx: &mut UsefulnessCtxt<'a, 'p, Cx>,
     matrix: &mut Matrix<'p, Cx>,
 ) -> Result<WitnessMatrix<Cx>, Cx::Error> {
     debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count()));
@@ -1635,7 +1720,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>(
         let mut useful = true; // Whether the next row is useful.
         for (i, row) in matrix.rows_mut().enumerate() {
             row.useful = useful;
-            row.intersects.insert_range(0..i);
+            row.intersects_at_least.insert_range(0..i);
             // The next rows stays useful if this one is under a guard.
             useful &= row.is_under_guard;
         }
@@ -1677,7 +1762,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>(
         if let Constructor::IntRange(overlap_range) = ctor {
             if overlap_range.is_singleton()
                 && spec_matrix.rows.len() >= 2
-                && spec_matrix.rows.iter().any(|row| !row.intersects.is_empty())
+                && spec_matrix.rows.iter().any(|row| !row.intersects_at_least.is_empty())
             {
                 collect_overlapping_range_endpoints(mcx.tycx, overlap_range, matrix, &spec_matrix);
             }
@@ -1697,14 +1782,11 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>(
         }
     }
 
-    // Record usefulness in the patterns.
+    // Record usefulness of the branch patterns.
     for row in matrix.rows() {
-        if row.useful {
+        if row.head_is_branch {
             if let PatOrWild::Pat(pat) = row.head() {
-                let newly_useful = mcx.useful_subpatterns.insert(pat.uid);
-                if newly_useful {
-                    debug!("newly useful: {pat:?}");
-                }
+                mcx.branch_usefulness.entry(pat.uid).or_default().update(row, matrix);
             }
         }
     }
@@ -1712,16 +1794,25 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>(
     Ok(ret)
 }
 
+/// Indicates why a given pattern is considered redundant.
+#[derive(Clone, Debug)]
+pub struct RedundancyExplanation<'p, Cx: PatCx> {
+    /// All the values matched by this pattern are already matched by the given set of patterns.
+    /// This list is not guaranteed to be minimal but the contained patterns are at least guaranteed
+    /// to intersect this pattern.
+    pub covered_by: Vec<&'p DeconstructedPat<Cx>>,
+}
+
 /// Indicates whether or not a given arm is useful.
 #[derive(Clone, Debug)]
 pub enum Usefulness<'p, Cx: PatCx> {
     /// The arm is useful. This additionally carries a set of or-pattern branches that have been
     /// found to be redundant despite the overall arm being useful. Used only in the presence of
     /// or-patterns, otherwise it stays empty.
-    Useful(Vec<&'p DeconstructedPat<Cx>>),
+    Useful(Vec<(&'p DeconstructedPat<Cx>, RedundancyExplanation<'p, Cx>)>),
     /// The arm is redundant and can be removed without changing the behavior of the match
     /// expression.
-    Redundant,
+    Redundant(RedundancyExplanation<'p, Cx>),
 }
 
 /// The output of checking a match for exhaustiveness and arm usefulness.
@@ -1747,7 +1838,7 @@ pub fn compute_match_usefulness<'p, Cx: PatCx>(
 ) -> Result<UsefulnessReport<'p, Cx>, Cx::Error> {
     let mut cx = UsefulnessCtxt {
         tycx,
-        useful_subpatterns: FxHashSet::default(),
+        branch_usefulness: FxHashMap::default(),
         complexity_limit,
         complexity_level: 0,
     };
@@ -1760,26 +1851,32 @@ pub fn compute_match_usefulness<'p, Cx: PatCx>(
         .copied()
         .map(|arm| {
             debug!(?arm);
-            let usefulness = if cx.useful_subpatterns.contains(&arm.pat.uid) {
+            let usefulness = cx.branch_usefulness.get(&arm.pat.uid).unwrap();
+            let usefulness = if let Some(explanation) = usefulness.is_redundant() {
+                Usefulness::Redundant(explanation)
+            } else {
                 let mut redundant_subpats = Vec::new();
                 arm.pat.walk(&mut |subpat| {
-                    if cx.useful_subpatterns.contains(&subpat.uid) {
-                        true // keep recursing
+                    if let Some(u) = cx.branch_usefulness.get(&subpat.uid) {
+                        if let Some(explanation) = u.is_redundant() {
+                            redundant_subpats.push((subpat, explanation));
+                            false // stop recursing
+                        } else {
+                            true // keep recursing
+                        }
                     } else {
-                        redundant_subpats.push(subpat);
-                        false // stop recursing
+                        true // keep recursing
                     }
                 });
                 Usefulness::Useful(redundant_subpats)
-            } else {
-                Usefulness::Redundant
             };
             debug!(?usefulness);
             (arm, usefulness)
         })
         .collect();
 
-    let arm_intersections: Vec<_> = matrix.rows().map(|row| row.intersects.clone()).collect();
+    let arm_intersections: Vec<_> =
+        matrix.rows().map(|row| row.intersects_at_least.clone()).collect();
 
     Ok(UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses, arm_intersections })
 }
diff --git a/tests/ui/consts/packed_pattern.stderr b/tests/ui/consts/packed_pattern.stderr
index 9ca50a95e4e..a0b434b2d78 100644
--- a/tests/ui/consts/packed_pattern.stderr
+++ b/tests/ui/consts/packed_pattern.stderr
@@ -1,8 +1,10 @@
 warning: unreachable pattern
   --> $DIR/packed_pattern.rs:16:9
    |
+LL |         Foo { field: (5, 6, 7, 8) } => {},
+   |         --------------------------- matches all the values already
 LL |         FOO => unreachable!(),
-   |         ^^^
+   |         ^^^ unreachable pattern
    |
    = note: `#[warn(unreachable_patterns)]` on by default
 
diff --git a/tests/ui/consts/packed_pattern2.stderr b/tests/ui/consts/packed_pattern2.stderr
index 4dc54461eeb..4785f4d0297 100644
--- a/tests/ui/consts/packed_pattern2.stderr
+++ b/tests/ui/consts/packed_pattern2.stderr
@@ -1,8 +1,10 @@
 warning: unreachable pattern
   --> $DIR/packed_pattern2.rs:24:9
    |
+LL |         Bar { a: Foo { field: (5, 6) } } => {},
+   |         -------------------------------- matches all the values already
 LL |         FOO => unreachable!(),
-   |         ^^^
+   |         ^^^ unreachable pattern
    |
    = note: `#[warn(unreachable_patterns)]` on by default
 
diff --git a/tests/ui/error-codes/E0001.stderr b/tests/ui/error-codes/E0001.stderr
index 49bb73e7ba8..bf8c5235f70 100644
--- a/tests/ui/error-codes/E0001.stderr
+++ b/tests/ui/error-codes/E0001.stderr
@@ -1,8 +1,12 @@
 error: unreachable pattern
   --> $DIR/E0001.rs:8:9
    |
+LL |         Some(_) => {/* ... */}
+   |         ------- matches some of the same values
+LL |         None => {/* ... */}
+   |         ---- matches some of the same values
 LL |         _ => {/* ... */}
-   |         ^
+   |         ^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/E0001.rs:1:9
diff --git a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs
index 1ad335bf394..afdcff0346b 100644
--- a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs
+++ b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs
@@ -29,6 +29,11 @@ fn main() {
         (1, 4 | 5) => {} //~ ERROR unreachable pattern
         _ => {}
     }
+    match (0u8, 0u8, 0u8) {
+        (0, 0, 0) => {}
+        (0, 0 | 1, 0) => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
     match (true, true) {
         (false | true, false | true) => (),
     }
diff --git a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr
index 336530d1b32..5c7d9db1c92 100644
--- a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr
+++ b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr
@@ -1,8 +1,10 @@
 error: unreachable pattern
   --> $DIR/exhaustiveness-unreachable-pattern.rs:8:9
    |
+LL |         (1 | 2,) => {}
+   |         -------- matches all the values already
 LL |         (1,) => {}
-   |         ^^^^
+   |         ^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/exhaustiveness-unreachable-pattern.rs:1:9
@@ -13,212 +15,310 @@ LL | #![deny(unreachable_patterns)]
 error: unreachable pattern
   --> $DIR/exhaustiveness-unreachable-pattern.rs:13:9
    |
+LL |         (1 | 2,) => {}
+   |         -------- matches all the values already
 LL |         (2,) => {}
-   |         ^^^^
+   |         ^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/exhaustiveness-unreachable-pattern.rs:19:9
    |
+LL |         (1,) => {}
+   |         ---- matches some of the same values
+LL |         (2,) => {}
+   |         ---- matches some of the same values
 LL |         (1 | 2,) => {}
-   |         ^^^^^^^^
+   |         ^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/exhaustiveness-unreachable-pattern.rs:24:9
    |
+LL |         (1 | 2, 3 | 4) => {}
+   |         -------------- matches all the values already
 LL |         (1, 3) => {}
-   |         ^^^^^^
+   |         ^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/exhaustiveness-unreachable-pattern.rs:25:9
    |
+LL |         (1 | 2, 3 | 4) => {}
+   |         -------------- matches all the values already
+LL |         (1, 3) => {}
 LL |         (1, 4) => {}
-   |         ^^^^^^
+   |         ^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/exhaustiveness-unreachable-pattern.rs:26:9
    |
+LL |         (1 | 2, 3 | 4) => {}
+   |         -------------- matches all the values already
+...
 LL |         (2, 4) => {}
-   |         ^^^^^^
+   |         ^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/exhaustiveness-unreachable-pattern.rs:27:9
    |
+LL |         (1 | 2, 3 | 4) => {}
+   |         -------------- matches all the values already
+...
 LL |         (2 | 1, 4) => {}
-   |         ^^^^^^^^^^
+   |         ^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/exhaustiveness-unreachable-pattern.rs:29:9
    |
+LL |         (1 | 2, 3 | 4) => {}
+   |         -------------- matches some of the same values
+...
+LL |         (1, 5 | 6) => {}
+   |         ---------- matches some of the same values
 LL |         (1, 4 | 5) => {}
-   |         ^^^^^^^^^^
+   |         ^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:37:9
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:34:13
    |
+LL |         (0, 0, 0) => {}
+   |             - matches all the values already
+LL |         (0, 0 | 1, 0) => {}
+   |             ^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:42:9
+   |
+LL |         (None | Some(1 | 2),) => {}
+   |         --------------------- matches all the values already
 LL |         (Some(1),) => {}
-   |         ^^^^^^^^^^
+   |         ^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:38:9
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:43:9
    |
+LL |         (None | Some(1 | 2),) => {}
+   |         --------------------- matches all the values already
+LL |         (Some(1),) => {}
 LL |         (None,) => {}
-   |         ^^^^^^^
+   |         ^^^^^^^ unreachable pattern
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:43:9
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:48:9
    |
+LL |         ((1 | 2,) | (3 | 4,),) => {}
+   |         ---------------------- matches all the values already
 LL |         ((1..=4,),) => {}
-   |         ^^^^^^^^^^^
+   |         ^^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:48:14
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:53:14
    |
 LL |         (1 | 1,) => {}
-   |              ^
+   |          -   ^ unreachable pattern
+   |          |
+   |          matches all the values already
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:52:19
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:57:19
    |
 LL |         (0 | 1) | 1 => {}
-   |                   ^
+   |              -    ^ unreachable pattern
+   |              |
+   |              matches all the values already
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:58:14
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:63:14
    |
 LL |         0 | (0 | 0) => {}
-   |              ^
+   |         -    ^ unreachable pattern
+   |         |
+   |         matches all the values already
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:58:18
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:63:18
    |
 LL |         0 | (0 | 0) => {}
-   |                  ^
+   |         -        ^ unreachable pattern
+   |         |
+   |         matches all the values already
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:66:13
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:71:13
    |
+LL |           Some(0) |
+   |           ------- matches all the values already
 LL | /             Some(
 LL | |                 0 | 0) => {}
-   | |______________________^
+   | |______________________^ unreachable pattern
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:72:15
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:77:15
    |
+LL |         [0
+   |          - matches all the values already
 LL |             | 0
-   |               ^
+   |               ^ unreachable pattern
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:74:15
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:79:15
    |
+LL |         , 0
+   |           - matches all the values already
 LL |             | 0] => {}
-   |               ^
+   |               ^ unreachable pattern
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:78:20
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:83:20
    |
 LL |         (true, 0 | 0) => {}
-   |                    ^
+   |                -   ^ unreachable pattern
+   |                |
+   |                matches all the values already
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:79:17
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:84:17
    |
+LL |         (true, 0 | 0) => {}
+   |                - matches some of the same values
 LL |         (_, 0 | 0) => {}
-   |                 ^
+   |             -   ^ unreachable pattern
+   |             |
+   |             matches some of the same values
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:87:10
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:92:10
    |
+LL |         [1, ..] => {}
+   |          - matches all the values already
 LL |         [1
-   |          ^
+   |          ^ unreachable pattern
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:99:10
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:104:10
    |
+LL |         [true, ..] => {}
+   |          ---- matches all the values already
 LL |         [true
-   |          ^^^^
+   |          ^^^^ unreachable pattern
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:106:36
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:111:36
    |
+LL |         (true, Some(_)) => {}
+   |                     - matches some of the same values
+LL |         (false, Some(true)) => {}
+   |                      ---- matches some of the same values
 LL |         (true | false, None | Some(true
-   |                                    ^^^^
+   |                                    ^^^^ unreachable pattern
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:111:14
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:116:14
    |
 LL |             (true
-   |              ^^^^
+   |              ^^^^ unreachable pattern
 ...
+LL |         (true, Some(_)) => {}
+   |                     - matches some of the same values
+LL |         (false, Some(true)) => {}
+   |                      ---- matches some of the same values
 LL |         (true | false, None | Some(t_or_f!())) => {}
    |                                    --------- in this macro invocation
    |
    = note: this error originates in the macro `t_or_f` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:122:14
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:127:14
    |
+LL |         Some(0) => {}
+   |              - matches all the values already
 LL |         Some(0
-   |              ^
+   |              ^ unreachable pattern
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:141:19
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:146:19
    |
+LL |         Some(false) => {}
+   |              ----- matches all the values already
+LL |         None | Some(true
 LL |                 | false) => {}
-   |                   ^^^^^
+   |                   ^^^^^ unreachable pattern
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:149:15
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:154:15
    |
+LL |         (false, true) => {}
+   |                 ---- matches some of the same values
+LL |         (true, true) => {}
+   |                ---- matches some of the same values
+LL |         (false | true, false
 LL |             | true) => {}
-   |               ^^^^
+   |               ^^^^ unreachable pattern
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:155:15
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:160:15
    |
+LL |         (true, false) => {}
+   |          ---- matches some of the same values
+LL |         (true, true) => {}
+   |          ---- matches some of the same values
+LL |         (false
 LL |             | true,
-   |               ^^^^
+   |               ^^^^ unreachable pattern
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:160:15
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:165:15
    |
+LL |         (x, y)
+   |         ------ matches any value
 LL |             | (y, x) => {}
-   |               ^^^^^^
+   |               ^^^^^^ unreachable pattern
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:164:30
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:169:30
    |
 LL | fn unreachable_in_param((_ | (_, _)): (bool, bool)) {}
-   |                              ^^^^^^
+   |                          -   ^^^^^^ unreachable pattern
+   |                          |
+   |                          matches any value
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:171:14
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:176:14
    |
 LL |     let (_ | (_, _)) = bool_pair;
-   |              ^^^^^^
+   |          -   ^^^^^^ unreachable pattern
+   |          |
+   |          matches any value
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:173:14
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:178:14
    |
 LL |     for (_ | (_, _)) in [bool_pair] {}
-   |              ^^^^^^
+   |          -   ^^^^^^ unreachable pattern
+   |          |
+   |          matches any value
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:176:20
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:181:20
    |
 LL |     let (Some(_) | Some(true)) = bool_option else { return };
-   |                    ^^^^^^^^^^
+   |          -------   ^^^^^^^^^^ unreachable pattern
+   |          |
+   |          matches all the values already
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:178:22
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:183:22
    |
 LL |     if let Some(_) | Some(true) = bool_option {}
-   |                      ^^^^^^^^^^
+   |            -------   ^^^^^^^^^^ unreachable pattern
+   |            |
+   |            matches all the values already
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:180:25
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:185:25
    |
 LL |     while let Some(_) | Some(true) = bool_option {}
-   |                         ^^^^^^^^^^
+   |               -------   ^^^^^^^^^^ unreachable pattern
+   |               |
+   |               matches all the values already
 
-error: aborting due to 35 previous errors
+error: aborting due to 36 previous errors
 
diff --git a/tests/ui/pattern/usefulness/consts-opaque.stderr b/tests/ui/pattern/usefulness/consts-opaque.stderr
index d057309e420..fdec3683c4a 100644
--- a/tests/ui/pattern/usefulness/consts-opaque.stderr
+++ b/tests/ui/pattern/usefulness/consts-opaque.stderr
@@ -106,20 +106,28 @@ LL |         _ => {} // should not be emitting unreachable warning
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:72:9
    |
+LL |         BAZ => {}
+   |         --- matches all the values already
 LL |         Baz::Baz1 => {} // should not be emitting unreachable warning
-   |         ^^^^^^^^^
+   |         ^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:79:9
    |
+LL |         Baz::Baz1 => {}
+   |         --------- matches all the values already
 LL |         BAZ => {}
-   |         ^^^
+   |         ^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:87:9
    |
+LL |         BAZ => {}
+   |         --- matches some of the same values
+LL |         Baz::Baz2 => {}
+   |         --------- matches some of the same values
 LL |         _ => {} // should not be emitting unreachable warning
-   |         ^
+   |         ^ unreachable pattern
 
 error: aborting due to 17 previous errors
 
diff --git a/tests/ui/pattern/usefulness/empty-match-check-notes.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-match-check-notes.exhaustive_patterns.stderr
index 4c434192431..9e700ee55ef 100644
--- a/tests/ui/pattern/usefulness/empty-match-check-notes.exhaustive_patterns.stderr
+++ b/tests/ui/pattern/usefulness/empty-match-check-notes.exhaustive_patterns.stderr
@@ -4,6 +4,7 @@ error: unreachable pattern
 LL |         _ => {}
    |         ^
    |
+   = note: this pattern matches no values because `EmptyEnum` is uninhabited
 note: the lint level is defined here
   --> $DIR/empty-match-check-notes.rs:7:9
    |
@@ -11,25 +12,31 @@ LL | #![deny(unreachable_patterns)]
    |         ^^^^^^^^^^^^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-match-check-notes.rs:20:9
+  --> $DIR/empty-match-check-notes.rs:21:9
    |
 LL |         _ if false => {}
    |         ^
+   |
+   = note: this pattern matches no values because `EmptyEnum` is uninhabited
 
 error: unreachable pattern
-  --> $DIR/empty-match-check-notes.rs:27:9
+  --> $DIR/empty-match-check-notes.rs:29:9
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `EmptyForeignEnum` is uninhabited
 
 error: unreachable pattern
-  --> $DIR/empty-match-check-notes.rs:30:9
+  --> $DIR/empty-match-check-notes.rs:33:9
    |
 LL |         _ if false => {}
    |         ^
+   |
+   = note: this pattern matches no values because `EmptyForeignEnum` is uninhabited
 
 error[E0005]: refutable pattern in local binding
-  --> $DIR/empty-match-check-notes.rs:35:9
+  --> $DIR/empty-match-check-notes.rs:39:9
    |
 LL |     let None = x;
    |         ^^^^ pattern `Some(_)` not covered
@@ -44,7 +51,7 @@ LL |     if let None = x { todo!() };
    |     ++              +++++++++++
 
 error[E0004]: non-exhaustive patterns: `0_u8..=u8::MAX` not covered
-  --> $DIR/empty-match-check-notes.rs:45:11
+  --> $DIR/empty-match-check-notes.rs:49:11
    |
 LL |     match 0u8 {
    |           ^^^ pattern `0_u8..=u8::MAX` not covered
diff --git a/tests/ui/pattern/usefulness/empty-match-check-notes.normal.stderr b/tests/ui/pattern/usefulness/empty-match-check-notes.normal.stderr
index 45f715dc7b2..480ae7095a6 100644
--- a/tests/ui/pattern/usefulness/empty-match-check-notes.normal.stderr
+++ b/tests/ui/pattern/usefulness/empty-match-check-notes.normal.stderr
@@ -4,6 +4,7 @@ error: unreachable pattern
 LL |         _ => {}
    |         ^
    |
+   = note: this pattern matches no values because `EmptyEnum` is uninhabited
 note: the lint level is defined here
   --> $DIR/empty-match-check-notes.rs:7:9
    |
@@ -11,25 +12,31 @@ LL | #![deny(unreachable_patterns)]
    |         ^^^^^^^^^^^^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-match-check-notes.rs:20:9
+  --> $DIR/empty-match-check-notes.rs:21:9
    |
 LL |         _ if false => {}
    |         ^
+   |
+   = note: this pattern matches no values because `EmptyEnum` is uninhabited
 
 error: unreachable pattern
-  --> $DIR/empty-match-check-notes.rs:27:9
+  --> $DIR/empty-match-check-notes.rs:29:9
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `EmptyForeignEnum` is uninhabited
 
 error: unreachable pattern
-  --> $DIR/empty-match-check-notes.rs:30:9
+  --> $DIR/empty-match-check-notes.rs:33:9
    |
 LL |         _ if false => {}
    |         ^
+   |
+   = note: this pattern matches no values because `EmptyForeignEnum` is uninhabited
 
 error[E0005]: refutable pattern in local binding
-  --> $DIR/empty-match-check-notes.rs:35:9
+  --> $DIR/empty-match-check-notes.rs:39:9
    |
 LL |     let None = x;
    |         ^^^^ pattern `Some(_)` not covered
@@ -43,7 +50,7 @@ LL |     if let None = x { todo!() };
    |     ++              +++++++++++
 
 error[E0004]: non-exhaustive patterns: `0_u8..=u8::MAX` not covered
-  --> $DIR/empty-match-check-notes.rs:45:11
+  --> $DIR/empty-match-check-notes.rs:49:11
    |
 LL |     match 0u8 {
    |           ^^^ pattern `0_u8..=u8::MAX` not covered
diff --git a/tests/ui/pattern/usefulness/empty-match-check-notes.rs b/tests/ui/pattern/usefulness/empty-match-check-notes.rs
index ea797bc7dd5..2eef283a21e 100644
--- a/tests/ui/pattern/usefulness/empty-match-check-notes.rs
+++ b/tests/ui/pattern/usefulness/empty-match-check-notes.rs
@@ -15,9 +15,11 @@ fn empty_enum(x: EmptyEnum) {
     match x {} // ok
     match x {
         _ => {} //~ ERROR unreachable pattern
+                //~^ NOTE matches no values
     }
     match x {
         _ if false => {} //~ ERROR unreachable pattern
+                         //~^ NOTE matches no values
     }
 }
 
@@ -25,9 +27,11 @@ fn empty_foreign_enum(x: empty::EmptyForeignEnum) {
     match x {} // ok
     match x {
         _ => {} //~ ERROR unreachable pattern
+                //~^ NOTE matches no values
     }
     match x {
         _ if false => {} //~ ERROR unreachable pattern
+                         //~^ NOTE matches no values
     }
 }
 
diff --git a/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr
index 45bdba94d78..416a50b87b5 100644
--- a/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr
+++ b/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr
@@ -4,6 +4,7 @@ error: unreachable pattern
 LL |         _ => {}
    |         ^
    |
+   = note: this pattern matches no values because `!` is uninhabited
 note: the lint level is defined here
   --> $DIR/empty-types.rs:17:9
    |
@@ -15,6 +16,8 @@ error: unreachable pattern
    |
 LL |         _x => {}
    |         ^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error[E0004]: non-exhaustive patterns: type `&!` is non-empty
   --> $DIR/empty-types.rs:58:11
@@ -36,24 +39,32 @@ error: unreachable pattern
    |
 LL |         (_, _) => {}
    |         ^^^^^^
+   |
+   = note: this pattern matches no values because `(u32, !)` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:80:9
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `(!, !)` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:83:9
    |
 LL |         (_, _) => {}
    |         ^^^^^^
+   |
+   = note: this pattern matches no values because `(!, !)` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:87:9
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error[E0004]: non-exhaustive patterns: `Ok(_)` not covered
   --> $DIR/empty-types.rs:91:11
@@ -79,12 +90,16 @@ error: unreachable pattern
    |
 LL |         Err(_) => {}
    |         ^^^^^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:104:9
    |
 LL |         Err(_) => {}
    |         ^^^^^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered
   --> $DIR/empty-types.rs:101:11
@@ -123,114 +138,152 @@ error: unreachable pattern
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:123:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:126:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:127:9
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:130:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:131:9
    |
 LL |         Err(_) => {}
    |         ^^^^^^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:140:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:143:13
    |
 LL |             _ if false => {}
    |             ^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:152:13
    |
 LL |             Some(_) => {}
    |             ^^^^^^^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:156:13
    |
+LL |             None => {}
+   |             ---- matches all the values already
 LL |             _ => {}
-   |             ^
+   |             ^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:208:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:213:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:218:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:223:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:229:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:288:9
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:291:9
    |
 LL |         (_, _) => {}
    |         ^^^^^^
+   |
+   = note: this pattern matches no values because `(!, !)` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:294:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:295:9
    |
 LL |         Err(_) => {}
    |         ^^^^^^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty
   --> $DIR/empty-types.rs:327:11
@@ -292,18 +345,24 @@ error: unreachable pattern
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `[!; 3]` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:372:9
    |
 LL |         [_, _, _] => {}
    |         ^^^^^^^^^
+   |
+   = note: this pattern matches no values because `[!; 3]` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:375:9
    |
 LL |         [_, ..] => {}
    |         ^^^^^^^
+   |
+   = note: this pattern matches no values because `[!; 3]` is uninhabited
 
 error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty
   --> $DIR/empty-types.rs:389:11
@@ -322,8 +381,10 @@ LL +     }
 error: unreachable pattern
   --> $DIR/empty-types.rs:396:9
    |
+LL |         [] => {}
+   |         -- matches all the values already
 LL |         _ => {}
-   |         ^
+   |         ^ unreachable pattern
 
 error[E0004]: non-exhaustive patterns: `[]` not covered
   --> $DIR/empty-types.rs:398:11
@@ -344,48 +405,66 @@ error: unreachable pattern
    |
 LL |         Some(_) => {}
    |         ^^^^^^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:422:9
    |
 LL |         Some(_a) => {}
    |         ^^^^^^^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:427:9
    |
+LL |         None => {}
+   |         ---- matches all the values already
+LL |         // !useful, !reachable
 LL |         _ => {}
-   |         ^
+   |         ^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:432:9
    |
+LL |         None => {}
+   |         ---- matches all the values already
+LL |         // !useful, !reachable
 LL |         _a => {}
-   |         ^^
+   |         ^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:604:9
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:607:9
    |
 LL |         _x => {}
    |         ^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:610:9
    |
 LL |         _ if false => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:613:9
    |
 LL |         _x if false => {}
    |         ^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: aborting due to 49 previous errors
 
diff --git a/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr b/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr
index 9b57c895eea..2e5511527d5 100644
--- a/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr
+++ b/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr
@@ -4,6 +4,7 @@ error: unreachable pattern
 LL |         _ => {}
    |         ^
    |
+   = note: this pattern matches no values because `!` is uninhabited
 note: the lint level is defined here
   --> $DIR/empty-types.rs:17:9
    |
@@ -15,6 +16,8 @@ error: unreachable pattern
    |
 LL |         _x => {}
    |         ^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error[E0004]: non-exhaustive patterns: type `&!` is non-empty
   --> $DIR/empty-types.rs:58:11
@@ -36,24 +39,32 @@ error: unreachable pattern
    |
 LL |         (_, _) => {}
    |         ^^^^^^
+   |
+   = note: this pattern matches no values because `(u32, !)` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:80:9
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `(!, !)` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:83:9
    |
 LL |         (_, _) => {}
    |         ^^^^^^
+   |
+   = note: this pattern matches no values because `(!, !)` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:87:9
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error[E0004]: non-exhaustive patterns: `Ok(_)` not covered
   --> $DIR/empty-types.rs:91:11
@@ -79,12 +90,16 @@ error: unreachable pattern
    |
 LL |         Err(_) => {}
    |         ^^^^^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:104:9
    |
 LL |         Err(_) => {}
    |         ^^^^^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered
   --> $DIR/empty-types.rs:101:11
@@ -137,60 +152,80 @@ error: unreachable pattern
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:123:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:126:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:127:9
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:130:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:131:9
    |
 LL |         Err(_) => {}
    |         ^^^^^^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:140:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:143:13
    |
 LL |             _ if false => {}
    |             ^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:152:13
    |
 LL |             Some(_) => {}
    |             ^^^^^^^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:156:13
    |
+LL |             None => {}
+   |             ---- matches all the values already
 LL |             _ => {}
-   |             ^
+   |             ^ unreachable pattern
 
 error[E0004]: non-exhaustive patterns: `Some(_)` not covered
   --> $DIR/empty-types.rs:165:15
@@ -216,54 +251,72 @@ error: unreachable pattern
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:213:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:218:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:223:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:229:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:288:9
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:291:9
    |
 LL |         (_, _) => {}
    |         ^^^^^^
+   |
+   = note: this pattern matches no values because `(!, !)` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:294:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:295:9
    |
 LL |         Err(_) => {}
    |         ^^^^^^
+   |
+   = note: this pattern matches no values because `Result<!, !>` is uninhabited
 
 error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
   --> $DIR/empty-types.rs:316:11
@@ -403,18 +456,24 @@ error: unreachable pattern
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `[!; 3]` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:372:9
    |
 LL |         [_, _, _] => {}
    |         ^^^^^^^^^
+   |
+   = note: this pattern matches no values because `[!; 3]` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:375:9
    |
 LL |         [_, ..] => {}
    |         ^^^^^^^
+   |
+   = note: this pattern matches no values because `[!; 3]` is uninhabited
 
 error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty
   --> $DIR/empty-types.rs:389:11
@@ -433,8 +492,10 @@ LL +     }
 error: unreachable pattern
   --> $DIR/empty-types.rs:396:9
    |
+LL |         [] => {}
+   |         -- matches all the values already
 LL |         _ => {}
-   |         ^
+   |         ^ unreachable pattern
 
 error[E0004]: non-exhaustive patterns: `[]` not covered
   --> $DIR/empty-types.rs:398:11
@@ -455,24 +516,34 @@ error: unreachable pattern
    |
 LL |         Some(_) => {}
    |         ^^^^^^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:422:9
    |
 LL |         Some(_a) => {}
    |         ^^^^^^^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:427:9
    |
+LL |         None => {}
+   |         ---- matches all the values already
+LL |         // !useful, !reachable
 LL |         _ => {}
-   |         ^
+   |         ^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:432:9
    |
+LL |         None => {}
+   |         ---- matches all the values already
+LL |         // !useful, !reachable
 LL |         _a => {}
-   |         ^^
+   |         ^^ unreachable pattern
 
 error[E0004]: non-exhaustive patterns: `&Some(_)` not covered
   --> $DIR/empty-types.rs:452:11
@@ -569,24 +640,32 @@ error: unreachable pattern
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:607:9
    |
 LL |         _x => {}
    |         ^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:610:9
    |
 LL |         _ if false => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:613:9
    |
 LL |         _x if false => {}
    |         ^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error[E0004]: non-exhaustive patterns: `&_` not covered
   --> $DIR/empty-types.rs:638:11
diff --git a/tests/ui/pattern/usefulness/empty-types.never_pats.stderr b/tests/ui/pattern/usefulness/empty-types.never_pats.stderr
index 0ff2472922e..4856a2f8e08 100644
--- a/tests/ui/pattern/usefulness/empty-types.never_pats.stderr
+++ b/tests/ui/pattern/usefulness/empty-types.never_pats.stderr
@@ -13,6 +13,7 @@ error: unreachable pattern
 LL |         _ => {}
    |         ^
    |
+   = note: this pattern matches no values because `!` is uninhabited
 note: the lint level is defined here
   --> $DIR/empty-types.rs:17:9
    |
@@ -24,6 +25,8 @@ error: unreachable pattern
    |
 LL |         _x => {}
    |         ^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error[E0004]: non-exhaustive patterns: type `&!` is non-empty
   --> $DIR/empty-types.rs:58:11
@@ -73,6 +76,8 @@ error: unreachable pattern
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(!)` not covered
   --> $DIR/empty-types.rs:91:11
@@ -218,12 +223,16 @@ error: unreachable pattern
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:143:13
    |
 LL |             _ if false => {}
    |             ^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error[E0004]: non-exhaustive patterns: `Some(!)` not covered
   --> $DIR/empty-types.rs:146:15
@@ -266,36 +275,48 @@ error: unreachable pattern
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:213:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:218:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:223:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:229:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:288:9
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
   --> $DIR/empty-types.rs:316:11
@@ -460,8 +481,10 @@ LL +     }
 error: unreachable pattern
   --> $DIR/empty-types.rs:396:9
    |
+LL |         [] => {}
+   |         -- matches all the values already
 LL |         _ => {}
-   |         ^
+   |         ^ unreachable pattern
 
 error[E0004]: non-exhaustive patterns: `[]` not covered
   --> $DIR/empty-types.rs:398:11
@@ -568,24 +591,32 @@ error: unreachable pattern
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:607:9
    |
 LL |         _x => {}
    |         ^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:610:9
    |
 LL |         _ if false => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:613:9
    |
 LL |         _x if false => {}
    |         ^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error[E0004]: non-exhaustive patterns: `&!` not covered
   --> $DIR/empty-types.rs:638:11
diff --git a/tests/ui/pattern/usefulness/empty-types.normal.stderr b/tests/ui/pattern/usefulness/empty-types.normal.stderr
index 1d13802a2bd..78db9ee349b 100644
--- a/tests/ui/pattern/usefulness/empty-types.normal.stderr
+++ b/tests/ui/pattern/usefulness/empty-types.normal.stderr
@@ -4,6 +4,7 @@ error: unreachable pattern
 LL |         _ => {}
    |         ^
    |
+   = note: this pattern matches no values because `!` is uninhabited
 note: the lint level is defined here
   --> $DIR/empty-types.rs:17:9
    |
@@ -15,6 +16,8 @@ error: unreachable pattern
    |
 LL |         _x => {}
    |         ^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error[E0004]: non-exhaustive patterns: type `&!` is non-empty
   --> $DIR/empty-types.rs:58:11
@@ -64,6 +67,8 @@ error: unreachable pattern
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered
   --> $DIR/empty-types.rs:91:11
@@ -209,12 +214,16 @@ error: unreachable pattern
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:143:13
    |
 LL |             _ if false => {}
    |             ^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error[E0004]: non-exhaustive patterns: `Some(_)` not covered
   --> $DIR/empty-types.rs:146:15
@@ -257,36 +266,48 @@ error: unreachable pattern
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:213:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:218:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:223:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:229:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:288:9
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
   --> $DIR/empty-types.rs:316:11
@@ -451,8 +472,10 @@ LL +     }
 error: unreachable pattern
   --> $DIR/empty-types.rs:396:9
    |
+LL |         [] => {}
+   |         -- matches all the values already
 LL |         _ => {}
-   |         ^
+   |         ^ unreachable pattern
 
 error[E0004]: non-exhaustive patterns: `[]` not covered
   --> $DIR/empty-types.rs:398:11
@@ -559,24 +582,32 @@ error: unreachable pattern
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:607:9
    |
 LL |         _x => {}
    |         ^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:610:9
    |
 LL |         _ if false => {}
    |         ^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/empty-types.rs:613:9
    |
 LL |         _x if false => {}
    |         ^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error[E0004]: non-exhaustive patterns: `&_` not covered
   --> $DIR/empty-types.rs:638:11
diff --git a/tests/ui/pattern/usefulness/explain-unreachable-pats.rs b/tests/ui/pattern/usefulness/explain-unreachable-pats.rs
new file mode 100644
index 00000000000..6a27f7d4a82
--- /dev/null
+++ b/tests/ui/pattern/usefulness/explain-unreachable-pats.rs
@@ -0,0 +1,97 @@
+#![feature(never_type)]
+#![feature(min_exhaustive_patterns)]
+#![deny(unreachable_patterns)]
+//~^ NOTE lint level is defined here
+
+#[rustfmt::skip]
+fn main() {
+    match (0u8,) {
+        (1 | 2,) => {}
+        //~^ NOTE matches all the values already
+        (2,) => {}
+        //~^ ERROR unreachable pattern
+        //~| NOTE unreachable pattern
+        _ => {}
+    }
+
+    match (0u8,) {
+        (1,) => {}
+        //~^ NOTE matches some of the same values
+        (2,) => {}
+        //~^ NOTE matches some of the same values
+        (1 | 2,) => {}
+        //~^ ERROR unreachable pattern
+        //~| NOTE unreachable pattern
+        _ => {}
+    }
+
+    let res: Result<(),!> = Ok(());
+    match res {
+        Ok(_) => {}
+        Err(_) => {}
+        //~^ ERROR unreachable pattern
+        //~| NOTE this pattern matches no values because `!` is uninhabited
+    }
+
+    #[derive(Copy, Clone)]
+    enum Void1 {}
+    #[derive(Copy, Clone)]
+    enum Void2 {}
+    // Only an empty type matched _by value_ can make an arm unreachable. We must get the right one.
+    let res1: Result<(), Void1> = Ok(());
+    let res2: Result<(), Void2> = Ok(());
+    match (&res1, res2) {
+        (Err(_), Err(_)) => {}
+        //~^ ERROR unreachable pattern
+        //~| NOTE this pattern matches no values because `Void2` is uninhabited
+        _ => {}
+    }
+    match (res1, &res2) {
+        (Err(_), Err(_)) => {}
+        //~^ ERROR unreachable pattern
+        //~| NOTE this pattern matches no values because `Void1` is uninhabited
+        _ => {}
+    }
+
+
+    if let (0
+        //~^ NOTE matches all the values already
+        | 0, _) = (0, 0) {}
+        //~^ ERROR unreachable pattern
+        //~| NOTE unreachable pattern
+
+    match (true, true) {
+        (_, true) if false => {} // Guarded patterns don't cover others
+        (true, _) => {}
+        //~^ NOTE matches some of the same values
+        (false, _) => {}
+        //~^ NOTE matches some of the same values
+        (_, true) => {}
+        //~^ ERROR unreachable pattern
+        //~| NOTE unreachable pattern
+    }
+
+    match (true, true) {
+        (true, _) => {}
+        //~^ NOTE matches all the values already
+        (false, _) => {}
+        #[allow(unreachable_patterns)]
+        (_, true) => {} // Doesn't cover below because it's already unreachable.
+        (true, true) => {}
+        //~^ ERROR unreachable pattern
+        //~| NOTE unreachable pattern
+    }
+
+    // Despite skipping some irrelevant cases, we still report a set of rows that covers the
+    // unreachable one.
+    match (true, true, 0) {
+        (true, _, _) => {}
+        (_, true, 0..10) => {}
+        //~^ NOTE matches all the values already
+        (_, true, 10..) => {}
+        (_, true, 3) => {}
+        //~^ ERROR unreachable pattern
+        //~| NOTE unreachable pattern
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/explain-unreachable-pats.stderr b/tests/ui/pattern/usefulness/explain-unreachable-pats.stderr
new file mode 100644
index 00000000000..7c2382c26b8
--- /dev/null
+++ b/tests/ui/pattern/usefulness/explain-unreachable-pats.stderr
@@ -0,0 +1,92 @@
+error: unreachable pattern
+  --> $DIR/explain-unreachable-pats.rs:11:9
+   |
+LL |         (1 | 2,) => {}
+   |         -------- matches all the values already
+LL |
+LL |         (2,) => {}
+   |         ^^^^ unreachable pattern
+   |
+note: the lint level is defined here
+  --> $DIR/explain-unreachable-pats.rs:3:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/explain-unreachable-pats.rs:22:9
+   |
+LL |         (1,) => {}
+   |         ---- matches some of the same values
+LL |
+LL |         (2,) => {}
+   |         ---- matches some of the same values
+LL |
+LL |         (1 | 2,) => {}
+   |         ^^^^^^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/explain-unreachable-pats.rs:31:9
+   |
+LL |         Err(_) => {}
+   |         ^^^^^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
+
+error: unreachable pattern
+  --> $DIR/explain-unreachable-pats.rs:44:9
+   |
+LL |         (Err(_), Err(_)) => {}
+   |         ^^^^^^^^^^^^^^^^
+   |
+   = note: this pattern matches no values because `Void2` is uninhabited
+
+error: unreachable pattern
+  --> $DIR/explain-unreachable-pats.rs:50:9
+   |
+LL |         (Err(_), Err(_)) => {}
+   |         ^^^^^^^^^^^^^^^^
+   |
+   = note: this pattern matches no values because `Void1` is uninhabited
+
+error: unreachable pattern
+  --> $DIR/explain-unreachable-pats.rs:59:11
+   |
+LL |     if let (0
+   |             - matches all the values already
+LL |
+LL |         | 0, _) = (0, 0) {}
+   |           ^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/explain-unreachable-pats.rs:69:9
+   |
+LL |         (true, _) => {}
+   |         --------- matches some of the same values
+LL |
+LL |         (false, _) => {}
+   |         ---------- matches some of the same values
+LL |
+LL |         (_, true) => {}
+   |         ^^^^^^^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/explain-unreachable-pats.rs:80:9
+   |
+LL |         (true, _) => {}
+   |         --------- matches all the values already
+...
+LL |         (true, true) => {}
+   |         ^^^^^^^^^^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/explain-unreachable-pats.rs:92:9
+   |
+LL |         (_, true, 0..10) => {}
+   |         ---------------- matches all the values already
+...
+LL |         (_, true, 3) => {}
+   |         ^^^^^^^^^^^^ unreachable pattern
+
+error: aborting due to 9 previous errors
+
diff --git a/tests/ui/pattern/usefulness/floats.stderr b/tests/ui/pattern/usefulness/floats.stderr
index 684f6c93a16..d0a8841d6a8 100644
--- a/tests/ui/pattern/usefulness/floats.stderr
+++ b/tests/ui/pattern/usefulness/floats.stderr
@@ -14,8 +14,10 @@ LL +         _ => todo!()
 error: unreachable pattern
   --> $DIR/floats.rs:18:9
    |
+LL |         0.01f16..=6.5f16 => {}
+   |         ---------------- matches all the values already
 LL |         0.01f16 => {}
-   |         ^^^^^^^
+   |         ^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/floats.rs:1:9
@@ -26,80 +28,118 @@ LL | #![deny(unreachable_patterns)]
 error: unreachable pattern
   --> $DIR/floats.rs:19:9
    |
+LL |         0.01f16..=6.5f16 => {}
+   |         ---------------- matches all the values already
+LL |         0.01f16 => {}
 LL |         0.02f16 => {}
-   |         ^^^^^^^
+   |         ^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/floats.rs:20:9
    |
+LL |         0.01f16..=6.5f16 => {}
+   |         ---------------- matches all the values already
+...
 LL |         6.5f16 => {}
-   |         ^^^^^^
+   |         ^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/floats.rs:31:9
    |
+LL |         0.01f32..=6.5f32 => {}
+   |         ---------------- matches all the values already
 LL |         0.01f32 => {}
-   |         ^^^^^^^
+   |         ^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/floats.rs:32:9
    |
+LL |         0.01f32..=6.5f32 => {}
+   |         ---------------- matches all the values already
+LL |         0.01f32 => {}
 LL |         0.02f32 => {}
-   |         ^^^^^^^
+   |         ^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/floats.rs:33:9
    |
+LL |         0.01f32..=6.5f32 => {}
+   |         ---------------- matches all the values already
+...
 LL |         6.5f32 => {}
-   |         ^^^^^^
+   |         ^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/floats.rs:45:9
    |
+LL |         0.01f64..=6.5f64 => {}
+   |         ---------------- matches all the values already
+LL |         0.005f64 => {}
 LL |         0.01f64 => {}
-   |         ^^^^^^^
+   |         ^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/floats.rs:46:9
    |
+LL |         0.01f64..=6.5f64 => {}
+   |         ---------------- matches all the values already
+...
 LL |         0.02f64 => {}
-   |         ^^^^^^^
+   |         ^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/floats.rs:47:9
    |
+LL |         0.01f64..=6.5f64 => {}
+   |         ---------------- matches all the values already
+...
 LL |         6.5f64 => {}
-   |         ^^^^^^
+   |         ^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/floats.rs:49:9
    |
+LL |         0.01f64..=6.5f64 => {}
+   |         ---------------- matches all the values already
+...
 LL |         1.0f64..=4.0f64 => {}
-   |         ^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/floats.rs:62:9
    |
+LL |         0.01f128..=6.5f128 => {}
+   |         ------------------ matches all the values already
+LL |         0.005f128 => {}
 LL |         0.01f128 => {}
-   |         ^^^^^^^^
+   |         ^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/floats.rs:63:9
    |
+LL |         0.01f128..=6.5f128 => {}
+   |         ------------------ matches all the values already
+...
 LL |         0.02f128 => {}
-   |         ^^^^^^^^
+   |         ^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/floats.rs:64:9
    |
+LL |         0.01f128..=6.5f128 => {}
+   |         ------------------ matches all the values already
+...
 LL |         6.5f128 => {}
-   |         ^^^^^^^
+   |         ^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/floats.rs:66:9
    |
+LL |         0.01f128..=6.5f128 => {}
+   |         ------------------ matches all the values already
+...
 LL |         1.0f128..=4.0f128 => {}
-   |         ^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^ unreachable pattern
 
 error: aborting due to 15 previous errors
 
diff --git a/tests/ui/pattern/usefulness/impl-trait.stderr b/tests/ui/pattern/usefulness/impl-trait.stderr
index ba8b12f9f66..c079f5a259d 100644
--- a/tests/ui/pattern/usefulness/impl-trait.stderr
+++ b/tests/ui/pattern/usefulness/impl-trait.stderr
@@ -4,6 +4,7 @@ error: unreachable pattern
 LL |             _ => {}
    |             ^
    |
+   = note: this pattern matches no values because `Void` is uninhabited
 note: the lint level is defined here
   --> $DIR/impl-trait.rs:5:9
    |
@@ -15,36 +16,48 @@ error: unreachable pattern
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/impl-trait.rs:45:13
    |
 LL |             Some(_) => {}
    |             ^^^^^^^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/impl-trait.rs:49:13
    |
+LL |             None => {}
+   |             ---- matches all the values already
 LL |             _ => {}
-   |             ^
+   |             ^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/impl-trait.rs:59:13
    |
 LL |             Some(_) => {}
    |             ^^^^^^^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/impl-trait.rs:63:13
    |
+LL |             None => {}
+   |             ---- matches all the values already
 LL |             _ => {}
-   |             ^
+   |             ^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/impl-trait.rs:76:9
    |
 LL |         _ => {}
    |         ^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/impl-trait.rs:86:9
@@ -59,12 +72,16 @@ error: unreachable pattern
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/impl-trait.rs:105:9
    |
+LL |         Some((a, b)) => {}
+   |         ------------ matches all the values already
 LL |         Some((mut x, mut y)) => {
-   |         ^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/impl-trait.rs:124:13
@@ -79,12 +96,16 @@ error: unreachable pattern
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `SecretelyVoid` is uninhabited
 
 error: unreachable pattern
   --> $DIR/impl-trait.rs:151:13
    |
 LL |             _ => {}
    |             ^
+   |
+   = note: this pattern matches no values because `SecretelyDoubleVoid` is uninhabited
 
 error[E0004]: non-exhaustive patterns: type `impl Copy` is non-empty
   --> $DIR/impl-trait.rs:23:11
diff --git a/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr b/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr
index c5b028d2038..8cf81822659 100644
--- a/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr
+++ b/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr
@@ -2,7 +2,9 @@ error: unreachable pattern
   --> $DIR/reachability.rs:18:17
    |
 LL |     m!(0u8, 42, 42);
-   |                 ^^
+   |             --  ^^ unreachable pattern
+   |             |
+   |             matches all the values already
    |
 note: the lint level is defined here
   --> $DIR/reachability.rs:3:9
@@ -14,121 +16,181 @@ error: unreachable pattern
   --> $DIR/reachability.rs:22:22
    |
 LL |     m!(0u8, 20..=30, 20);
-   |                      ^^
+   |             -------  ^^ unreachable pattern
+   |             |
+   |             matches all the values already
 
 error: unreachable pattern
   --> $DIR/reachability.rs:23:22
    |
 LL |     m!(0u8, 20..=30, 21);
-   |                      ^^
+   |             -------  ^^ unreachable pattern
+   |             |
+   |             matches all the values already
 
 error: unreachable pattern
   --> $DIR/reachability.rs:24:22
    |
 LL |     m!(0u8, 20..=30, 25);
-   |                      ^^
+   |             -------  ^^ unreachable pattern
+   |             |
+   |             matches all the values already
 
 error: unreachable pattern
   --> $DIR/reachability.rs:25:22
    |
 LL |     m!(0u8, 20..=30, 29);
-   |                      ^^
+   |             -------  ^^ unreachable pattern
+   |             |
+   |             matches all the values already
 
 error: unreachable pattern
   --> $DIR/reachability.rs:26:22
    |
 LL |     m!(0u8, 20..=30, 30);
-   |                      ^^
+   |             -------  ^^ unreachable pattern
+   |             |
+   |             matches all the values already
 
 error: unreachable pattern
   --> $DIR/reachability.rs:29:21
    |
 LL |     m!(0u8, 20..30, 20);
-   |                     ^^
+   |             ------  ^^ unreachable pattern
+   |             |
+   |             matches all the values already
 
 error: unreachable pattern
   --> $DIR/reachability.rs:30:21
    |
 LL |     m!(0u8, 20..30, 21);
-   |                     ^^
+   |             ------  ^^ unreachable pattern
+   |             |
+   |             matches all the values already
 
 error: unreachable pattern
   --> $DIR/reachability.rs:31:21
    |
 LL |     m!(0u8, 20..30, 25);
-   |                     ^^
+   |             ------  ^^ unreachable pattern
+   |             |
+   |             matches all the values already
 
 error: unreachable pattern
   --> $DIR/reachability.rs:32:21
    |
 LL |     m!(0u8, 20..30, 29);
-   |                     ^^
+   |             ------  ^^ unreachable pattern
+   |             |
+   |             matches all the values already
 
 error: unreachable pattern
   --> $DIR/reachability.rs:36:22
    |
 LL |     m!(0u8, 20..=30, 20..=30);
-   |                      ^^^^^^^
+   |             -------  ^^^^^^^ unreachable pattern
+   |             |
+   |             matches all the values already
 
 error: unreachable pattern
   --> $DIR/reachability.rs:37:22
    |
 LL |     m!(0u8, 20.. 30, 20.. 30);
-   |                      ^^^^^^^
+   |             -------  ^^^^^^^ unreachable pattern
+   |             |
+   |             matches all the values already
 
 error: unreachable pattern
   --> $DIR/reachability.rs:38:22
    |
 LL |     m!(0u8, 20..=30, 20.. 30);
-   |                      ^^^^^^^
+   |             -------  ^^^^^^^ unreachable pattern
+   |             |
+   |             matches all the values already
 
 error: unreachable pattern
   --> $DIR/reachability.rs:40:22
    |
 LL |     m!(0u8, 20..=30, 21..=30);
-   |                      ^^^^^^^
+   |             -------  ^^^^^^^ unreachable pattern
+   |             |
+   |             matches all the values already
 
 error: unreachable pattern
   --> $DIR/reachability.rs:41:22
    |
 LL |     m!(0u8, 20..=30, 20..=29);
-   |                      ^^^^^^^
+   |             -------  ^^^^^^^ unreachable pattern
+   |             |
+   |             matches all the values already
 
 error: unreachable pattern
   --> $DIR/reachability.rs:43:24
    |
 LL |     m!('a', 'A'..='z', 'a'..='z');
-   |                        ^^^^^^^^^
+   |             ---------  ^^^^^^^^^ unreachable pattern
+   |             |
+   |             matches all the values already
 
 error: unreachable pattern
   --> $DIR/reachability.rs:50:9
    |
+LL |         5 => {},
+   |         - matches some of the same values
+LL |         6 => {},
+   |         - matches some of the same values
+LL |         7 => {},
+   |         - matches some of the same values
+LL |         8 => {},
+   |         - matches some of the same values
 LL |         5..=8 => {},
-   |         ^^^^^
+   |         ^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/reachability.rs:56:9
    |
+LL |         0..10 => {},
+   |         ----- matches some of the same values
+LL |         10..20 => {},
+   |         ------ matches some of the same values
 LL |         5..15 => {},
-   |         ^^^^^
+   |         ^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/reachability.rs:63:9
    |
+LL |         0..10 => {},
+   |         ----- matches some of the same values
+LL |         10..20 => {},
+   |         ------ matches some of the same values
+LL |         20..30 => {},
+   |         ------ matches some of the same values
 LL |         5..25 => {},
-   |         ^^^^^
+   |         ^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/reachability.rs:71:9
    |
+LL |         0..10 => {},
+   |         ----- matches some of the same values
+LL |         10 => {},
+   |         -- matches some of the same values
+LL |         11..=23 => {},
+   |         ------- matches some of the same values
+LL |         19..30 => {},
+   |         ------ matches some of the same values
 LL |         5..25 => {},
-   |         ^^^^^
+   |         ^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/reachability.rs:77:9
    |
+LL |         0..10 => {},
+   |         ----- matches some of the same values
+LL |         10..20 => {},
+   |         ------ matches some of the same values
 LL |         5..15 => {},
-   |         ^^^^^
+   |         ^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/reachability.rs:84:9
@@ -141,20 +203,29 @@ LL |         '\u{D7FF}'..='\u{E000}' => {},
 error: unreachable pattern
   --> $DIR/reachability.rs:89:9
    |
+LL |         '\u{0}'..='\u{D7FF}' => {},
+   |         -------------------- matches some of the same values
+LL |         '\u{E000}'..='\u{10_FFFF}' => {},
+   |         -------------------------- matches some of the same values
 LL |         '\u{D7FF}'..='\u{E000}' => {},
-   |         ^^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/reachability.rs:105:9
    |
+LL |         &42 => {}
+   |         --- matches all the values already
 LL |         &FOO => {}
-   |         ^^^^
+   |         ^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/reachability.rs:106:9
    |
+LL |         &42 => {}
+   |         --- matches all the values already
+LL |         &FOO => {}
 LL |         BAR => {}
-   |         ^^^
+   |         ^^^ unreachable pattern
 
 error: aborting due to 25 previous errors
 
diff --git a/tests/ui/pattern/usefulness/issue-12116.stderr b/tests/ui/pattern/usefulness/issue-12116.stderr
index 199bdc6ac97..b2c2be97563 100644
--- a/tests/ui/pattern/usefulness/issue-12116.stderr
+++ b/tests/ui/pattern/usefulness/issue-12116.stderr
@@ -1,8 +1,10 @@
 error: unreachable pattern
   --> $DIR/issue-12116.rs:15:9
    |
+LL |         &IntList::Cons(val, box ref next_list) => tail(next_list),
+   |         -------------------------------------- matches all the values already
 LL |         &IntList::Cons(val, box IntList::Nil)  => IntList::Cons(val, Box::new(IntList::Nil)),
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/issue-12116.rs:4:9
diff --git a/tests/ui/pattern/usefulness/issue-12369.stderr b/tests/ui/pattern/usefulness/issue-12369.stderr
index 2b6e2e14b7c..dbd6bab6aeb 100644
--- a/tests/ui/pattern/usefulness/issue-12369.stderr
+++ b/tests/ui/pattern/usefulness/issue-12369.stderr
@@ -1,8 +1,12 @@
 error: unreachable pattern
   --> $DIR/issue-12369.rs:9:9
    |
+LL |         &[a,b,c] => 3,
+   |         -------- matches some of the same values
+LL |         &[a, ref rest @ ..] => a,
+   |         ------------------- matches some of the same values
 LL |         &[10,a, ref rest @ ..] => 10
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/issue-12369.rs:1:9
diff --git a/tests/ui/pattern/usefulness/issue-13727.stderr b/tests/ui/pattern/usefulness/issue-13727.stderr
index ab80c56ea88..ca8533b33a4 100644
--- a/tests/ui/pattern/usefulness/issue-13727.stderr
+++ b/tests/ui/pattern/usefulness/issue-13727.stderr
@@ -1,8 +1,10 @@
 error: unreachable pattern
   --> $DIR/issue-13727.rs:7:5
    |
+LL |     256 => print!("0b1110\n"),
+   |     --- matches all the values already
 LL |     512 => print!("0b1111\n"),
-   |     ^^^
+   |     ^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/issue-13727.rs:2:9
diff --git a/tests/ui/pattern/usefulness/issue-30240-b.stderr b/tests/ui/pattern/usefulness/issue-30240-b.stderr
index 74d39eba98c..749515fc94b 100644
--- a/tests/ui/pattern/usefulness/issue-30240-b.stderr
+++ b/tests/ui/pattern/usefulness/issue-30240-b.stderr
@@ -2,7 +2,9 @@ error: unreachable pattern
   --> $DIR/issue-30240-b.rs:12:9
    |
 LL |         "hello" => {}
-   |         ^^^^^^^
+   |         ------- matches all the values already
+LL |         "hello" => {}
+   |         ^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/issue-30240-b.rs:1:9
diff --git a/tests/ui/pattern/usefulness/issue-31221.stderr b/tests/ui/pattern/usefulness/issue-31221.stderr
index 7d349144456..f564422faae 100644
--- a/tests/ui/pattern/usefulness/issue-31221.stderr
+++ b/tests/ui/pattern/usefulness/issue-31221.stderr
@@ -23,8 +23,10 @@ LL |         &Var2 => (),
 error: unreachable pattern
   --> $DIR/issue-31221.rs:31:9
    |
+LL |         (Var1, b) => (),
+   |         --------- matches some of the same values
 LL |         (c, d) => (),
-   |         ------ matches any value
+   |         ------ matches some of the same values
 LL |         anything => ()
    |         ^^^^^^^^ unreachable pattern
 
diff --git a/tests/ui/pattern/usefulness/issue-57472.stderr b/tests/ui/pattern/usefulness/issue-57472.stderr
index c814eaec0d1..68b5b7cb791 100644
--- a/tests/ui/pattern/usefulness/issue-57472.stderr
+++ b/tests/ui/pattern/usefulness/issue-57472.stderr
@@ -1,8 +1,10 @@
 error: unreachable pattern
   --> $DIR/issue-57472.rs:15:13
    |
+LL |             Punned { foo: [_], .. } => println!("foo"),
+   |             ----------------------- matches all the values already
 LL |             Punned { bar: [_], .. } => println!("bar"),
-   |             ^^^^^^^^^^^^^^^^^^^^^^^
+   |             ^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/issue-57472.rs:2:9
@@ -13,8 +15,10 @@ LL | #![deny(unreachable_patterns)]
 error: unreachable pattern
   --> $DIR/issue-57472.rs:32:17
    |
+LL |                 Punned { foo: [_] } => println!("foo"),
+   |                 ------------------- matches all the values already
 LL |                 Punned { bar: [_] } => println!("bar"),
-   |                 ^^^^^^^^^^^^^^^^^^^
+   |                 ^^^^^^^^^^^^^^^^^^^ unreachable pattern
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/pattern/usefulness/match-arm-statics.stderr b/tests/ui/pattern/usefulness/match-arm-statics.stderr
index a5dffebf699..37f1ac58a16 100644
--- a/tests/ui/pattern/usefulness/match-arm-statics.stderr
+++ b/tests/ui/pattern/usefulness/match-arm-statics.stderr
@@ -1,8 +1,11 @@
 error: unreachable pattern
   --> $DIR/match-arm-statics.rs:25:9
    |
+LL |         TRUE_TRUE => (),
+   |         --------- matches all the values already
+...
 LL |         (true, true) => ()
-   |         ^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/match-arm-statics.rs:2:9
@@ -13,14 +16,23 @@ LL | #![deny(unreachable_patterns)]
 error: unreachable pattern
   --> $DIR/match-arm-statics.rs:40:9
    |
+LL |         Some(Some(EAST)) => (),
+   |         ---------------- matches all the values already
+...
 LL |         Some(Some(East)) => (),
-   |         ^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/match-arm-statics.rs:60:9
    |
+LL |         Foo { bar: _, baz: NEW_FALSE } => (),
+   |         ------------------------------ matches some of the same values
+...
+LL |         Foo { bar: Some(EAST), .. } => (),
+   |         --------------------------- matches some of the same values
+LL |         Foo { bar: Some(North), baz: NewBool(true) } => (),
 LL |         Foo { bar: Some(EAST), baz: NewBool(false) } => ()
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/pattern/usefulness/match-byte-array-patterns.stderr b/tests/ui/pattern/usefulness/match-byte-array-patterns.stderr
index 0c582be8df8..39675e2bdd4 100644
--- a/tests/ui/pattern/usefulness/match-byte-array-patterns.stderr
+++ b/tests/ui/pattern/usefulness/match-byte-array-patterns.stderr
@@ -1,8 +1,10 @@
 error: unreachable pattern
   --> $DIR/match-byte-array-patterns.rs:8:9
    |
+LL |         b"AAAA" => {},
+   |         ------- matches all the values already
 LL |         &[0x41, 0x41, 0x41, 0x41] => {}
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/match-byte-array-patterns.rs:1:9
@@ -13,44 +15,58 @@ LL | #![deny(unreachable_patterns)]
 error: unreachable pattern
   --> $DIR/match-byte-array-patterns.rs:14:9
    |
+LL |         &[0x41, 0x41, 0x41, 0x41] => {}
+   |         ------------------------- matches all the values already
 LL |         b"AAAA" => {},
-   |         ^^^^^^^
+   |         ^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/match-byte-array-patterns.rs:20:9
    |
+LL |         &[_, 0x41, 0x41, 0x41] => {},
+   |         ---------------------- matches all the values already
 LL |         b"AAAA" => {},
-   |         ^^^^^^^
+   |         ^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/match-byte-array-patterns.rs:26:9
    |
+LL |         &[0x41, .., 0x41] => {}
+   |         ----------------- matches all the values already
 LL |         b"AAAA" => {},
-   |         ^^^^^^^
+   |         ^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/match-byte-array-patterns.rs:34:9
    |
+LL |         b"AAAA" => {},
+   |         ------- matches all the values already
 LL |         &[0x41, 0x41, 0x41, 0x41] => {}
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/match-byte-array-patterns.rs:40:9
    |
+LL |         &[0x41, 0x41, 0x41, 0x41] => {}
+   |         ------------------------- matches all the values already
 LL |         b"AAAA" => {},
-   |         ^^^^^^^
+   |         ^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/match-byte-array-patterns.rs:46:9
    |
+LL |         &[_, 0x41, 0x41, 0x41] => {},
+   |         ---------------------- matches all the values already
 LL |         b"AAAA" => {},
-   |         ^^^^^^^
+   |         ^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/match-byte-array-patterns.rs:52:9
    |
+LL |         &[0x41, .., 0x41] => {}
+   |         ----------------- matches all the values already
 LL |         b"AAAA" => {},
-   |         ^^^^^^^
+   |         ^^^^^^^ unreachable pattern
 
 error: aborting due to 8 previous errors
 
diff --git a/tests/ui/pattern/usefulness/match-ref-ice.stderr b/tests/ui/pattern/usefulness/match-ref-ice.stderr
index 0dbfa776f69..9c5af47cc1e 100644
--- a/tests/ui/pattern/usefulness/match-ref-ice.stderr
+++ b/tests/ui/pattern/usefulness/match-ref-ice.stderr
@@ -1,8 +1,10 @@
 error: unreachable pattern
   --> $DIR/match-ref-ice.rs:13:9
    |
+LL |         [1, ref _madoka, 3] => (),
+   |         ------------------- matches all the values already
 LL |         [1, 2, 3] => (),
-   |         ^^^^^^^^^
+   |         ^^^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/match-ref-ice.rs:1:9
diff --git a/tests/ui/pattern/usefulness/match-vec-fixed.stderr b/tests/ui/pattern/usefulness/match-vec-fixed.stderr
index e388a06cb9a..04507a22856 100644
--- a/tests/ui/pattern/usefulness/match-vec-fixed.stderr
+++ b/tests/ui/pattern/usefulness/match-vec-fixed.stderr
@@ -2,7 +2,9 @@ error: unreachable pattern
   --> $DIR/match-vec-fixed.rs:7:9
    |
 LL |         [_, _, _] => {}
-   |         ^^^^^^^^^
+   |         --------- matches all the values already
+LL |         [_, _, _] => {}
+   |         ^^^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/match-vec-fixed.rs:1:9
@@ -14,7 +16,9 @@ error: unreachable pattern
   --> $DIR/match-vec-fixed.rs:11:9
    |
 LL |         [_, 1, _] => {}
-   |         ^^^^^^^^^
+   |         --------- matches all the values already
+LL |         [_, 1, _] => {}
+   |         ^^^^^^^^^ unreachable pattern
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/pattern/usefulness/match-vec-unreachable.stderr b/tests/ui/pattern/usefulness/match-vec-unreachable.stderr
index 672fd92fb5e..865f5b319a7 100644
--- a/tests/ui/pattern/usefulness/match-vec-unreachable.stderr
+++ b/tests/ui/pattern/usefulness/match-vec-unreachable.stderr
@@ -1,8 +1,10 @@
 error: unreachable pattern
   --> $DIR/match-vec-unreachable.rs:8:9
    |
+LL |         [a, (2, 3), _] => (),
+   |         -------------- matches all the values already
 LL |         [(1, 2), (2, 3), b] => (),
-   |         ^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/match-vec-unreachable.rs:1:9
@@ -13,14 +15,18 @@ LL | #![deny(unreachable_patterns)]
 error: unreachable pattern
   --> $DIR/match-vec-unreachable.rs:18:9
    |
+LL |         [ref a, _, _, ..] => { println!("{}", a); }
+   |         ----------------- matches all the values already
 LL |         [_, _, _, _, _] => { }
-   |         ^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/match-vec-unreachable.rs:26:9
    |
+LL |         ['a', 'b', 'c', ref _tail @ ..] => {}
+   |         ------------------------------- matches all the values already
 LL |         ['a', 'b', 'c'] => {}
-   |         ^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^ unreachable pattern
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/pattern/usefulness/slice-pattern-const-2.stderr b/tests/ui/pattern/usefulness/slice-pattern-const-2.stderr
index dcad11a38a7..12db48590a4 100644
--- a/tests/ui/pattern/usefulness/slice-pattern-const-2.stderr
+++ b/tests/ui/pattern/usefulness/slice-pattern-const-2.stderr
@@ -1,8 +1,11 @@
 error: unreachable pattern
   --> $DIR/slice-pattern-const-2.rs:9:9
    |
+LL |         MAGIC_TEST => (),
+   |         ---------- matches all the values already
+LL |         [0x00, 0x00, 0x00, 0x00] => (),
 LL |         [4, 5, 6, 7] => (),
-   |         ^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/slice-pattern-const-2.rs:1:9
@@ -13,20 +16,26 @@ LL | #![deny(unreachable_patterns)]
 error: unreachable pattern
   --> $DIR/slice-pattern-const-2.rs:15:9
    |
+LL |         MAGIC_TEST => (),
+   |         ---------- matches all the values already
 LL |         [4, 5, 6, 7] => (),
-   |         ^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/slice-pattern-const-2.rs:21:9
    |
+LL |         [4, 5, 6, 7] => (),
+   |         ------------ matches all the values already
 LL |         MAGIC_TEST => (),
-   |         ^^^^^^^^^^
+   |         ^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/slice-pattern-const-2.rs:28:9
    |
+LL |         [4] => (),
+   |         --- matches all the values already
 LL |         FOO => (),
-   |         ^^^
+   |         ^^^ unreachable pattern
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/pattern/usefulness/slice-pattern-const-3.stderr b/tests/ui/pattern/usefulness/slice-pattern-const-3.stderr
index b90b3a88a18..5a66799d9c9 100644
--- a/tests/ui/pattern/usefulness/slice-pattern-const-3.stderr
+++ b/tests/ui/pattern/usefulness/slice-pattern-const-3.stderr
@@ -1,8 +1,11 @@
 error: unreachable pattern
   --> $DIR/slice-pattern-const-3.rs:9:9
    |
+LL |         MAGIC_TEST => (),
+   |         ---------- matches all the values already
+LL |         ["0x00", "0x00", "0x00", "0x00"] => (),
 LL |         ["4", "5", "6", "7"] => (),
-   |         ^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/slice-pattern-const-3.rs:1:9
@@ -13,20 +16,26 @@ LL | #![deny(unreachable_patterns)]
 error: unreachable pattern
   --> $DIR/slice-pattern-const-3.rs:15:9
    |
+LL |         MAGIC_TEST => (),
+   |         ---------- matches all the values already
 LL |         ["4", "5", "6", "7"] => (),
-   |         ^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/slice-pattern-const-3.rs:21:9
    |
+LL |         ["4", "5", "6", "7"] => (),
+   |         -------------------- matches all the values already
 LL |         MAGIC_TEST => (),
-   |         ^^^^^^^^^^
+   |         ^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/slice-pattern-const-3.rs:28:9
    |
+LL |         ["boo"] => (),
+   |         ------- matches all the values already
 LL |         FOO => (),
-   |         ^^^
+   |         ^^^ unreachable pattern
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/pattern/usefulness/slice-pattern-const.stderr b/tests/ui/pattern/usefulness/slice-pattern-const.stderr
index 1fffb9fedbf..87a85acc4c5 100644
--- a/tests/ui/pattern/usefulness/slice-pattern-const.stderr
+++ b/tests/ui/pattern/usefulness/slice-pattern-const.stderr
@@ -1,8 +1,11 @@
 error: unreachable pattern
   --> $DIR/slice-pattern-const.rs:9:9
    |
+LL |         MAGIC_TEST => (),
+   |         ---------- matches all the values already
+LL |         [0x00, 0x00, 0x00, 0x00] => (),
 LL |         [84, 69, 83, 84] => (),
-   |         ^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/slice-pattern-const.rs:1:9
@@ -13,50 +16,68 @@ LL | #![deny(unreachable_patterns)]
 error: unreachable pattern
   --> $DIR/slice-pattern-const.rs:15:9
    |
+LL |         MAGIC_TEST => (),
+   |         ---------- matches all the values already
 LL |         [84, 69, 83, 84] => (),
-   |         ^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/slice-pattern-const.rs:21:9
    |
+LL |         [84, 69, 83, 84] => (),
+   |         ---------------- matches all the values already
 LL |         MAGIC_TEST => (),
-   |         ^^^^^^^^^^
+   |         ^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/slice-pattern-const.rs:28:9
    |
+LL |         [4] => (),
+   |         --- matches all the values already
 LL |         FOO => (),
-   |         ^^^
+   |         ^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/slice-pattern-const.rs:35:9
    |
+LL |         [4] => (),
+   |         --- matches all the values already
 LL |         BAR => (),
-   |         ^^^
+   |         ^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/slice-pattern-const.rs:43:9
    |
+LL |         [] => (),
+   |         -- matches all the values already
 LL |         BOO => (),
-   |         ^^^
+   |         ^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/slice-pattern-const.rs:44:9
    |
+LL |         [] => (),
+   |         -- matches all the values already
+LL |         BOO => (),
 LL |         b"" => (),
-   |         ^^^
+   |         ^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/slice-pattern-const.rs:45:9
    |
+LL |         [] => (),
+   |         -- matches all the values already
+...
 LL |         _ => (),
-   |         ^
+   |         ^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/slice-pattern-const.rs:51:9
    |
+LL |         CONST1 => {}
+   |         ------ matches all the values already
 LL |         [true] => {}
-   |         ^^^^^^
+   |         ^^^^^^ unreachable pattern
 
 error: aborting due to 9 previous errors
 
diff --git a/tests/ui/pattern/usefulness/slice-patterns-reachability.stderr b/tests/ui/pattern/usefulness/slice-patterns-reachability.stderr
index 607ffb76595..40fbb00de1f 100644
--- a/tests/ui/pattern/usefulness/slice-patterns-reachability.stderr
+++ b/tests/ui/pattern/usefulness/slice-patterns-reachability.stderr
@@ -2,7 +2,9 @@ error: unreachable pattern
   --> $DIR/slice-patterns-reachability.rs:8:9
    |
 LL |         [true, ..] => {}
-   |         ^^^^^^^^^^
+   |         ---------- matches all the values already
+LL |         [true, ..] => {}
+   |         ^^^^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/slice-patterns-reachability.rs:1:9
@@ -13,32 +15,45 @@ LL | #![deny(unreachable_patterns)]
 error: unreachable pattern
   --> $DIR/slice-patterns-reachability.rs:9:9
    |
+LL |         [true, ..] => {}
+   |         ---------- matches all the values already
+LL |         [true, ..] => {}
 LL |         [true] => {}
-   |         ^^^^^^
+   |         ^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/slice-patterns-reachability.rs:14:9
    |
 LL |         [.., true] => {}
-   |         ^^^^^^^^^^
+   |         ---------- matches all the values already
+LL |         [.., true] => {}
+   |         ^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/slice-patterns-reachability.rs:15:9
    |
+LL |         [.., true] => {}
+   |         ---------- matches all the values already
+LL |         [.., true] => {}
 LL |         [true] => {}
-   |         ^^^^^^
+   |         ^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/slice-patterns-reachability.rs:20:9
    |
 LL |         [false, .., true] => {}
-   |         ^^^^^^^^^^^^^^^^^
+   |         ----------------- matches all the values already
+LL |         [false, .., true] => {}
+   |         ^^^^^^^^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/slice-patterns-reachability.rs:21:9
    |
+LL |         [false, .., true] => {}
+   |         ----------------- matches all the values already
+LL |         [false, .., true] => {}
 LL |         [false, true] => {}
-   |         ^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^ unreachable pattern
 
 error: aborting due to 6 previous errors
 
diff --git a/tests/ui/pattern/usefulness/top-level-alternation.stderr b/tests/ui/pattern/usefulness/top-level-alternation.stderr
index 17fa951c539..0e7e7d4969d 100644
--- a/tests/ui/pattern/usefulness/top-level-alternation.stderr
+++ b/tests/ui/pattern/usefulness/top-level-alternation.stderr
@@ -2,7 +2,9 @@ error: unreachable pattern
   --> $DIR/top-level-alternation.rs:4:23
    |
 LL |     while let 0..=2 | 1 = 0 {}
-   |                       ^
+   |               -----   ^ unreachable pattern
+   |               |
+   |               matches all the values already
    |
 note: the lint level is defined here
   --> $DIR/top-level-alternation.rs:1:9
@@ -14,61 +16,84 @@ error: unreachable pattern
   --> $DIR/top-level-alternation.rs:5:20
    |
 LL |     if let 0..=2 | 1 = 0 {}
-   |                    ^
+   |            -----   ^ unreachable pattern
+   |            |
+   |            matches all the values already
 
 error: unreachable pattern
   --> $DIR/top-level-alternation.rs:9:15
    |
+LL |         0
+   |         - matches all the values already
 LL |             | 0 => {}
-   |               ^
+   |               ^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/top-level-alternation.rs:14:15
    |
+LL |         Some(0)
+   |         ------- matches all the values already
 LL |             | Some(0) => {}
-   |               ^^^^^^^
+   |               ^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/top-level-alternation.rs:19:9
    |
+LL |         (0, _) | (_, 0) => {}
+   |         --------------- matches all the values already
 LL |         (0, 0) => {}
-   |         ^^^^^^
+   |         ^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/top-level-alternation.rs:39:9
    |
+LL |         None | Some(_) => {}
+   |         -------------- matches all the values already
 LL |         _ => {}
-   |         ^
+   |         ^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/top-level-alternation.rs:43:9
    |
+LL |         None | Some(_) => {}
+   |         -------------- matches all the values already
 LL |         Some(_) => {}
-   |         ^^^^^^^
+   |         ^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/top-level-alternation.rs:44:9
    |
+LL |         None | Some(_) => {}
+   |         -------------- matches all the values already
+LL |         Some(_) => {}
 LL |         None => {}
-   |         ^^^^
+   |         ^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/top-level-alternation.rs:49:9
    |
+LL |         Some(_) => {}
+   |         ------- matches some of the same values
+LL |         None => {}
+   |         ---- matches some of the same values
 LL |         None | Some(_) => {}
-   |         ^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/top-level-alternation.rs:53:9
    |
+LL |         1 | 2 => {},
+   |         ----- matches all the values already
 LL |         1..=2 => {},
-   |         ^^^^^
+   |         ^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/top-level-alternation.rs:56:14
    |
 LL |     let (0 | 0) = 0 else { return };
-   |              ^
+   |          -   ^ unreachable pattern
+   |          |
+   |          matches all the values already
 
 error: aborting due to 11 previous errors
 
diff --git a/tests/ui/reachable/unreachable-arm.stderr b/tests/ui/reachable/unreachable-arm.stderr
index 60db8217640..79627404030 100644
--- a/tests/ui/reachable/unreachable-arm.stderr
+++ b/tests/ui/reachable/unreachable-arm.stderr
@@ -1,8 +1,10 @@
 error: unreachable pattern
   --> $DIR/unreachable-arm.rs:11:9
    |
+LL |         Foo::B(_) | Foo::A(box _, 1) => { }
+   |         ---------------------------- matches all the values already
 LL |         Foo::A(_, 1) => { }
-   |         ^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/unreachable-arm.rs:4:9
diff --git a/tests/ui/reachable/unreachable-loop-patterns.stderr b/tests/ui/reachable/unreachable-loop-patterns.stderr
index 1dea9d813f9..bdd9b5ee411 100644
--- a/tests/ui/reachable/unreachable-loop-patterns.stderr
+++ b/tests/ui/reachable/unreachable-loop-patterns.stderr
@@ -4,6 +4,7 @@ error: unreachable pattern
 LL |     for _ in unimplemented!() as Void {}
    |         ^
    |
+   = note: this pattern matches no values because `Void` is uninhabited
 note: the lint level is defined here
   --> $DIR/unreachable-loop-patterns.rs:5:9
    |
diff --git a/tests/ui/reachable/unreachable-try-pattern.stderr b/tests/ui/reachable/unreachable-try-pattern.stderr
index 8f3e23119fb..bc1a6fffda6 100644
--- a/tests/ui/reachable/unreachable-try-pattern.stderr
+++ b/tests/ui/reachable/unreachable-try-pattern.stderr
@@ -19,6 +19,7 @@ warning: unreachable pattern
 LL |     let y = (match x { Ok(n) => Ok(n as u32), Err(e) => Err(e) })?;
    |                        ^^^^^
    |
+   = note: this pattern matches no values because `!` is uninhabited
 note: the lint level is defined here
   --> $DIR/unreachable-try-pattern.rs:4:9
    |
@@ -30,6 +31,8 @@ warning: unreachable pattern
    |
 LL |     let y = (match x { Ok(n) => Ok(n), Err(e) => Err(e) })?;
    |                                        ^^^^^^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 warning: 3 warnings emitted
 
diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr
index fe2a72d2a31..a875041d89c 100644
--- a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr
+++ b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr
@@ -4,6 +4,7 @@ error: unreachable pattern
 LL |         Err(!),
    |         ^^^^^^
    |
+   = note: this pattern matches no values because `Void` is uninhabited
 note: the lint level is defined here
   --> $DIR/unreachable.rs:7:9
    |
@@ -15,30 +16,40 @@ error: unreachable pattern
    |
 LL |     let (Ok(_x) | Err(!)) = res_void;
    |                   ^^^^^^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/unreachable.rs:22:12
    |
 LL |     if let Err(!) = res_void {}
    |            ^^^^^^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/unreachable.rs:24:24
    |
 LL |     if let (Ok(true) | Err(!)) = res_void {}
    |                        ^^^^^^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/unreachable.rs:26:23
    |
 LL |     for (Ok(mut _x) | Err(!)) in [res_void] {}
    |                       ^^^^^^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: unreachable pattern
   --> $DIR/unreachable.rs:30:18
    |
 LL | fn foo((Ok(_x) | Err(!)): Result<bool, Void>) {}
    |                  ^^^^^^
+   |
+   = note: this pattern matches no values because `Void` is uninhabited
 
 error: aborting due to 6 previous errors
 
diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr
index 7386f10a6fb..d5f58e436c5 100644
--- a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr
+++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr
@@ -4,6 +4,7 @@ error: unreachable pattern
 LL |         _ => {}
    |         ^
    |
+   = note: this pattern matches no values because `EmptyNonExhaustiveEnum` is uninhabited
 note: the lint level is defined here
   --> $DIR/enum_same_crate_empty_match.rs:1:9
    |
diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.stderr
index 3034a67dc43..4ec4ec9705a 100644
--- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.stderr
+++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.stderr
@@ -2,7 +2,9 @@ error: unreachable pattern
   --> $DIR/issue-65157-repeated-match-arm.rs:15:9
    |
 LL |         PartiallyInhabitedVariants::Struct { .. } => {},
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         ----------------------------------------- matches all the values already
+LL |         PartiallyInhabitedVariants::Struct { .. } => {},
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/issue-65157-repeated-match-arm.rs:2:9
diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr
index 8bfd6e91f4d..d5192a70ed5 100644
--- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr
+++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr
@@ -4,6 +4,7 @@ error: unreachable pattern
 LL |         Some(_x) => (),
    |         ^^^^^^^^
    |
+   = note: this pattern matches no values because `UninhabitedEnum` is uninhabited
 note: the lint level is defined here
   --> $DIR/patterns_same_crate.rs:1:9
    |
@@ -15,24 +16,32 @@ error: unreachable pattern
    |
 LL |         Some(_x) => (),
    |         ^^^^^^^^
+   |
+   = note: this pattern matches no values because `UninhabitedVariants` is uninhabited
 
 error: unreachable pattern
   --> $DIR/patterns_same_crate.rs:61:15
    |
 LL |     while let PartiallyInhabitedVariants::Struct { x } = partially_inhabited_variant() {
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this pattern matches no values because `!` is uninhabited
 
 error: unreachable pattern
   --> $DIR/patterns_same_crate.rs:65:15
    |
 LL |     while let Some(_x) = uninhabited_struct() {
    |               ^^^^^^^^
+   |
+   = note: this pattern matches no values because `UninhabitedStruct` is uninhabited
 
 error: unreachable pattern
   --> $DIR/patterns_same_crate.rs:68:15
    |
 LL |     while let Some(_x) = uninhabited_tuple_struct() {
    |               ^^^^^^^^
+   |
+   = note: this pattern matches no values because `UninhabitedTupleStruct` is uninhabited
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/warns.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/warns.stderr
index eed5dbb88de..8d0874fa900 100644
--- a/tests/ui/rfcs/rfc-2294-if-let-guard/warns.stderr
+++ b/tests/ui/rfcs/rfc-2294-if-let-guard/warns.stderr
@@ -16,7 +16,9 @@ error: unreachable pattern
   --> $DIR/warns.rs:15:25
    |
 LL |         x if let None | None = x => {}
-   |                         ^^^^
+   |                  ----   ^^^^ unreachable pattern
+   |                  |
+   |                  matches all the values already
    |
 note: the lint level is defined here
   --> $DIR/warns.rs:12:8
diff --git a/tests/ui/uninhabited/uninhabited-patterns.stderr b/tests/ui/uninhabited/uninhabited-patterns.stderr
index a6fda88f032..ca62386d7ef 100644
--- a/tests/ui/uninhabited/uninhabited-patterns.stderr
+++ b/tests/ui/uninhabited/uninhabited-patterns.stderr
@@ -4,6 +4,7 @@ error: unreachable pattern
 LL |         Ok(box _) => (),
    |         ^^^^^^^^^
    |
+   = note: this pattern matches no values because `NotSoSecretlyEmpty` is uninhabited
 note: the lint level is defined here
   --> $DIR/uninhabited-patterns.rs:4:9
    |
@@ -15,12 +16,16 @@ error: unreachable pattern
    |
 LL |         Err(Ok(_y)) => (),
    |         ^^^^^^^^^^^
+   |
+   = note: this pattern matches no values because `NotSoSecretlyEmpty` is uninhabited
 
 error: unreachable pattern
   --> $DIR/uninhabited-patterns.rs:42:15
    |
 LL |     while let Some(_y) = foo() {
    |               ^^^^^^^^
+   |
+   = note: this pattern matches no values because `NotSoSecretlyEmpty` is uninhabited
 
 error: aborting due to 3 previous errors