about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs10
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs1
-rw-r--r--compiler/rustc_data_structures/src/sync/parallel.rs13
-rw-r--r--compiler/rustc_mir_build/messages.ftl5
-rw-r--r--compiler/rustc_mir_build/src/errors.rs12
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs839
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/usefulness.rs69
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs2
-rw-r--r--src/tools/tidy/src/deps.rs1
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr4
-rw-r--r--tests/ui/error-codes/E0004.stderr6
-rw-r--r--tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.stderr6
-rw-r--r--tests/ui/match/match_non_exhaustive.stderr4
-rw-r--r--tests/ui/pattern/issue-94866.stderr4
-rw-r--r--tests/ui/pattern/usefulness/conflicting_bindings.rs24
-rw-r--r--tests/ui/pattern/usefulness/conflicting_bindings.stderr66
-rw-r--r--tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr18
-rw-r--r--tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr94
-rw-r--r--tests/ui/pattern/usefulness/empty-match.normal.stderr94
-rw-r--r--tests/ui/pattern/usefulness/empty-match.rs18
-rw-r--r--tests/ui/pattern/usefulness/issue-35609.stderr3
-rw-r--r--tests/ui/pattern/usefulness/issue-39362.stderr6
-rw-r--r--tests/ui/pattern/usefulness/issue-40221.stderr6
-rw-r--r--tests/ui/pattern/usefulness/issue-56379.stderr10
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs18
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr54
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr4
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-match.stderr8
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr33
-rw-r--r--tests/ui/pattern/usefulness/stable-gated-patterns.stderr6
-rw-r--r--tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr6
-rw-r--r--tests/ui/pattern/usefulness/unstable-gated-patterns.stderr6
-rw-r--r--tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr20
-rw-r--r--tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns-dont-lint-on-arm.lint.stderr75
-rw-r--r--tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns-dont-lint-on-arm.normal.stderr31
-rw-r--r--tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns-dont-lint-on-arm.rs53
-rw-r--r--tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.stderr8
-rw-r--r--tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr8
-rw-r--r--tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr8
40 files changed, 986 insertions, 667 deletions
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index 73821b1685d..307c1264dc1 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -374,15 +374,7 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
 
             let g = self.get_static(def_id);
 
-            // boolean SSA values are i1, but they have to be stored in i8 slots,
-            // otherwise some LLVM optimization passes don't work as expected
-            let mut val_llty = self.val_ty(v);
-            let v = if val_llty == self.type_i1() {
-                val_llty = self.type_i8();
-                llvm::LLVMConstZExt(v, val_llty)
-            } else {
-                v
-            };
+            let val_llty = self.val_ty(v);
 
             let instance = Instance::mono(self.tcx, def_id);
             let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index a038b3af03d..7fc02a95be0 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -969,7 +969,6 @@ extern "C" {
         ConstantIndices: *const &'a Value,
         NumIndices: c_uint,
     ) -> &'a Value;
-    pub fn LLVMConstZExt<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value;
     pub fn LLVMConstPtrToInt<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value;
     pub fn LLVMConstIntToPtr<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value;
     pub fn LLVMConstBitCast<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value;
diff --git a/compiler/rustc_data_structures/src/sync/parallel.rs b/compiler/rustc_data_structures/src/sync/parallel.rs
index 39dddb59569..5695d839d3e 100644
--- a/compiler/rustc_data_structures/src/sync/parallel.rs
+++ b/compiler/rustc_data_structures/src/sync/parallel.rs
@@ -77,12 +77,12 @@ mod disabled {
         })
     }
 
-    pub fn try_par_for_each_in<T: IntoIterator, E: Copy>(
+    pub fn try_par_for_each_in<T: IntoIterator, E>(
         t: T,
         mut for_each: impl FnMut(T::Item) -> Result<(), E>,
     ) -> Result<(), E> {
         parallel_guard(|guard| {
-            t.into_iter().fold(Ok(()), |ret, i| guard.run(|| for_each(i)).unwrap_or(ret).and(ret))
+            t.into_iter().filter_map(|i| guard.run(|| for_each(i))).fold(Ok(()), Result::and)
         })
     }
 
@@ -178,7 +178,7 @@ mod enabled {
 
     pub fn try_par_for_each_in<
         T: IntoIterator + IntoParallelIterator<Item = <T as IntoIterator>::Item>,
-        E: Copy + Send,
+        E: Send,
     >(
         t: T,
         for_each: impl Fn(<T as IntoIterator>::Item) -> Result<(), E> + DynSync + DynSend,
@@ -187,11 +187,10 @@ mod enabled {
             if mode::is_dyn_thread_safe() {
                 let for_each = FromDyn::from(for_each);
                 t.into_par_iter()
-                    .fold_with(Ok(()), |ret, i| guard.run(|| for_each(i)).unwrap_or(ret).and(ret))
-                    .reduce(|| Ok(()), |a, b| a.and(b))
+                    .filter_map(|i| guard.run(|| for_each(i)))
+                    .reduce(|| Ok(()), Result::and)
             } else {
-                t.into_iter()
-                    .fold(Ok(()), |ret, i| guard.run(|| for_each(i)).unwrap_or(ret).and(ret))
+                t.into_iter().filter_map(|i| guard.run(|| for_each(i))).fold(Ok(()), Result::and)
             }
         })
     }
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index 32711c23dc4..563851f712c 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -221,6 +221,11 @@ mir_build_non_exhaustive_omitted_pattern = some variants are not matched explici
     .help = ensure that all variants are matched explicitly by adding the suggested match arms
     .note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found
 
+mir_build_non_exhaustive_omitted_pattern_lint_on_arm = the lint level must be set on the whole match
+    .help = it no longer has any effect to set the lint level on an individual match arm
+    .label = remove this attribute
+    .suggestion = set the lint level on the whole match
+
 mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type `{$ty}` is non-empty
     .def_note = `{$peeled_ty}` defined here
     .type_note = the matched value is of type `{$ty}`
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 5bfce3ab510..c4eed5532ee 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -789,6 +789,18 @@ pub(crate) struct NonExhaustiveOmittedPattern<'tcx> {
     pub uncovered: Uncovered<'tcx>,
 }
 
+#[derive(LintDiagnostic)]
+#[diag(mir_build_non_exhaustive_omitted_pattern_lint_on_arm)]
+#[help]
+pub(crate) struct NonExhaustiveOmittedPatternLintOnArm {
+    #[label]
+    pub lint_span: Span,
+    #[suggestion(code = "#[{lint_level}({lint_name})]\n", applicability = "maybe-incorrect")]
+    pub suggest_lint_on_match: Option<Span>,
+    pub lint_level: &'static str,
+    pub lint_name: &'static str,
+}
+
 #[derive(Subdiagnostic)]
 #[label(mir_build_uncovered)]
 pub(crate) struct Uncovered<'tcx> {
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 933653e708e..8c3d09c19a1 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -9,9 +9,7 @@ use rustc_arena::TypedArena;
 use rustc_ast::Mutability;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::{
-    struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
-};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::*;
 use rustc_hir::def_id::LocalDefId;
@@ -44,7 +42,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
 
     for param in thir.params.iter() {
         if let Some(box ref pattern) = param.pat {
-            visitor.check_irrefutable(pattern, "function argument", None);
+            visitor.check_binding_is_irrefutable(pattern, "function argument", None);
         }
     }
     visitor.error
@@ -58,7 +56,7 @@ fn create_e0004(
     struct_span_err!(sess, sp, E0004, "{}", &error_message)
 }
 
-#[derive(PartialEq)]
+#[derive(Debug, Copy, Clone, PartialEq)]
 enum RefutableFlag {
     Irrefutable,
     Refutable,
@@ -68,24 +66,30 @@ use RefutableFlag::*;
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 enum LetSource {
     None,
+    PlainLet,
     IfLet,
     IfLetGuard,
     LetElse,
     WhileLet,
 }
 
-struct MatchVisitor<'a, 'p, 'tcx> {
+struct MatchVisitor<'thir, 'p, 'tcx> {
     tcx: TyCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
-    thir: &'a Thir<'tcx>,
+    thir: &'thir Thir<'tcx>,
     lint_level: HirId,
     let_source: LetSource,
     pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
+    /// Tracks if we encountered an error while checking this body. That the first function to
+    /// report it stores it here. Some functions return `Result` to allow callers to short-circuit
+    /// on error, but callers don't need to store it here again.
     error: Result<(), ErrorGuaranteed>,
 }
 
-impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
-    fn thir(&self) -> &'a Thir<'tcx> {
+// Visitor for a thir body. This calls `check_match`, `check_let` and `check_let_chain` as
+// appropriate.
+impl<'thir, 'tcx> Visitor<'thir, 'tcx> for MatchVisitor<'thir, '_, 'tcx> {
+    fn thir(&self) -> &'thir Thir<'tcx> {
         self.thir
     }
 
@@ -100,7 +104,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
                 }
                 Some(Guard::IfLet(ref pat, expr)) => {
                     this.with_let_source(LetSource::IfLetGuard, |this| {
-                        this.check_let(pat, expr, LetSource::IfLetGuard, pat.span);
+                        this.check_let(pat, Some(expr), pat.span);
                         this.visit_pat(pat);
                         this.visit_expr(&this.thir[expr]);
                     });
@@ -148,10 +152,18 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
                 self.check_match(scrutinee, arms, source, ex.span);
             }
             ExprKind::Let { box ref pat, expr } => {
-                self.check_let(pat, expr, self.let_source, ex.span);
+                self.check_let(pat, Some(expr), ex.span);
             }
-            ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
-                self.check_let_chain(self.let_source, ex.span, lhs, rhs);
+            ExprKind::LogicalOp { op: LogicalOp::And, .. }
+                if !matches!(self.let_source, LetSource::None) =>
+            {
+                let mut chain_refutabilities = Vec::new();
+                let Ok(()) = self.visit_land(ex, &mut chain_refutabilities) else { return };
+                // If at least one of the operands is a `let ... = ...`.
+                if chain_refutabilities.iter().any(|x| x.is_some()) {
+                    self.check_let_chain(chain_refutabilities, ex.span);
+                }
+                return;
             }
             _ => {}
         };
@@ -159,33 +171,27 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
     }
 
     fn visit_stmt(&mut self, stmt: &Stmt<'tcx>) {
-        let old_lint_level = self.lint_level;
         match stmt.kind {
             StmtKind::Let {
                 box ref pattern, initializer, else_block, lint_level, span, ..
             } => {
-                if let LintLevel::Explicit(lint_level) = lint_level {
-                    self.lint_level = lint_level;
-                }
-
-                if let Some(initializer) = initializer
-                    && else_block.is_some()
-                {
-                    self.check_let(pattern, initializer, LetSource::LetElse, span);
-                }
-
-                if else_block.is_none() {
-                    self.check_irrefutable(pattern, "local binding", Some(span));
-                }
+                self.with_lint_level(lint_level, |this| {
+                    let let_source =
+                        if else_block.is_some() { LetSource::LetElse } else { LetSource::PlainLet };
+                    this.with_let_source(let_source, |this| {
+                        this.check_let(pattern, initializer, span)
+                    });
+                    visit::walk_stmt(this, stmt);
+                });
+            }
+            StmtKind::Expr { .. } => {
+                visit::walk_stmt(self, stmt);
             }
-            _ => {}
         }
-        visit::walk_stmt(self, stmt);
-        self.lint_level = old_lint_level;
     }
 }
 
-impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
+impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
     #[instrument(level = "trace", skip(self, f))]
     fn with_let_source(&mut self, let_source: LetSource, f: impl FnOnce(&mut Self)) {
         let old_let_source = self.let_source;
@@ -194,53 +200,127 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
         self.let_source = old_let_source;
     }
 
-    fn with_lint_level(&mut self, new_lint_level: LintLevel, f: impl FnOnce(&mut Self)) {
+    fn with_lint_level<T>(
+        &mut self,
+        new_lint_level: LintLevel,
+        f: impl FnOnce(&mut Self) -> T,
+    ) -> T {
         if let LintLevel::Explicit(hir_id) = new_lint_level {
             let old_lint_level = self.lint_level;
             self.lint_level = hir_id;
-            f(self);
+            let ret = f(self);
             self.lint_level = old_lint_level;
+            ret
         } else {
-            f(self);
+            f(self)
         }
     }
 
-    fn check_patterns(&self, pat: &Pat<'tcx>, rf: RefutableFlag) {
-        pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
-        check_for_bindings_named_same_as_variants(self, pat, rf);
+    /// Visit a nested chain of `&&`. Used for if-let chains. This must call `visit_expr` on the
+    /// subexpressions we are not handling ourselves.
+    fn visit_land(
+        &mut self,
+        ex: &Expr<'tcx>,
+        accumulator: &mut Vec<Option<(Span, RefutableFlag)>>,
+    ) -> Result<(), ErrorGuaranteed> {
+        match ex.kind {
+            ExprKind::Scope { value, lint_level, .. } => self.with_lint_level(lint_level, |this| {
+                this.visit_land(&this.thir[value], accumulator)
+            }),
+            ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
+                // We recurse into the lhs only, because `&&` chains associate to the left.
+                let res_lhs = self.visit_land(&self.thir[lhs], accumulator);
+                let res_rhs = self.visit_land_rhs(&self.thir[rhs])?;
+                accumulator.push(res_rhs);
+                res_lhs
+            }
+            _ => {
+                let res = self.visit_land_rhs(ex)?;
+                accumulator.push(res);
+                Ok(())
+            }
+        }
+    }
+
+    /// Visit the right-hand-side of a `&&`. Used for if-let chains. Returns `Some` if the
+    /// expression was ultimately a `let ... = ...`, and `None` if it was a normal boolean
+    /// expression. This must call `visit_expr` on the subexpressions we are not handling ourselves.
+    fn visit_land_rhs(
+        &mut self,
+        ex: &Expr<'tcx>,
+    ) -> Result<Option<(Span, RefutableFlag)>, ErrorGuaranteed> {
+        match ex.kind {
+            ExprKind::Scope { value, lint_level, .. } => {
+                self.with_lint_level(lint_level, |this| this.visit_land_rhs(&this.thir[value]))
+            }
+            ExprKind::Let { box ref pat, expr } => {
+                self.with_let_source(LetSource::None, |this| {
+                    this.visit_expr(&this.thir()[expr]);
+                });
+                Ok(Some((ex.span, self.is_let_irrefutable(pat)?)))
+            }
+            _ => {
+                self.with_let_source(LetSource::None, |this| {
+                    this.visit_expr(ex);
+                });
+                Ok(None)
+            }
+        }
     }
 
     fn lower_pattern(
-        &self,
-        cx: &mut MatchCheckCtxt<'p, 'tcx>,
-        pattern: &Pat<'tcx>,
-    ) -> &'p DeconstructedPat<'p, 'tcx> {
-        cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern))
+        &mut self,
+        cx: &MatchCheckCtxt<'p, 'tcx>,
+        pat: &Pat<'tcx>,
+    ) -> Result<&'p DeconstructedPat<'p, 'tcx>, ErrorGuaranteed> {
+        if let Err(err) = pat.pat_error_reported() {
+            self.error = Err(err);
+            Err(err)
+        } else {
+            // Check the pattern for some things unrelated to exhaustiveness.
+            let refutable = if cx.refutable { Refutable } else { Irrefutable };
+            pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
+            pat.walk_always(|pat| check_for_bindings_named_same_as_variants(self, pat, refutable));
+            Ok(cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, pat)))
+        }
     }
 
-    fn new_cx(&self, hir_id: HirId, refutable: bool) -> MatchCheckCtxt<'p, 'tcx> {
+    fn new_cx(
+        &self,
+        refutability: RefutableFlag,
+        match_span: Option<Span>,
+    ) -> MatchCheckCtxt<'p, 'tcx> {
+        let refutable = match refutability {
+            Irrefutable => false,
+            Refutable => true,
+        };
         MatchCheckCtxt {
             tcx: self.tcx,
             param_env: self.param_env,
-            module: self.tcx.parent_module(hir_id).to_def_id(),
+            module: self.tcx.parent_module(self.lint_level).to_def_id(),
             pattern_arena: &self.pattern_arena,
+            match_span,
             refutable,
         }
     }
 
     #[instrument(level = "trace", skip(self))]
-    fn check_let(&mut self, pat: &Pat<'tcx>, scrutinee: ExprId, source: LetSource, span: Span) {
-        if let LetSource::None = source {
-            return;
-        }
-        if let Err(err) = pat.pat_error_reported() {
-            self.error = Err(err);
-            return;
+    fn check_let(&mut self, pat: &Pat<'tcx>, scrutinee: Option<ExprId>, span: Span) {
+        assert!(self.let_source != LetSource::None);
+        if let LetSource::PlainLet = self.let_source {
+            self.check_binding_is_irrefutable(pat, "local binding", Some(span))
+        } else {
+            let Ok(refutability) = self.is_let_irrefutable(pat) else { return };
+            if matches!(refutability, Irrefutable) {
+                report_irrefutable_let_patterns(
+                    self.tcx,
+                    self.lint_level,
+                    self.let_source,
+                    1,
+                    span,
+                );
+            }
         }
-        self.check_patterns(pat, Refutable);
-        let mut cx = self.new_cx(self.lint_level, true);
-        let tpat = self.lower_pattern(&mut cx, pat);
-        self.check_let_reachability(&mut cx, self.lint_level, source, tpat, span);
     }
 
     fn check_match(
@@ -250,33 +330,22 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
         source: hir::MatchSource,
         expr_span: Span,
     ) {
-        let mut cx = self.new_cx(self.lint_level, true);
+        let cx = self.new_cx(Refutable, Some(expr_span));
 
+        let mut tarms = Vec::with_capacity(arms.len());
         for &arm in arms {
-            // Check the arm for some things unrelated to exhaustiveness.
             let arm = &self.thir.arms[arm];
-            self.with_lint_level(arm.lint_level, |this| {
-                this.check_patterns(&arm.pattern, Refutable);
+            let got_error = self.with_lint_level(arm.lint_level, |this| {
+                let Ok(pat) = this.lower_pattern(&cx, &arm.pattern) else { return true };
+                let arm = MatchArm { pat, hir_id: this.lint_level, has_guard: arm.guard.is_some() };
+                tarms.push(arm);
+                false
             });
-            if let Err(err) = arm.pattern.pat_error_reported() {
-                self.error = Err(err);
+            if got_error {
                 return;
             }
         }
 
-        let tarms: Vec<_> = arms
-            .iter()
-            .map(|&arm| {
-                let arm = &self.thir.arms[arm];
-                let hir_id = match arm.lint_level {
-                    LintLevel::Explicit(hir_id) => hir_id,
-                    LintLevel::Inherited => self.lint_level,
-                };
-                let pat = self.lower_pattern(&mut cx, &arm.pattern);
-                MatchArm { pat, hir_id, has_guard: arm.guard.is_some() }
-            })
-            .collect();
-
         let scrut = &self.thir[scrut];
         let scrut_ty = scrut.ty;
         let report = compute_match_usefulness(&cx, &tarms, self.lint_level, scrut_ty, scrut.span);
@@ -303,118 +372,37 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
                 debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop));
                 let PatKind::Variant { ref subpatterns, .. } = pat.kind else { bug!() };
                 let [pat_field] = &subpatterns[..] else { bug!() };
-                self.check_irrefutable(&pat_field.pattern, "`for` loop binding", None);
+                self.check_binding_is_irrefutable(&pat_field.pattern, "`for` loop binding", None);
             } else {
-                self.error = Err(non_exhaustive_match(
+                self.error = Err(report_non_exhaustive_match(
                     &cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span,
                 ));
             }
         }
     }
 
-    fn check_let_reachability(
-        &mut self,
-        cx: &mut MatchCheckCtxt<'p, 'tcx>,
-        pat_id: HirId,
-        source: LetSource,
-        pat: &'p DeconstructedPat<'p, 'tcx>,
-        span: Span,
-    ) {
-        if is_let_irrefutable(cx, pat_id, pat) {
-            irrefutable_let_patterns(cx.tcx, pat_id, source, 1, span);
-        }
-    }
-
     #[instrument(level = "trace", skip(self))]
     fn check_let_chain(
         &mut self,
-        let_source: LetSource,
-        top_expr_span: Span,
-        mut lhs: ExprId,
-        rhs: ExprId,
+        chain_refutabilities: Vec<Option<(Span, RefutableFlag)>>,
+        whole_chain_span: Span,
     ) {
-        if let LetSource::None = let_source {
-            return;
-        }
-
-        // Lint level enclosing the next `lhs`.
-        let mut cur_lint_level = self.lint_level;
-
-        // Obtain the refutabilities of all exprs in the chain,
-        // and record chain members that aren't let exprs.
-        let mut chain_refutabilities = Vec::new();
-
-        let mut error = Ok(());
-        let mut add = |expr: ExprId, mut local_lint_level| {
-            // `local_lint_level` is the lint level enclosing the pattern inside `expr`.
-            let mut expr = &self.thir[expr];
-            debug!(?expr, ?local_lint_level, "add");
-            // Fast-forward through scopes.
-            while let ExprKind::Scope { value, lint_level, .. } = expr.kind {
-                if let LintLevel::Explicit(hir_id) = lint_level {
-                    local_lint_level = hir_id
-                }
-                expr = &self.thir[value];
-            }
-            debug!(?expr, ?local_lint_level, "after scopes");
-            match expr.kind {
-                ExprKind::Let { box ref pat, expr: _ } => {
-                    if let Err(err) = pat.pat_error_reported() {
-                        error = Err(err);
-                        return None;
-                    }
-                    let mut ncx = self.new_cx(local_lint_level, true);
-                    let tpat = self.lower_pattern(&mut ncx, pat);
-                    let refutable = !is_let_irrefutable(&mut ncx, local_lint_level, tpat);
-                    Some((expr.span, refutable))
-                }
-                _ => None,
-            }
-        };
-
-        // Let chains recurse on the left, so we start by adding the rightmost.
-        chain_refutabilities.push(add(rhs, cur_lint_level));
+        assert!(self.let_source != LetSource::None);
 
-        loop {
-            while let ExprKind::Scope { value, lint_level, .. } = self.thir[lhs].kind {
-                if let LintLevel::Explicit(hir_id) = lint_level {
-                    cur_lint_level = hir_id
-                }
-                lhs = value;
-            }
-            if let ExprKind::LogicalOp { op: LogicalOp::And, lhs: new_lhs, rhs: expr } =
-                self.thir[lhs].kind
-            {
-                chain_refutabilities.push(add(expr, cur_lint_level));
-                lhs = new_lhs;
-            } else {
-                chain_refutabilities.push(add(lhs, cur_lint_level));
-                break;
-            }
-        }
-        debug!(?chain_refutabilities);
-        chain_refutabilities.reverse();
-
-        if error.is_err() {
-            self.error = error;
-            return;
-        }
-
-        // Third, emit the actual warnings.
-        if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, false)))) {
+        if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, Irrefutable)))) {
             // The entire chain is made up of irrefutable `let` statements
-            irrefutable_let_patterns(
+            report_irrefutable_let_patterns(
                 self.tcx,
                 self.lint_level,
-                let_source,
+                self.let_source,
                 chain_refutabilities.len(),
-                top_expr_span,
+                whole_chain_span,
             );
             return;
         }
 
         if let Some(until) =
-            chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, false))))
+            chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, Irrefutable))))
             && until > 0
         {
             // The chain has a non-zero prefix of irrefutable `let` statements.
@@ -425,7 +413,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
             // so can't always be moved out.
             // FIXME: Add checking whether the bindings are actually used in the prefix,
             // and lint if they are not.
-            if !matches!(let_source, LetSource::WhileLet | LetSource::IfLetGuard) {
+            if !matches!(self.let_source, LetSource::WhileLet | LetSource::IfLetGuard) {
                 // Emit the lint
                 let prefix = &chain_refutabilities[..until];
                 let span_start = prefix[0].unwrap().0;
@@ -442,7 +430,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
         }
 
         if let Some(from) =
-            chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, false))))
+            chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, Irrefutable))))
             && from != (chain_refutabilities.len() - 1)
         {
             // The chain has a non-empty suffix of irrefutable `let` statements
@@ -460,28 +448,36 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
         }
     }
 
-    #[instrument(level = "trace", skip(self))]
-    fn check_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
-        // If we got errors while lowering, don't emit anything more.
-        if let Err(err) = pat.pat_error_reported() {
-            self.error = Err(err);
-            return;
-        }
+    fn analyze_binding(
+        &mut self,
+        pat: &Pat<'tcx>,
+        refutability: RefutableFlag,
+    ) -> Result<(MatchCheckCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> {
+        let cx = self.new_cx(refutability, None);
+        let pat = self.lower_pattern(&cx, pat)?;
+        let arms = [MatchArm { pat, hir_id: self.lint_level, has_guard: false }];
+        let report = compute_match_usefulness(&cx, &arms, self.lint_level, pat.ty(), pat.span());
+        Ok((cx, report))
+    }
 
-        let mut cx = self.new_cx(self.lint_level, false);
+    fn is_let_irrefutable(&mut self, pat: &Pat<'tcx>) -> Result<RefutableFlag, ErrorGuaranteed> {
+        let (cx, report) = self.analyze_binding(pat, Refutable)?;
+        // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
+        // This also reports unreachable sub-patterns.
+        report_arm_reachability(&cx, &report);
+        // If the list of witnesses is empty, the match is exhaustive, i.e. the `if let` pattern is
+        // irrefutable.
+        Ok(if report.non_exhaustiveness_witnesses.is_empty() { Irrefutable } else { Refutable })
+    }
 
-        let pattern = self.lower_pattern(&mut cx, pat);
-        let pattern_ty = pattern.ty();
-        let arm = MatchArm { pat: pattern, hir_id: self.lint_level, has_guard: false };
-        let report =
-            compute_match_usefulness(&cx, &[arm], self.lint_level, pattern_ty, pattern.span());
+    #[instrument(level = "trace", skip(self))]
+    fn check_binding_is_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
+        let pattern_ty = pat.ty;
 
-        // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
-        // only care about exhaustiveness here.
+        let Ok((cx, report)) = self.analyze_binding(pat, Irrefutable) else { return };
         let witnesses = report.non_exhaustiveness_witnesses;
         if witnesses.is_empty() {
             // The pattern is irrefutable.
-            self.check_patterns(pat, Irrefutable);
             return;
         }
 
@@ -528,30 +524,20 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
             });
         };
 
-        let adt_defined_here = try {
-            let ty = pattern_ty.peel_refs();
-            let ty::Adt(def, _) = ty.kind() else { None? };
-            let adt_def_span = cx.tcx.hir().get_if_local(def.did())?.ident()?.span;
-            let mut variants = vec![];
-
-            for span in maybe_point_at_variant(&cx, *def, witnesses.iter().take(5)) {
-                variants.push(Variant { span });
-            }
-            AdtDefinedHere { adt_def_span, ty, variants }
-        };
+        let adt_defined_here = report_adt_defined_here(self.tcx, pattern_ty, &witnesses, false);
 
         // Emit an extra note if the first uncovered witness would be uninhabited
         // if we disregard visibility.
-        let witness_1_is_privately_uninhabited = if cx.tcx.features().exhaustive_patterns
+        let witness_1_is_privately_uninhabited = if self.tcx.features().exhaustive_patterns
             && let Some(witness_1) = witnesses.get(0)
             && let ty::Adt(adt, args) = witness_1.ty().kind()
             && adt.is_enum()
             && let Constructor::Variant(variant_index) = witness_1.ctor()
         {
             let variant = adt.variant(*variant_index);
-            let inhabited = variant.inhabited_predicate(cx.tcx, *adt).instantiate(cx.tcx, args);
-            assert!(inhabited.apply(cx.tcx, cx.param_env, cx.module));
-            !inhabited.apply_ignore_module(cx.tcx, cx.param_env)
+            let inhabited = variant.inhabited_predicate(self.tcx, *adt).instantiate(self.tcx, args);
+            assert!(inhabited.apply(self.tcx, cx.param_env, cx.module));
+            !inhabited.apply_ignore_module(self.tcx, cx.param_env)
         } else {
             false
         };
@@ -572,70 +558,154 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
     }
 }
 
-fn check_for_bindings_named_same_as_variants(
-    cx: &MatchVisitor<'_, '_, '_>,
-    pat: &Pat<'_>,
-    rf: RefutableFlag,
-) {
-    pat.walk_always(|p| {
-        if let PatKind::Binding {
-            name,
-            mode: BindingMode::ByValue,
-            mutability: Mutability::Not,
-            subpattern: None,
-            ty,
-            ..
-        } = p.kind
-            && let ty::Adt(edef, _) = ty.peel_refs().kind()
-            && edef.is_enum()
-            && edef
-                .variants()
-                .iter()
-                .any(|variant| variant.name == name && variant.ctor_kind() == Some(CtorKind::Const))
-        {
-            let variant_count = edef.variants().len();
-            let ty_path = with_no_trimmed_paths!(cx.tcx.def_path_str(edef.did()));
-            cx.tcx.emit_spanned_lint(
-                BINDINGS_WITH_VARIANT_NAME,
-                cx.lint_level,
-                p.span,
-                BindingsWithVariantName {
-                    // If this is an irrefutable pattern, and there's > 1 variant,
-                    // then we can't actually match on this. Applying the below
-                    // suggestion would produce code that breaks on `check_irrefutable`.
-                    suggestion: if rf == Refutable || variant_count == 1 {
-                        Some(p.span)
-                    } else {
-                        None
-                    },
-                    ty_path,
+/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
+/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
+///
+/// For example, this would reject:
+/// - `ref x @ Some(ref mut y)`,
+/// - `ref mut x @ Some(ref y)`,
+/// - `ref mut x @ Some(ref mut y)`,
+/// - `ref mut? x @ Some(y)`, and
+/// - `x @ Some(ref mut? y)`.
+///
+/// This analysis is *not* subsumed by NLL.
+fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, '_, 'tcx>, pat: &Pat<'tcx>) {
+    // Extract `sub` in `binding @ sub`.
+    let PatKind::Binding { name, mode, ty, subpattern: Some(box ref sub), .. } = pat.kind else {
+        return;
+    };
+
+    let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.param_env);
+
+    let sess = cx.tcx.sess;
+
+    // Get the binding move, extract the mutability if by-ref.
+    let mut_outer = match mode {
+        BindingMode::ByValue if is_binding_by_move(ty) => {
+            // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
+            let mut conflicts_ref = Vec::new();
+            sub.each_binding(|_, mode, _, span| match mode {
+                BindingMode::ByValue => {}
+                BindingMode::ByRef(_) => conflicts_ref.push(span),
+            });
+            if !conflicts_ref.is_empty() {
+                sess.emit_err(BorrowOfMovedValue {
+                    binding_span: pat.span,
+                    conflicts_ref,
                     name,
-                },
-            )
+                    ty,
+                    suggest_borrowing: Some(pat.span.shrink_to_lo()),
+                });
+            }
+            return;
+        }
+        BindingMode::ByValue => return,
+        BindingMode::ByRef(m) => m.mutability(),
+    };
+
+    // We now have `ref $mut_outer binding @ sub` (semantically).
+    // Recurse into each binding in `sub` and find mutability or move conflicts.
+    let mut conflicts_move = Vec::new();
+    let mut conflicts_mut_mut = Vec::new();
+    let mut conflicts_mut_ref = Vec::new();
+    sub.each_binding(|name, mode, ty, span| {
+        match mode {
+            BindingMode::ByRef(mut_inner) => match (mut_outer, mut_inner.mutability()) {
+                // Both sides are `ref`.
+                (Mutability::Not, Mutability::Not) => {}
+                // 2x `ref mut`.
+                (Mutability::Mut, Mutability::Mut) => {
+                    conflicts_mut_mut.push(Conflict::Mut { span, name })
+                }
+                (Mutability::Not, Mutability::Mut) => {
+                    conflicts_mut_ref.push(Conflict::Mut { span, name })
+                }
+                (Mutability::Mut, Mutability::Not) => {
+                    conflicts_mut_ref.push(Conflict::Ref { span, name })
+                }
+            },
+            BindingMode::ByValue if is_binding_by_move(ty) => {
+                conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict.
+            }
+            BindingMode::ByValue => {} // `ref mut?` + by-copy is fine.
         }
     });
-}
 
-/// Checks for common cases of "catchall" patterns that may not be intended as such.
-fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
-    use Constructor::*;
-    match pat.ctor() {
-        Wildcard => true,
-        Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
-        _ => false,
+    let report_mut_mut = !conflicts_mut_mut.is_empty();
+    let report_mut_ref = !conflicts_mut_ref.is_empty();
+    let report_move_conflict = !conflicts_move.is_empty();
+
+    let mut occurrences = match mut_outer {
+        Mutability::Mut => vec![Conflict::Mut { span: pat.span, name }],
+        Mutability::Not => vec![Conflict::Ref { span: pat.span, name }],
+    };
+    occurrences.extend(conflicts_mut_mut);
+    occurrences.extend(conflicts_mut_ref);
+    occurrences.extend(conflicts_move);
+
+    // Report errors if any.
+    if report_mut_mut {
+        // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
+        sess.emit_err(MultipleMutBorrows { span: pat.span, occurrences });
+    } else if report_mut_ref {
+        // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
+        match mut_outer {
+            Mutability::Mut => {
+                sess.emit_err(AlreadyMutBorrowed { span: pat.span, occurrences });
+            }
+            Mutability::Not => {
+                sess.emit_err(AlreadyBorrowed { span: pat.span, occurrences });
+            }
+        };
+    } else if report_move_conflict {
+        // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
+        sess.emit_err(MovedWhileBorrowed { span: pat.span, occurrences });
     }
 }
 
-fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
-    tcx.emit_spanned_lint(
-        UNREACHABLE_PATTERNS,
-        id,
-        span,
-        UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall },
-    );
+fn check_for_bindings_named_same_as_variants(
+    cx: &MatchVisitor<'_, '_, '_>,
+    pat: &Pat<'_>,
+    rf: RefutableFlag,
+) {
+    if let PatKind::Binding {
+        name,
+        mode: BindingMode::ByValue,
+        mutability: Mutability::Not,
+        subpattern: None,
+        ty,
+        ..
+    } = pat.kind
+        && let ty::Adt(edef, _) = ty.peel_refs().kind()
+        && edef.is_enum()
+        && edef
+            .variants()
+            .iter()
+            .any(|variant| variant.name == name && variant.ctor_kind() == Some(CtorKind::Const))
+    {
+        let variant_count = edef.variants().len();
+        let ty_path = with_no_trimmed_paths!(cx.tcx.def_path_str(edef.did()));
+        cx.tcx.emit_spanned_lint(
+            BINDINGS_WITH_VARIANT_NAME,
+            cx.lint_level,
+            pat.span,
+            BindingsWithVariantName {
+                // If this is an irrefutable pattern, and there's > 1 variant,
+                // then we can't actually match on this. Applying the below
+                // suggestion would produce code that breaks on `check_binding_is_irrefutable`.
+                suggestion: if rf == Refutable || variant_count == 1 {
+                    Some(pat.span)
+                } else {
+                    None
+                },
+                ty_path,
+                name,
+            },
+        )
+    }
 }
 
-fn irrefutable_let_patterns(
+fn report_irrefutable_let_patterns(
     tcx: TyCtxt<'_>,
     id: HirId,
     source: LetSource,
@@ -649,7 +719,7 @@ fn irrefutable_let_patterns(
     }
 
     match source {
-        LetSource::None => bug!(),
+        LetSource::None | LetSource::PlainLet => bug!(),
         LetSource::IfLet => emit_diag!(IrrefutableLetPatternsIfLet),
         LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard),
         LetSource::LetElse => emit_diag!(IrrefutableLetPatternsLetElse),
@@ -657,34 +727,28 @@ fn irrefutable_let_patterns(
     }
 }
 
-fn is_let_irrefutable<'p, 'tcx>(
-    cx: &mut MatchCheckCtxt<'p, 'tcx>,
-    pat_id: HirId,
-    pat: &'p DeconstructedPat<'p, 'tcx>,
-) -> bool {
-    let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
-    let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty(), pat.span());
-
-    // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
-    // This also reports unreachable sub-patterns though, so we can't just replace it with an
-    // `is_uninhabited` check.
-    report_arm_reachability(&cx, &report);
-
-    // If the list of witnesses is empty, the match is exhaustive,
-    // i.e. the `if let` pattern is irrefutable.
-    report.non_exhaustiveness_witnesses.is_empty()
-}
-
 /// Report unreachable arms, if any.
 fn report_arm_reachability<'p, 'tcx>(
     cx: &MatchCheckCtxt<'p, 'tcx>,
     report: &UsefulnessReport<'p, 'tcx>,
 ) {
+    let report_unreachable_pattern = |span, hir_id, catchall: Option<Span>| {
+        cx.tcx.emit_spanned_lint(
+            UNREACHABLE_PATTERNS,
+            hir_id,
+            span,
+            UnreachablePattern {
+                span: if catchall.is_some() { Some(span) } else { None },
+                catchall,
+            },
+        );
+    };
+
     use Reachability::*;
     let mut catchall = None;
     for (arm, is_useful) in report.arm_usefulness.iter() {
         match is_useful {
-            Unreachable => unreachable_pattern(cx.tcx, arm.pat.span(), arm.hir_id, catchall),
+            Unreachable => report_unreachable_pattern(arm.pat.span(), arm.hir_id, catchall),
             Reachable(unreachables) if unreachables.is_empty() => {}
             // The arm is reachable, but contains unreachable subpatterns (from or-patterns).
             Reachable(unreachables) => {
@@ -692,7 +756,7 @@ fn report_arm_reachability<'p, 'tcx>(
                 // Emit lints in the order in which they occur in the file.
                 unreachables.sort_unstable();
                 for span in unreachables {
-                    unreachable_pattern(cx.tcx, span, arm.hir_id, None);
+                    report_unreachable_pattern(span, arm.hir_id, None);
                 }
             }
         }
@@ -702,26 +766,18 @@ fn report_arm_reachability<'p, 'tcx>(
     }
 }
 
-fn collect_non_exhaustive_tys<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    pat: &WitnessPat<'tcx>,
-    non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
-) {
-    if matches!(pat.ctor(), Constructor::NonExhaustive) {
-        non_exhaustive_tys.insert(pat.ty());
-    }
-    if let Constructor::IntRange(range) = pat.ctor() {
-        if range.is_beyond_boundaries(pat.ty(), tcx) {
-            // The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
-            non_exhaustive_tys.insert(pat.ty());
-        }
+/// Checks for common cases of "catchall" patterns that may not be intended as such.
+fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
+    use Constructor::*;
+    match pat.ctor() {
+        Wildcard => true,
+        Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
+        _ => false,
     }
-    pat.iter_fields()
-        .for_each(|field_pat| collect_non_exhaustive_tys(tcx, field_pat, non_exhaustive_tys))
 }
 
 /// Report that a match is not exhaustive.
-fn non_exhaustive_match<'p, 'tcx>(
+fn report_non_exhaustive_match<'p, 'tcx>(
     cx: &MatchCheckCtxt<'p, 'tcx>,
     thir: &Thir<'tcx>,
     scrut_ty: Ty<'tcx>,
@@ -755,7 +811,14 @@ fn non_exhaustive_match<'p, 'tcx>(
             sp,
             format!("non-exhaustive patterns: {joined_patterns} not covered"),
         );
-        err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
+        err.span_label(
+            sp,
+            format!(
+                "pattern{} {} not covered",
+                rustc_errors::pluralize!(witnesses.len()),
+                joined_patterns
+            ),
+        );
         patterns_len = witnesses.len();
         pattern = if witnesses.len() < 4 {
             witnesses
@@ -768,7 +831,17 @@ fn non_exhaustive_match<'p, 'tcx>(
         };
     };
 
-    adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
+    // Point at the definition of non-covered `enum` variants.
+    if let Some(AdtDefinedHere { adt_def_span, ty, variants }) =
+        report_adt_defined_here(cx.tcx, scrut_ty, &witnesses, true)
+    {
+        let mut multi_span = MultiSpan::from_span(adt_def_span);
+        multi_span.push_span_label(adt_def_span, "");
+        for Variant { span } in variants {
+            multi_span.push_span_label(span, "not covered");
+        }
+        err.span_note(multi_span, format!("`{ty}` defined here"));
+    }
     err.note(format!("the matched value is of type `{}`", scrut_ty));
 
     if !is_empty_match {
@@ -910,7 +983,7 @@ fn non_exhaustive_match<'p, 'tcx>(
     err.emit()
 }
 
-pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
+fn joined_uncovered_patterns<'p, 'tcx>(
     cx: &MatchCheckCtxt<'p, 'tcx>,
     witnesses: &[WitnessPat<'tcx>],
 ) -> String {
@@ -931,48 +1004,51 @@ pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
     }
 }
 
-pub(crate) fn pattern_not_covered_label(
-    witnesses: &[WitnessPat<'_>],
-    joined_patterns: &str,
-) -> String {
-    format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
+fn collect_non_exhaustive_tys<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    pat: &WitnessPat<'tcx>,
+    non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
+) {
+    if matches!(pat.ctor(), Constructor::NonExhaustive) {
+        non_exhaustive_tys.insert(pat.ty());
+    }
+    if let Constructor::IntRange(range) = pat.ctor() {
+        if range.is_beyond_boundaries(pat.ty(), tcx) {
+            // The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
+            non_exhaustive_tys.insert(pat.ty());
+        }
+    }
+    pat.iter_fields()
+        .for_each(|field_pat| collect_non_exhaustive_tys(tcx, field_pat, non_exhaustive_tys))
 }
 
-/// Point at the definition of non-covered `enum` variants.
-fn adt_defined_here<'p, 'tcx>(
-    cx: &MatchCheckCtxt<'p, 'tcx>,
-    err: &mut Diagnostic,
+fn report_adt_defined_here<'tcx>(
+    tcx: TyCtxt<'tcx>,
     ty: Ty<'tcx>,
     witnesses: &[WitnessPat<'tcx>],
-) {
+    point_at_non_local_ty: bool,
+) -> Option<AdtDefinedHere<'tcx>> {
     let ty = ty.peel_refs();
-    if let ty::Adt(def, _) = ty.kind() {
-        let mut spans = vec![];
-        if witnesses.len() < 5 {
-            for sp in maybe_point_at_variant(cx, *def, witnesses.iter()) {
-                spans.push(sp);
-            }
-        }
-        let def_span = cx
-            .tcx
-            .hir()
-            .get_if_local(def.did())
-            .and_then(|node| node.ident())
-            .map(|ident| ident.span)
-            .unwrap_or_else(|| cx.tcx.def_span(def.did()));
-        let mut span: MultiSpan =
-            if spans.is_empty() { def_span.into() } else { spans.clone().into() };
-
-        span.push_span_label(def_span, "");
-        for pat in spans {
-            span.push_span_label(pat, "not covered");
-        }
-        err.span_note(span, format!("`{ty}` defined here"));
+    let ty::Adt(def, _) = ty.kind() else {
+        return None;
+    };
+    let adt_def_span =
+        tcx.hir().get_if_local(def.did()).and_then(|node| node.ident()).map(|ident| ident.span);
+    let adt_def_span = if point_at_non_local_ty {
+        adt_def_span.unwrap_or_else(|| tcx.def_span(def.did()))
+    } else {
+        adt_def_span?
+    };
+
+    let mut variants = vec![];
+    for span in maybe_point_at_variant(tcx, *def, witnesses.iter().take(5)) {
+        variants.push(Variant { span });
     }
+    Some(AdtDefinedHere { adt_def_span, ty, variants })
 }
 
-fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
-    cx: &MatchCheckCtxt<'p, 'tcx>,
+fn maybe_point_at_variant<'a, 'tcx: 'a>(
+    tcx: TyCtxt<'tcx>,
     def: AdtDef<'tcx>,
     patterns: impl Iterator<Item = &'a WitnessPat<'tcx>>,
 ) -> Vec<Span> {
@@ -985,7 +1061,7 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
             {
                 continue;
             }
-            let sp = def.variant(*variant_index).ident(cx.tcx).span;
+            let sp = def.variant(*variant_index).ident(tcx).span;
             if covered.contains(&sp) {
                 // Don't point at variants that have already been covered due to other patterns to avoid
                 // visual clutter.
@@ -993,112 +1069,7 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
             }
             covered.push(sp);
         }
-        covered.extend(maybe_point_at_variant(cx, def, pattern.iter_fields()));
+        covered.extend(maybe_point_at_variant(tcx, def, pattern.iter_fields()));
     }
     covered
 }
-
-/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
-/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
-///
-/// For example, this would reject:
-/// - `ref x @ Some(ref mut y)`,
-/// - `ref mut x @ Some(ref y)`,
-/// - `ref mut x @ Some(ref mut y)`,
-/// - `ref mut? x @ Some(y)`, and
-/// - `x @ Some(ref mut? y)`.
-///
-/// This analysis is *not* subsumed by NLL.
-fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, '_, 'tcx>, pat: &Pat<'tcx>) {
-    // Extract `sub` in `binding @ sub`.
-    let PatKind::Binding { name, mode, ty, subpattern: Some(box ref sub), .. } = pat.kind else {
-        return;
-    };
-
-    let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.param_env);
-
-    let sess = cx.tcx.sess;
-
-    // Get the binding move, extract the mutability if by-ref.
-    let mut_outer = match mode {
-        BindingMode::ByValue if is_binding_by_move(ty) => {
-            // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
-            let mut conflicts_ref = Vec::new();
-            sub.each_binding(|_, mode, _, span| match mode {
-                BindingMode::ByValue => {}
-                BindingMode::ByRef(_) => conflicts_ref.push(span),
-            });
-            if !conflicts_ref.is_empty() {
-                sess.emit_err(BorrowOfMovedValue {
-                    binding_span: pat.span,
-                    conflicts_ref,
-                    name,
-                    ty,
-                    suggest_borrowing: Some(pat.span.shrink_to_lo()),
-                });
-            }
-            return;
-        }
-        BindingMode::ByValue => return,
-        BindingMode::ByRef(m) => m.mutability(),
-    };
-
-    // We now have `ref $mut_outer binding @ sub` (semantically).
-    // Recurse into each binding in `sub` and find mutability or move conflicts.
-    let mut conflicts_move = Vec::new();
-    let mut conflicts_mut_mut = Vec::new();
-    let mut conflicts_mut_ref = Vec::new();
-    sub.each_binding(|name, mode, ty, span| {
-        match mode {
-            BindingMode::ByRef(mut_inner) => match (mut_outer, mut_inner.mutability()) {
-                // Both sides are `ref`.
-                (Mutability::Not, Mutability::Not) => {}
-                // 2x `ref mut`.
-                (Mutability::Mut, Mutability::Mut) => {
-                    conflicts_mut_mut.push(Conflict::Mut { span, name })
-                }
-                (Mutability::Not, Mutability::Mut) => {
-                    conflicts_mut_ref.push(Conflict::Mut { span, name })
-                }
-                (Mutability::Mut, Mutability::Not) => {
-                    conflicts_mut_ref.push(Conflict::Ref { span, name })
-                }
-            },
-            BindingMode::ByValue if is_binding_by_move(ty) => {
-                conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict.
-            }
-            BindingMode::ByValue => {} // `ref mut?` + by-copy is fine.
-        }
-    });
-
-    let report_mut_mut = !conflicts_mut_mut.is_empty();
-    let report_mut_ref = !conflicts_mut_ref.is_empty();
-    let report_move_conflict = !conflicts_move.is_empty();
-
-    let mut occurrences = match mut_outer {
-        Mutability::Mut => vec![Conflict::Mut { span: pat.span, name }],
-        Mutability::Not => vec![Conflict::Ref { span: pat.span, name }],
-    };
-    occurrences.extend(conflicts_mut_mut);
-    occurrences.extend(conflicts_mut_ref);
-    occurrences.extend(conflicts_move);
-
-    // Report errors if any.
-    if report_mut_mut {
-        // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
-        sess.emit_err(MultipleMutBorrows { span: pat.span, occurrences });
-    } else if report_mut_ref {
-        // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
-        match mut_outer {
-            Mutability::Mut => {
-                sess.emit_err(AlreadyMutBorrowed { span: pat.span, occurrences });
-            }
-            Mutability::Not => {
-                sess.emit_err(AlreadyBorrowed { span: pat.span, occurrences });
-            }
-        };
-    } else if report_move_conflict {
-        // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
-        sess.emit_err(MovedWhileBorrowed { span: pat.span, occurrences });
-    }
-}
diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
index 1eb1dd72a61..da7b6587a72 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
@@ -311,7 +311,10 @@ use super::deconstruct_pat::{
     Constructor, ConstructorSet, DeconstructedPat, IntRange, MaybeInfiniteInt, SplitConstructorSet,
     WitnessPat,
 };
-use crate::errors::{NonExhaustiveOmittedPattern, Overlap, OverlappingRangeEndpoints, Uncovered};
+use crate::errors::{
+    NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Overlap,
+    OverlappingRangeEndpoints, Uncovered,
+};
 
 use rustc_data_structures::captures::Captures;
 
@@ -337,6 +340,8 @@ pub(crate) struct MatchCheckCtxt<'p, 'tcx> {
     pub(crate) module: DefId,
     pub(crate) param_env: ty::ParamEnv<'tcx>,
     pub(crate) pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
+    /// The span of the whole match, if applicable.
+    pub(crate) match_span: Option<Span>,
     /// Only produce `NON_EXHAUSTIVE_OMITTED_PATTERNS` lint on refutable patterns.
     pub(crate) refutable: bool,
 }
@@ -1149,28 +1154,50 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
 
     // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
     // `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
-    if cx.refutable
-        && non_exhaustiveness_witnesses.is_empty()
-        && !matches!(
+    if cx.refutable && non_exhaustiveness_witnesses.is_empty() {
+        if !matches!(
             cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, lint_root).0,
             rustc_session::lint::Level::Allow
-        )
-    {
-        let witnesses = collect_nonexhaustive_missing_variants(cx, &pat_column);
-        if !witnesses.is_empty() {
-            // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
-            // is not exhaustive enough.
-            //
-            // NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
-            cx.tcx.emit_spanned_lint(
-                NON_EXHAUSTIVE_OMITTED_PATTERNS,
-                lint_root,
-                scrut_span,
-                NonExhaustiveOmittedPattern {
-                    scrut_ty,
-                    uncovered: Uncovered::new(scrut_span, cx, witnesses),
-                },
-            );
+        ) {
+            let witnesses = collect_nonexhaustive_missing_variants(cx, &pat_column);
+
+            if !witnesses.is_empty() {
+                // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
+                // is not exhaustive enough.
+                //
+                // NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
+                cx.tcx.emit_spanned_lint(
+                    NON_EXHAUSTIVE_OMITTED_PATTERNS,
+                    lint_root,
+                    scrut_span,
+                    NonExhaustiveOmittedPattern {
+                        scrut_ty,
+                        uncovered: Uncovered::new(scrut_span, cx, witnesses),
+                    },
+                );
+            }
+        } else {
+            // We used to allow putting the `#[allow(non_exhaustive_omitted_patterns)]` on a match
+            // arm. This no longer makes sense so we warn users, to avoid silently breaking their
+            // usage of the lint.
+            for arm in arms {
+                let (lint_level, lint_level_source) =
+                    cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.hir_id);
+                if !matches!(lint_level, rustc_session::lint::Level::Allow) {
+                    let decorator = NonExhaustiveOmittedPatternLintOnArm {
+                        lint_span: lint_level_source.span(),
+                        suggest_lint_on_match: cx.match_span.map(|span| span.shrink_to_lo()),
+                        lint_level: lint_level.as_str(),
+                        lint_name: "non_exhaustive_omitted_patterns",
+                    };
+
+                    use rustc_errors::DecorateLint;
+                    let mut err = cx.tcx.sess.struct_span_warn(arm.pat.span(), "");
+                    err.set_primary_message(decorator.msg());
+                    decorator.decorate_lint(&mut err);
+                    err.emit();
+                }
+            }
         }
     }
 
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject b4d18d4bd3db6d872892f6c87c51a02999b8080
+Subproject 65e297d1ec0dee1a74800efe600b8dc163bcf5d
diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs
index e1b95aa5776..5c277f925a8 100644
--- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs
+++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs
@@ -9,12 +9,12 @@ fn repeat() -> ! {
 }
 
 pub fn f(x: Ordering) {
+    #[deny(non_exhaustive_omitted_patterns)]
     match x {
         Ordering::Relaxed => println!("relaxed"),
         Ordering::Release => println!("release"),
         Ordering::Acquire => println!("acquire"),
         Ordering::AcqRel | Ordering::SeqCst => repeat(),
-        #[deny(non_exhaustive_omitted_patterns)]
         _ => repeat(),
     }
 }
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 3a3b26c2f63..5e6dfaca702 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -107,7 +107,6 @@ const EXCEPTIONS_CARGO: ExceptionList = &[
     // tidy-alphabetical-start
     ("bitmaps", "MPL-2.0+"),
     ("bytesize", "Apache-2.0"),
-    ("byteyarn", "Apache-2.0"),
     ("ciborium", "Apache-2.0"),
     ("ciborium-io", "Apache-2.0"),
     ("ciborium-ll", "Apache-2.0"),
diff --git a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr
index 0807f459029..85426dd9a5e 100644
--- a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr
+++ b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr
@@ -5,10 +5,10 @@ LL |     let _b = || { match l1 { L1::A => () } };
    |                         ^^ pattern `L1::B` not covered
    |
 note: `L1` defined here
-  --> $DIR/non-exhaustive-match.rs:12:14
+  --> $DIR/non-exhaustive-match.rs:12:6
    |
 LL | enum L1 { A, B }
-   |      --      ^ not covered
+   |      ^^      - not covered
    = note: the matched value is of type `L1`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
diff --git a/tests/ui/error-codes/E0004.stderr b/tests/ui/error-codes/E0004.stderr
index 603bc5237ea..ced478d65ea 100644
--- a/tests/ui/error-codes/E0004.stderr
+++ b/tests/ui/error-codes/E0004.stderr
@@ -5,12 +5,12 @@ LL |     match x {
    |           ^ pattern `Terminator::HastaLaVistaBaby` not covered
    |
 note: `Terminator` defined here
-  --> $DIR/E0004.rs:2:5
+  --> $DIR/E0004.rs:1:6
    |
 LL | enum Terminator {
-   |      ----------
+   |      ^^^^^^^^^^
 LL |     HastaLaVistaBaby,
-   |     ^^^^^^^^^^^^^^^^ not covered
+   |     ---------------- not covered
    = note: the matched value is of type `Terminator`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
diff --git a/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.stderr b/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.stderr
index eb61c4cf159..8af0eedc82b 100644
--- a/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.stderr
+++ b/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.stderr
@@ -134,13 +134,13 @@ LL |     match Foo::A {
    |           ^^^^^^ pattern `Foo::C` not covered
    |
 note: `Foo` defined here
-  --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:16:9
+  --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:13:10
    |
 LL |     enum Foo {
-   |          ---
+   |          ^^^
 ...
 LL |         C,
-   |         ^ not covered
+   |         - not covered
    = note: the matched value is of type `Foo`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
diff --git a/tests/ui/match/match_non_exhaustive.stderr b/tests/ui/match/match_non_exhaustive.stderr
index 7b8bdfe0053..40be39ec077 100644
--- a/tests/ui/match/match_non_exhaustive.stderr
+++ b/tests/ui/match/match_non_exhaustive.stderr
@@ -5,10 +5,10 @@ LL |     match l { L::A => () };
    |           ^ pattern `L::B` not covered
    |
 note: `L` defined here
-  --> $DIR/match_non_exhaustive.rs:10:13
+  --> $DIR/match_non_exhaustive.rs:10:6
    |
 LL | enum L { A, B }
-   |      -      ^ not covered
+   |      ^      - not covered
    = note: the matched value is of type `L`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
diff --git a/tests/ui/pattern/issue-94866.stderr b/tests/ui/pattern/issue-94866.stderr
index b3c17ce8974..dee4b3f557c 100644
--- a/tests/ui/pattern/issue-94866.stderr
+++ b/tests/ui/pattern/issue-94866.stderr
@@ -5,10 +5,10 @@ LL |     match Enum::A {
    |           ^^^^^^^ pattern `Enum::B` not covered
    |
 note: `Enum` defined here
-  --> $DIR/issue-94866.rs:7:16
+  --> $DIR/issue-94866.rs:7:6
    |
 LL | enum Enum { A, B }
-   |      ----      ^ not covered
+   |      ^^^^      - not covered
    = note: the matched value is of type `Enum`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
diff --git a/tests/ui/pattern/usefulness/conflicting_bindings.rs b/tests/ui/pattern/usefulness/conflicting_bindings.rs
new file mode 100644
index 00000000000..0b3e7ce9e9a
--- /dev/null
+++ b/tests/ui/pattern/usefulness/conflicting_bindings.rs
@@ -0,0 +1,24 @@
+#![feature(if_let_guard, let_chains)]
+
+fn main() {
+    let mut x = Some(String::new());
+    let ref mut y @ ref mut z = x;
+    //~^ ERROR: mutable more than once
+    let Some(ref mut y @ ref mut z) = x else { return };
+    //~^ ERROR: mutable more than once
+    if let Some(ref mut y @ ref mut z) = x {}
+    //~^ ERROR: mutable more than once
+    if let Some(ref mut y @ ref mut z) = x && true {}
+    //~^ ERROR: mutable more than once
+    while let Some(ref mut y @ ref mut z) = x {}
+    //~^ ERROR: mutable more than once
+    while let Some(ref mut y @ ref mut z) = x && true {}
+    //~^ ERROR: mutable more than once
+    match x {
+        ref mut y @ ref mut z => {} //~ ERROR: mutable more than once
+    }
+    match () {
+        () if let Some(ref mut y @ ref mut z) = x => {} //~ ERROR: mutable more than once
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/conflicting_bindings.stderr b/tests/ui/pattern/usefulness/conflicting_bindings.stderr
new file mode 100644
index 00000000000..679fc83e7f5
--- /dev/null
+++ b/tests/ui/pattern/usefulness/conflicting_bindings.stderr
@@ -0,0 +1,66 @@
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/conflicting_bindings.rs:5:9
+   |
+LL |     let ref mut y @ ref mut z = x;
+   |         ^^^^^^^^^   --------- value is mutably borrowed by `z` here
+   |         |
+   |         value is mutably borrowed by `y` here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/conflicting_bindings.rs:7:14
+   |
+LL |     let Some(ref mut y @ ref mut z) = x else { return };
+   |              ^^^^^^^^^   --------- value is mutably borrowed by `z` here
+   |              |
+   |              value is mutably borrowed by `y` here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/conflicting_bindings.rs:9:17
+   |
+LL |     if let Some(ref mut y @ ref mut z) = x {}
+   |                 ^^^^^^^^^   --------- value is mutably borrowed by `z` here
+   |                 |
+   |                 value is mutably borrowed by `y` here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/conflicting_bindings.rs:11:17
+   |
+LL |     if let Some(ref mut y @ ref mut z) = x && true {}
+   |                 ^^^^^^^^^   --------- value is mutably borrowed by `z` here
+   |                 |
+   |                 value is mutably borrowed by `y` here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/conflicting_bindings.rs:13:20
+   |
+LL |     while let Some(ref mut y @ ref mut z) = x {}
+   |                    ^^^^^^^^^   --------- value is mutably borrowed by `z` here
+   |                    |
+   |                    value is mutably borrowed by `y` here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/conflicting_bindings.rs:15:20
+   |
+LL |     while let Some(ref mut y @ ref mut z) = x && true {}
+   |                    ^^^^^^^^^   --------- value is mutably borrowed by `z` here
+   |                    |
+   |                    value is mutably borrowed by `y` here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/conflicting_bindings.rs:18:9
+   |
+LL |         ref mut y @ ref mut z => {}
+   |         ^^^^^^^^^   --------- value is mutably borrowed by `z` here
+   |         |
+   |         value is mutably borrowed by `y` here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/conflicting_bindings.rs:21:24
+   |
+LL |         () if let Some(ref mut y @ ref mut z) = x => {}
+   |                        ^^^^^^^^^   --------- value is mutably borrowed by `z` here
+   |                        |
+   |                        value is mutably borrowed by `y` here
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr b/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr
index ff29de03d6b..24f3eaa5230 100644
--- a/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr
+++ b/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr
@@ -23,13 +23,13 @@ LL |     match HiddenEnum::A {
    |           ^^^^^^^^^^^^^ pattern `HiddenEnum::B` not covered
    |
 note: `HiddenEnum` defined here
-  --> $DIR/auxiliary/hidden.rs:3:5
+  --> $DIR/auxiliary/hidden.rs:1:1
    |
 LL | pub enum HiddenEnum {
-   | -------------------
+   | ^^^^^^^^^^^^^^^^^^^
 LL |     A,
 LL |     B,
-   |     ^ not covered
+   |     - not covered
    = note: the matched value is of type `HiddenEnum`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
@@ -44,13 +44,13 @@ LL |     match HiddenEnum::A {
    |           ^^^^^^^^^^^^^ patterns `HiddenEnum::B` and `_` not covered
    |
 note: `HiddenEnum` defined here
-  --> $DIR/auxiliary/hidden.rs:3:5
+  --> $DIR/auxiliary/hidden.rs:1:1
    |
 LL | pub enum HiddenEnum {
-   | -------------------
+   | ^^^^^^^^^^^^^^^^^^^
 LL |     A,
 LL |     B,
-   |     ^ not covered
+   |     - not covered
    = note: the matched value is of type `HiddenEnum`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
    |
@@ -83,13 +83,13 @@ LL |     match InCrate::A {
    |           ^^^^^^^^^^ pattern `InCrate::C` not covered
    |
 note: `InCrate` defined here
-  --> $DIR/doc-hidden-non-exhaustive.rs:11:5
+  --> $DIR/doc-hidden-non-exhaustive.rs:7:6
    |
 LL | enum InCrate {
-   |      -------
+   |      ^^^^^^^
 ...
 LL |     C,
-   |     ^ not covered
+   |     - not covered
    = note: the matched value is of type `InCrate`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
diff --git a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr
index 5b81a8c3d3c..8f9bd5bde89 100644
--- a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr
+++ b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr
@@ -1,5 +1,5 @@
 error: unreachable pattern
-  --> $DIR/empty-match.rs:58:9
+  --> $DIR/empty-match.rs:68:9
    |
 LL |         _ => {},
    |         ^
@@ -11,25 +11,25 @@ LL | #![deny(unreachable_patterns)]
    |         ^^^^^^^^^^^^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-match.rs:61:9
+  --> $DIR/empty-match.rs:71:9
    |
 LL |         _ if false => {},
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-match.rs:68:9
+  --> $DIR/empty-match.rs:78:9
    |
 LL |         _ => {},
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-match.rs:71:9
+  --> $DIR/empty-match.rs:81:9
    |
 LL |         _ if false => {},
    |         ^
 
 error[E0005]: refutable pattern in local binding
-  --> $DIR/empty-match.rs:76:9
+  --> $DIR/empty-match.rs:86:9
    |
 LL |     let None = x;
    |         ^^^^ pattern `Some(_)` not covered
@@ -44,19 +44,19 @@ LL |     if let None = x { todo!() };
    |     ++              +++++++++++
 
 error: unreachable pattern
-  --> $DIR/empty-match.rs:88:9
+  --> $DIR/empty-match.rs:98:9
    |
 LL |         _ => {},
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-match.rs:91:9
+  --> $DIR/empty-match.rs:101:9
    |
 LL |         _ if false => {},
    |         ^
 
 error[E0004]: non-exhaustive patterns: type `u8` is non-empty
-  --> $DIR/empty-match.rs:109:20
+  --> $DIR/empty-match.rs:119:20
    |
 LL |     match_no_arms!(0u8);
    |                    ^^^
@@ -65,7 +65,7 @@ LL |     match_no_arms!(0u8);
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty
-  --> $DIR/empty-match.rs:111:20
+  --> $DIR/empty-match.rs:121:20
    |
 LL |     match_no_arms!(NonEmptyStruct1);
    |                    ^^^^^^^^^^^^^^^
@@ -79,7 +79,7 @@ LL | struct NonEmptyStruct1;
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty
-  --> $DIR/empty-match.rs:113:20
+  --> $DIR/empty-match.rs:123:20
    |
 LL |     match_no_arms!(NonEmptyStruct2(true));
    |                    ^^^^^^^^^^^^^^^^^^^^^
@@ -93,7 +93,7 @@ LL | struct NonEmptyStruct2(bool);
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
-  --> $DIR/empty-match.rs:115:20
+  --> $DIR/empty-match.rs:125:20
    |
 LL |     match_no_arms!((NonEmptyUnion1 { foo: () }));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -107,7 +107,7 @@ LL | union NonEmptyUnion1 {
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
-  --> $DIR/empty-match.rs:117:20
+  --> $DIR/empty-match.rs:127:20
    |
 LL |     match_no_arms!((NonEmptyUnion2 { foo: () }));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -121,42 +121,44 @@ LL | union NonEmptyUnion2 {
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
-  --> $DIR/empty-match.rs:119:20
+  --> $DIR/empty-match.rs:129:20
    |
 LL |     match_no_arms!(NonEmptyEnum1::Foo(true));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
    |
 note: `NonEmptyEnum1` defined here
-  --> $DIR/empty-match.rs:33:5
+  --> $DIR/empty-match.rs:32:6
    |
 LL | enum NonEmptyEnum1 {
-   |      -------------
+   |      ^^^^^^^^^^^^^
+...
 LL |     Foo(bool),
-   |     ^^^ not covered
+   |     --- not covered
    = note: the matched value is of type `NonEmptyEnum1`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
-  --> $DIR/empty-match.rs:122:20
+  --> $DIR/empty-match.rs:132:20
    |
 LL |     match_no_arms!(NonEmptyEnum2::Foo(true));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
    |
 note: `NonEmptyEnum2` defined here
-  --> $DIR/empty-match.rs:40:5
+  --> $DIR/empty-match.rs:39:6
    |
 LL | enum NonEmptyEnum2 {
-   |      -------------
+   |      ^^^^^^^^^^^^^
+...
 LL |     Foo(bool),
-   |     ^^^ not covered
+   |     --- not covered
 ...
 LL |     Bar,
-   |     ^^^ not covered
+   |     --- not covered
    = note: the matched value is of type `NonEmptyEnum2`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
-  --> $DIR/empty-match.rs:125:20
+  --> $DIR/empty-match.rs:135:20
    |
 LL |     match_no_arms!(NonEmptyEnum5::V1);
    |                    ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
@@ -166,11 +168,19 @@ note: `NonEmptyEnum5` defined here
    |
 LL | enum NonEmptyEnum5 {
    |      ^^^^^^^^^^^^^
+...
+LL |     V1, V2, V3, V4, V5,
+   |     --  --  --  --  -- not covered
+   |     |   |   |   |
+   |     |   |   |   not covered
+   |     |   |   not covered
+   |     |   not covered
+   |     not covered
    = note: the matched value is of type `NonEmptyEnum5`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
 
 error[E0004]: non-exhaustive patterns: `_` not covered
-  --> $DIR/empty-match.rs:129:24
+  --> $DIR/empty-match.rs:139:24
    |
 LL |     match_guarded_arm!(0u8);
    |                        ^^^ pattern `_` not covered
@@ -184,7 +194,7 @@ LL +             _ => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered
-  --> $DIR/empty-match.rs:134:24
+  --> $DIR/empty-match.rs:144:24
    |
 LL |     match_guarded_arm!(NonEmptyStruct1);
    |                        ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered
@@ -203,7 +213,7 @@ LL +             NonEmptyStruct1 => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered
-  --> $DIR/empty-match.rs:139:24
+  --> $DIR/empty-match.rs:149:24
    |
 LL |     match_guarded_arm!(NonEmptyStruct2(true));
    |                        ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered
@@ -222,7 +232,7 @@ LL +             NonEmptyStruct2(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
-  --> $DIR/empty-match.rs:144:24
+  --> $DIR/empty-match.rs:154:24
    |
 LL |     match_guarded_arm!((NonEmptyUnion1 { foo: () }));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
@@ -241,7 +251,7 @@ LL +             NonEmptyUnion1 { .. } => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
-  --> $DIR/empty-match.rs:149:24
+  --> $DIR/empty-match.rs:159:24
    |
 LL |     match_guarded_arm!((NonEmptyUnion2 { foo: () }));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
@@ -260,18 +270,19 @@ LL +             NonEmptyUnion2 { .. } => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
-  --> $DIR/empty-match.rs:154:24
+  --> $DIR/empty-match.rs:164:24
    |
 LL |     match_guarded_arm!(NonEmptyEnum1::Foo(true));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
    |
 note: `NonEmptyEnum1` defined here
-  --> $DIR/empty-match.rs:33:5
+  --> $DIR/empty-match.rs:32:6
    |
 LL | enum NonEmptyEnum1 {
-   |      -------------
+   |      ^^^^^^^^^^^^^
+...
 LL |     Foo(bool),
-   |     ^^^ not covered
+   |     --- not covered
    = note: the matched value is of type `NonEmptyEnum1`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
@@ -281,21 +292,22 @@ LL +             NonEmptyEnum1::Foo(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
-  --> $DIR/empty-match.rs:159:24
+  --> $DIR/empty-match.rs:169:24
    |
 LL |     match_guarded_arm!(NonEmptyEnum2::Foo(true));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
    |
 note: `NonEmptyEnum2` defined here
-  --> $DIR/empty-match.rs:40:5
+  --> $DIR/empty-match.rs:39:6
    |
 LL | enum NonEmptyEnum2 {
-   |      -------------
+   |      ^^^^^^^^^^^^^
+...
 LL |     Foo(bool),
-   |     ^^^ not covered
+   |     --- not covered
 ...
 LL |     Bar,
-   |     ^^^ not covered
+   |     --- not covered
    = note: the matched value is of type `NonEmptyEnum2`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
@@ -305,7 +317,7 @@ LL +             NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
-  --> $DIR/empty-match.rs:164:24
+  --> $DIR/empty-match.rs:174:24
    |
 LL |     match_guarded_arm!(NonEmptyEnum5::V1);
    |                        ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
@@ -315,6 +327,14 @@ note: `NonEmptyEnum5` defined here
    |
 LL | enum NonEmptyEnum5 {
    |      ^^^^^^^^^^^^^
+...
+LL |     V1, V2, V3, V4, V5,
+   |     --  --  --  --  -- not covered
+   |     |   |   |   |
+   |     |   |   |   not covered
+   |     |   |   not covered
+   |     |   not covered
+   |     not covered
    = note: the matched value is of type `NonEmptyEnum5`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
diff --git a/tests/ui/pattern/usefulness/empty-match.normal.stderr b/tests/ui/pattern/usefulness/empty-match.normal.stderr
index 6d17455086b..7f0389f40e2 100644
--- a/tests/ui/pattern/usefulness/empty-match.normal.stderr
+++ b/tests/ui/pattern/usefulness/empty-match.normal.stderr
@@ -1,5 +1,5 @@
 error: unreachable pattern
-  --> $DIR/empty-match.rs:58:9
+  --> $DIR/empty-match.rs:68:9
    |
 LL |         _ => {},
    |         ^
@@ -11,25 +11,25 @@ LL | #![deny(unreachable_patterns)]
    |         ^^^^^^^^^^^^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-match.rs:61:9
+  --> $DIR/empty-match.rs:71:9
    |
 LL |         _ if false => {},
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-match.rs:68:9
+  --> $DIR/empty-match.rs:78:9
    |
 LL |         _ => {},
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-match.rs:71:9
+  --> $DIR/empty-match.rs:81:9
    |
 LL |         _ if false => {},
    |         ^
 
 error[E0005]: refutable pattern in local binding
-  --> $DIR/empty-match.rs:76:9
+  --> $DIR/empty-match.rs:86:9
    |
 LL |     let None = x;
    |         ^^^^ pattern `Some(_)` not covered
@@ -43,19 +43,19 @@ LL |     if let None = x { todo!() };
    |     ++              +++++++++++
 
 error: unreachable pattern
-  --> $DIR/empty-match.rs:88:9
+  --> $DIR/empty-match.rs:98:9
    |
 LL |         _ => {},
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-match.rs:91:9
+  --> $DIR/empty-match.rs:101:9
    |
 LL |         _ if false => {},
    |         ^
 
 error[E0004]: non-exhaustive patterns: type `u8` is non-empty
-  --> $DIR/empty-match.rs:109:20
+  --> $DIR/empty-match.rs:119:20
    |
 LL |     match_no_arms!(0u8);
    |                    ^^^
@@ -64,7 +64,7 @@ LL |     match_no_arms!(0u8);
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty
-  --> $DIR/empty-match.rs:111:20
+  --> $DIR/empty-match.rs:121:20
    |
 LL |     match_no_arms!(NonEmptyStruct1);
    |                    ^^^^^^^^^^^^^^^
@@ -78,7 +78,7 @@ LL | struct NonEmptyStruct1;
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty
-  --> $DIR/empty-match.rs:113:20
+  --> $DIR/empty-match.rs:123:20
    |
 LL |     match_no_arms!(NonEmptyStruct2(true));
    |                    ^^^^^^^^^^^^^^^^^^^^^
@@ -92,7 +92,7 @@ LL | struct NonEmptyStruct2(bool);
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
-  --> $DIR/empty-match.rs:115:20
+  --> $DIR/empty-match.rs:125:20
    |
 LL |     match_no_arms!((NonEmptyUnion1 { foo: () }));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -106,7 +106,7 @@ LL | union NonEmptyUnion1 {
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
-  --> $DIR/empty-match.rs:117:20
+  --> $DIR/empty-match.rs:127:20
    |
 LL |     match_no_arms!((NonEmptyUnion2 { foo: () }));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -120,42 +120,44 @@ LL | union NonEmptyUnion2 {
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
-  --> $DIR/empty-match.rs:119:20
+  --> $DIR/empty-match.rs:129:20
    |
 LL |     match_no_arms!(NonEmptyEnum1::Foo(true));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
    |
 note: `NonEmptyEnum1` defined here
-  --> $DIR/empty-match.rs:33:5
+  --> $DIR/empty-match.rs:32:6
    |
 LL | enum NonEmptyEnum1 {
-   |      -------------
+   |      ^^^^^^^^^^^^^
+...
 LL |     Foo(bool),
-   |     ^^^ not covered
+   |     --- not covered
    = note: the matched value is of type `NonEmptyEnum1`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
-  --> $DIR/empty-match.rs:122:20
+  --> $DIR/empty-match.rs:132:20
    |
 LL |     match_no_arms!(NonEmptyEnum2::Foo(true));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
    |
 note: `NonEmptyEnum2` defined here
-  --> $DIR/empty-match.rs:40:5
+  --> $DIR/empty-match.rs:39:6
    |
 LL | enum NonEmptyEnum2 {
-   |      -------------
+   |      ^^^^^^^^^^^^^
+...
 LL |     Foo(bool),
-   |     ^^^ not covered
+   |     --- not covered
 ...
 LL |     Bar,
-   |     ^^^ not covered
+   |     --- not covered
    = note: the matched value is of type `NonEmptyEnum2`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
-  --> $DIR/empty-match.rs:125:20
+  --> $DIR/empty-match.rs:135:20
    |
 LL |     match_no_arms!(NonEmptyEnum5::V1);
    |                    ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
@@ -165,11 +167,19 @@ note: `NonEmptyEnum5` defined here
    |
 LL | enum NonEmptyEnum5 {
    |      ^^^^^^^^^^^^^
+...
+LL |     V1, V2, V3, V4, V5,
+   |     --  --  --  --  -- not covered
+   |     |   |   |   |
+   |     |   |   |   not covered
+   |     |   |   not covered
+   |     |   not covered
+   |     not covered
    = note: the matched value is of type `NonEmptyEnum5`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
 
 error[E0004]: non-exhaustive patterns: `_` not covered
-  --> $DIR/empty-match.rs:129:24
+  --> $DIR/empty-match.rs:139:24
    |
 LL |     match_guarded_arm!(0u8);
    |                        ^^^ pattern `_` not covered
@@ -183,7 +193,7 @@ LL +             _ => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered
-  --> $DIR/empty-match.rs:134:24
+  --> $DIR/empty-match.rs:144:24
    |
 LL |     match_guarded_arm!(NonEmptyStruct1);
    |                        ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered
@@ -202,7 +212,7 @@ LL +             NonEmptyStruct1 => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered
-  --> $DIR/empty-match.rs:139:24
+  --> $DIR/empty-match.rs:149:24
    |
 LL |     match_guarded_arm!(NonEmptyStruct2(true));
    |                        ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered
@@ -221,7 +231,7 @@ LL +             NonEmptyStruct2(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
-  --> $DIR/empty-match.rs:144:24
+  --> $DIR/empty-match.rs:154:24
    |
 LL |     match_guarded_arm!((NonEmptyUnion1 { foo: () }));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
@@ -240,7 +250,7 @@ LL +             NonEmptyUnion1 { .. } => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
-  --> $DIR/empty-match.rs:149:24
+  --> $DIR/empty-match.rs:159:24
    |
 LL |     match_guarded_arm!((NonEmptyUnion2 { foo: () }));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
@@ -259,18 +269,19 @@ LL +             NonEmptyUnion2 { .. } => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
-  --> $DIR/empty-match.rs:154:24
+  --> $DIR/empty-match.rs:164:24
    |
 LL |     match_guarded_arm!(NonEmptyEnum1::Foo(true));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
    |
 note: `NonEmptyEnum1` defined here
-  --> $DIR/empty-match.rs:33:5
+  --> $DIR/empty-match.rs:32:6
    |
 LL | enum NonEmptyEnum1 {
-   |      -------------
+   |      ^^^^^^^^^^^^^
+...
 LL |     Foo(bool),
-   |     ^^^ not covered
+   |     --- not covered
    = note: the matched value is of type `NonEmptyEnum1`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
@@ -280,21 +291,22 @@ LL +             NonEmptyEnum1::Foo(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
-  --> $DIR/empty-match.rs:159:24
+  --> $DIR/empty-match.rs:169:24
    |
 LL |     match_guarded_arm!(NonEmptyEnum2::Foo(true));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
    |
 note: `NonEmptyEnum2` defined here
-  --> $DIR/empty-match.rs:40:5
+  --> $DIR/empty-match.rs:39:6
    |
 LL | enum NonEmptyEnum2 {
-   |      -------------
+   |      ^^^^^^^^^^^^^
+...
 LL |     Foo(bool),
-   |     ^^^ not covered
+   |     --- not covered
 ...
 LL |     Bar,
-   |     ^^^ not covered
+   |     --- not covered
    = note: the matched value is of type `NonEmptyEnum2`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
@@ -304,7 +316,7 @@ LL +             NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
-  --> $DIR/empty-match.rs:164:24
+  --> $DIR/empty-match.rs:174:24
    |
 LL |     match_guarded_arm!(NonEmptyEnum5::V1);
    |                        ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
@@ -314,6 +326,14 @@ note: `NonEmptyEnum5` defined here
    |
 LL | enum NonEmptyEnum5 {
    |      ^^^^^^^^^^^^^
+...
+LL |     V1, V2, V3, V4, V5,
+   |     --  --  --  --  -- not covered
+   |     |   |   |   |
+   |     |   |   |   not covered
+   |     |   |   not covered
+   |     |   not covered
+   |     not covered
    = note: the matched value is of type `NonEmptyEnum5`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
diff --git a/tests/ui/pattern/usefulness/empty-match.rs b/tests/ui/pattern/usefulness/empty-match.rs
index d56d2e3c817..fe5d0bce14f 100644
--- a/tests/ui/pattern/usefulness/empty-match.rs
+++ b/tests/ui/pattern/usefulness/empty-match.rs
@@ -30,17 +30,17 @@ union NonEmptyUnion2 {
     bar: (),
 }
 enum NonEmptyEnum1 {
-    Foo(bool),
     //~^ NOTE `NonEmptyEnum1` defined here
     //~| NOTE `NonEmptyEnum1` defined here
-    //~| NOTE not covered
+    Foo(bool),
+    //~^ NOTE not covered
     //~| NOTE not covered
 }
 enum NonEmptyEnum2 {
-    Foo(bool),
     //~^ NOTE `NonEmptyEnum2` defined here
     //~| NOTE `NonEmptyEnum2` defined here
-    //~| NOTE not covered
+    Foo(bool),
+    //~^ NOTE not covered
     //~| NOTE not covered
     Bar,
     //~^ NOTE not covered
@@ -50,6 +50,16 @@ enum NonEmptyEnum5 {
     //~^ NOTE `NonEmptyEnum5` defined here
     //~| NOTE `NonEmptyEnum5` defined here
     V1, V2, V3, V4, V5,
+    //~^ NOTE not covered
+    //~| NOTE not covered
+    //~| NOTE not covered
+    //~| NOTE not covered
+    //~| NOTE not covered
+    //~| NOTE not covered
+    //~| NOTE not covered
+    //~| NOTE not covered
+    //~| NOTE not covered
+    //~| NOTE not covered
 }
 
 fn empty_enum(x: EmptyEnum) {
diff --git a/tests/ui/pattern/usefulness/issue-35609.stderr b/tests/ui/pattern/usefulness/issue-35609.stderr
index 6d5e2f410bc..9feedfde469 100644
--- a/tests/ui/pattern/usefulness/issue-35609.stderr
+++ b/tests/ui/pattern/usefulness/issue-35609.stderr
@@ -107,6 +107,9 @@ LL |     match Some(A) {
    |
 note: `Option<Enum>` defined here
   --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
    = note: the matched value is of type `Option<Enum>`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
    |
diff --git a/tests/ui/pattern/usefulness/issue-39362.stderr b/tests/ui/pattern/usefulness/issue-39362.stderr
index b8b17918aef..8dc53491606 100644
--- a/tests/ui/pattern/usefulness/issue-39362.stderr
+++ b/tests/ui/pattern/usefulness/issue-39362.stderr
@@ -5,12 +5,12 @@ LL |     match f {
    |           ^ patterns `Foo::Bar { bar: Bar::C, .. }`, `Foo::Bar { bar: Bar::D, .. }`, `Foo::Bar { bar: Bar::E, .. }` and 1 more not covered
    |
 note: `Foo` defined here
-  --> $DIR/issue-39362.rs:2:5
+  --> $DIR/issue-39362.rs:1:6
    |
 LL | enum Foo {
-   |      ---
+   |      ^^^
 LL |     Bar { bar: Bar, id: usize }
-   |     ^^^ not covered
+   |     --- not covered
    = note: the matched value is of type `Foo`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
    |
diff --git a/tests/ui/pattern/usefulness/issue-40221.stderr b/tests/ui/pattern/usefulness/issue-40221.stderr
index 4973e42b054..40b42af26b3 100644
--- a/tests/ui/pattern/usefulness/issue-40221.stderr
+++ b/tests/ui/pattern/usefulness/issue-40221.stderr
@@ -5,12 +5,12 @@ LL |     match proto {
    |           ^^^^^ pattern `P::C(PC::QA)` not covered
    |
 note: `P` defined here
-  --> $DIR/issue-40221.rs:2:5
+  --> $DIR/issue-40221.rs:1:6
    |
 LL | enum P {
-   |      -
+   |      ^
 LL |     C(PC),
-   |     ^ not covered
+   |     - not covered
    = note: the matched value is of type `P`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
diff --git a/tests/ui/pattern/usefulness/issue-56379.stderr b/tests/ui/pattern/usefulness/issue-56379.stderr
index b3e40b99239..50e13bdfdfe 100644
--- a/tests/ui/pattern/usefulness/issue-56379.stderr
+++ b/tests/ui/pattern/usefulness/issue-56379.stderr
@@ -5,16 +5,16 @@ LL |     match Foo::A(true) {
    |           ^^^^^^^^^^^^ patterns `Foo::A(false)`, `Foo::B(false)` and `Foo::C(false)` not covered
    |
 note: `Foo` defined here
-  --> $DIR/issue-56379.rs:2:5
+  --> $DIR/issue-56379.rs:1:6
    |
 LL | enum Foo {
-   |      ---
+   |      ^^^
 LL |     A(bool),
-   |     ^ not covered
+   |     - not covered
 LL |     B(bool),
-   |     ^ not covered
+   |     - not covered
 LL |     C(bool),
-   |     ^ not covered
+   |     - not covered
    = note: the matched value is of type `Foo`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
    |
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs
index 5145f769075..1d1ea8e4964 100644
--- a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs
+++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs
@@ -1,10 +1,15 @@
+#![feature(custom_inner_attributes)]
+#![rustfmt::skip]
 // Test the "defined here" and "not covered" diagnostic hints.
 // We also make sure that references are peeled off from the scrutinee type
 // so that the diagnostics work better with default binding modes.
 
 #[derive(Clone)]
 enum E {
-    //~^ NOTE
+    //~^ NOTE `E` defined here
+    //~| NOTE `E` defined here
+    //~| NOTE `E` defined here
+    //~| NOTE
     //~| NOTE
     //~| NOTE
     //~| NOTE
@@ -12,10 +17,7 @@ enum E {
     //~| NOTE
     A,
     B,
-    //~^ NOTE `E` defined here
-    //~| NOTE `E` defined here
-    //~| NOTE `E` defined here
-    //~| NOTE  not covered
+    //~^ NOTE  not covered
     //~| NOTE  not covered
     //~| NOTE  not covered
     //~| NOTE  not covered
@@ -79,12 +81,12 @@ fn by_ref_thrice(e: & &mut &E) {
 }
 
 enum Opt {
-    //~^ NOTE
+    //~^ NOTE `Opt` defined here
+    //~| NOTE
     //~| NOTE
     Some(u8),
     None,
-    //~^ NOTE `Opt` defined here
-    //~| NOTE not covered
+    //~^ NOTE not covered
     //~| NOTE not covered
 }
 
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr
index 8489e2f14b8..a9e55fa53a6 100644
--- a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr
+++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr
@@ -1,20 +1,20 @@
 error[E0004]: non-exhaustive patterns: `E::B` and `E::C` not covered
-  --> $DIR/non-exhaustive-defined-here.rs:35:11
+  --> $DIR/non-exhaustive-defined-here.rs:37:11
    |
 LL |     match e1 {
    |           ^^ patterns `E::B` and `E::C` not covered
    |
 note: `E` defined here
-  --> $DIR/non-exhaustive-defined-here.rs:14:5
+  --> $DIR/non-exhaustive-defined-here.rs:8:6
    |
 LL | enum E {
-   |      -
+   |      ^
 ...
 LL |     B,
-   |     ^ not covered
+   |     - not covered
 ...
 LL |     C
-   |     ^ not covered
+   |     - not covered
    = note: the matched value is of type `E`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
    |
@@ -23,7 +23,7 @@ LL +         E::B | E::C => todo!()
    |
 
 error[E0005]: refutable pattern in local binding
-  --> $DIR/non-exhaustive-defined-here.rs:41:9
+  --> $DIR/non-exhaustive-defined-here.rs:43:9
    |
 LL |     let E::A = e;
    |         ^^^^ patterns `E::B` and `E::C` not covered
@@ -31,7 +31,7 @@ LL |     let E::A = e;
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
 note: `E` defined here
-  --> $DIR/non-exhaustive-defined-here.rs:6:6
+  --> $DIR/non-exhaustive-defined-here.rs:8:6
    |
 LL | enum E {
    |      ^
@@ -48,22 +48,22 @@ LL |     if let E::A = e { todo!() };
    |     ++              +++++++++++
 
 error[E0004]: non-exhaustive patterns: `&E::B` and `&E::C` not covered
-  --> $DIR/non-exhaustive-defined-here.rs:50:11
+  --> $DIR/non-exhaustive-defined-here.rs:52:11
    |
 LL |     match e {
    |           ^ patterns `&E::B` and `&E::C` not covered
    |
 note: `E` defined here
-  --> $DIR/non-exhaustive-defined-here.rs:14:5
+  --> $DIR/non-exhaustive-defined-here.rs:8:6
    |
 LL | enum E {
-   |      -
+   |      ^
 ...
 LL |     B,
-   |     ^ not covered
+   |     - not covered
 ...
 LL |     C
-   |     ^ not covered
+   |     - not covered
    = note: the matched value is of type `&E`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
    |
@@ -72,7 +72,7 @@ LL +         &E::B | &E::C => todo!()
    |
 
 error[E0005]: refutable pattern in local binding
-  --> $DIR/non-exhaustive-defined-here.rs:57:9
+  --> $DIR/non-exhaustive-defined-here.rs:59:9
    |
 LL |     let E::A = e;
    |         ^^^^ patterns `&E::B` and `&E::C` not covered
@@ -80,7 +80,7 @@ LL |     let E::A = e;
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
 note: `E` defined here
-  --> $DIR/non-exhaustive-defined-here.rs:6:6
+  --> $DIR/non-exhaustive-defined-here.rs:8:6
    |
 LL | enum E {
    |      ^
@@ -97,22 +97,22 @@ LL |     if let E::A = e { todo!() };
    |     ++              +++++++++++
 
 error[E0004]: non-exhaustive patterns: `&&mut &E::B` and `&&mut &E::C` not covered
-  --> $DIR/non-exhaustive-defined-here.rs:66:11
+  --> $DIR/non-exhaustive-defined-here.rs:68:11
    |
 LL |     match e {
    |           ^ patterns `&&mut &E::B` and `&&mut &E::C` not covered
    |
 note: `E` defined here
-  --> $DIR/non-exhaustive-defined-here.rs:14:5
+  --> $DIR/non-exhaustive-defined-here.rs:8:6
    |
 LL | enum E {
-   |      -
+   |      ^
 ...
 LL |     B,
-   |     ^ not covered
+   |     - not covered
 ...
 LL |     C
-   |     ^ not covered
+   |     - not covered
    = note: the matched value is of type `&&mut &E`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
    |
@@ -121,7 +121,7 @@ LL +         &&mut &E::B | &&mut &E::C => todo!()
    |
 
 error[E0005]: refutable pattern in local binding
-  --> $DIR/non-exhaustive-defined-here.rs:73:9
+  --> $DIR/non-exhaustive-defined-here.rs:75:9
    |
 LL |     let E::A = e;
    |         ^^^^ patterns `&&mut &E::B` and `&&mut &E::C` not covered
@@ -129,7 +129,7 @@ LL |     let E::A = e;
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
 note: `E` defined here
-  --> $DIR/non-exhaustive-defined-here.rs:6:6
+  --> $DIR/non-exhaustive-defined-here.rs:8:6
    |
 LL | enum E {
    |      ^
@@ -146,19 +146,19 @@ LL |     if let E::A = e { todo!() };
    |     ++              +++++++++++
 
 error[E0004]: non-exhaustive patterns: `Opt::None` not covered
-  --> $DIR/non-exhaustive-defined-here.rs:92:11
+  --> $DIR/non-exhaustive-defined-here.rs:94:11
    |
 LL |     match e {
    |           ^ pattern `Opt::None` not covered
    |
 note: `Opt` defined here
-  --> $DIR/non-exhaustive-defined-here.rs:85:5
+  --> $DIR/non-exhaustive-defined-here.rs:83:6
    |
 LL | enum Opt {
-   |      ---
+   |      ^^^
 ...
 LL |     None,
-   |     ^^^^ not covered
+   |     ---- not covered
    = note: the matched value is of type `Opt`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
@@ -167,7 +167,7 @@ LL +         Opt::None => todo!()
    |
 
 error[E0005]: refutable pattern in local binding
-  --> $DIR/non-exhaustive-defined-here.rs:99:9
+  --> $DIR/non-exhaustive-defined-here.rs:101:9
    |
 LL |     let Opt::Some(ref _x) = e;
    |         ^^^^^^^^^^^^^^^^^ pattern `Opt::None` not covered
@@ -175,7 +175,7 @@ LL |     let Opt::Some(ref _x) = e;
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
 note: `Opt` defined here
-  --> $DIR/non-exhaustive-defined-here.rs:81:6
+  --> $DIR/non-exhaustive-defined-here.rs:83:6
    |
 LL | enum Opt {
    |      ^^^
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr b/tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr
index 98e417a17f8..310049fe13e 100644
--- a/tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr
+++ b/tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr
@@ -18,10 +18,10 @@ LL |     match x {
    |           ^ pattern `T::A(U::C)` not covered
    |
 note: `T` defined here
-  --> $DIR/non-exhaustive-match-nested.rs:1:10
+  --> $DIR/non-exhaustive-match-nested.rs:1:6
    |
 LL | enum T { A(U), B }
-   |      -   ^ not covered
+   |      ^   - not covered
    = note: the matched value is of type `T`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-match.stderr b/tests/ui/pattern/usefulness/non-exhaustive-match.stderr
index e59e8885e1a..4bebd3cbbef 100644
--- a/tests/ui/pattern/usefulness/non-exhaustive-match.stderr
+++ b/tests/ui/pattern/usefulness/non-exhaustive-match.stderr
@@ -5,10 +5,10 @@ LL |     match x { T::B => { } }
    |           ^ pattern `T::A` not covered
    |
 note: `T` defined here
-  --> $DIR/non-exhaustive-match.rs:3:10
+  --> $DIR/non-exhaustive-match.rs:3:6
    |
 LL | enum T { A, B }
-   |      -   ^ not covered
+   |      ^   - not covered
    = note: the matched value is of type `T`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
@@ -79,10 +79,10 @@ LL |     match T::A {
    |           ^^^^ pattern `T::B` not covered
    |
 note: `T` defined here
-  --> $DIR/non-exhaustive-match.rs:3:13
+  --> $DIR/non-exhaustive-match.rs:3:6
    |
 LL | enum T { A, B }
-   |      -      ^ not covered
+   |      ^      - not covered
    = note: the matched value is of type `T`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr
index f914b98d923..cceb1d8f65d 100644
--- a/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr
+++ b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr
@@ -23,12 +23,12 @@ LL |     match Color::Red {
    |           ^^^^^^^^^^ pattern `Color::Red` not covered
    |
 note: `Color` defined here
-  --> $DIR/non-exhaustive-pattern-witness.rs:17:5
+  --> $DIR/non-exhaustive-pattern-witness.rs:16:6
    |
 LL | enum Color {
-   |      -----
+   |      ^^^^^
 LL |     Red,
-   |     ^^^ not covered
+   |     --- not covered
    = note: the matched value is of type `Color`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
@@ -43,17 +43,17 @@ LL |     match Direction::North {
    |           ^^^^^^^^^^^^^^^^ patterns `Direction::East`, `Direction::South` and `Direction::West` not covered
    |
 note: `Direction` defined here
-  --> $DIR/non-exhaustive-pattern-witness.rs:32:5
+  --> $DIR/non-exhaustive-pattern-witness.rs:30:6
    |
 LL | enum Direction {
-   |      ---------
+   |      ^^^^^^^^^
 LL |     North,
 LL |     East,
-   |     ^^^^ not covered
+   |     ---- not covered
 LL |     South,
-   |     ^^^^^ not covered
+   |     ----- not covered
 LL |     West,
-   |     ^^^^ not covered
+   |     ---- not covered
    = note: the matched value is of type `Direction`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
    |
@@ -72,6 +72,17 @@ note: `ExcessiveEnum` defined here
    |
 LL | enum ExcessiveEnum {
    |      ^^^^^^^^^^^^^
+LL |     First,
+LL |     Second,
+   |     ------ not covered
+LL |     Third,
+   |     ----- not covered
+LL |     Fourth,
+   |     ------ not covered
+LL |     Fifth,
+   |     ----- not covered
+LL |     Sixth,
+   |     ----- not covered
    = note: the matched value is of type `ExcessiveEnum`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
    |
@@ -86,13 +97,13 @@ LL |     match Color::Red {
    |           ^^^^^^^^^^ pattern `Color::CustomRGBA { a: true, .. }` not covered
    |
 note: `Color` defined here
-  --> $DIR/non-exhaustive-pattern-witness.rs:19:5
+  --> $DIR/non-exhaustive-pattern-witness.rs:16:6
    |
 LL | enum Color {
-   |      -----
+   |      ^^^^^
 ...
 LL |     CustomRGBA { a: bool, r: u8, g: u8, b: u8 },
-   |     ^^^^^^^^^^ not covered
+   |     ---------- not covered
    = note: the matched value is of type `Color`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
diff --git a/tests/ui/pattern/usefulness/stable-gated-patterns.stderr b/tests/ui/pattern/usefulness/stable-gated-patterns.stderr
index f944c25a905..f75517fb791 100644
--- a/tests/ui/pattern/usefulness/stable-gated-patterns.stderr
+++ b/tests/ui/pattern/usefulness/stable-gated-patterns.stderr
@@ -5,13 +5,13 @@ LL |     match UnstableEnum::Stable {
    |           ^^^^^^^^^^^^^^^^^^^^ patterns `UnstableEnum::Stable2` and `_` not covered
    |
 note: `UnstableEnum` defined here
-  --> $DIR/auxiliary/unstable.rs:9:5
+  --> $DIR/auxiliary/unstable.rs:5:1
    |
 LL | pub enum UnstableEnum {
-   | ---------------------
+   | ^^^^^^^^^^^^^^^^^^^^^
 ...
 LL |     Stable2,
-   |     ^^^^^^^ not covered
+   |     ------- not covered
    = note: the matched value is of type `UnstableEnum`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
    |
diff --git a/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr b/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr
index 22425aa0dd4..3d2b540a9f5 100644
--- a/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr
+++ b/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr
@@ -5,12 +5,12 @@ LL |     match x {
    |           ^ pattern `A::B { x: Some(_) }` not covered
    |
 note: `A` defined here
-  --> $DIR/struct-like-enum-nonexhaustive.rs:2:5
+  --> $DIR/struct-like-enum-nonexhaustive.rs:1:6
    |
 LL | enum A {
-   |      -
+   |      ^
 LL |     B { x: Option<isize> },
-   |     ^ not covered
+   |     - not covered
    = note: the matched value is of type `A`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
diff --git a/tests/ui/pattern/usefulness/unstable-gated-patterns.stderr b/tests/ui/pattern/usefulness/unstable-gated-patterns.stderr
index d776249b231..4a4945156f7 100644
--- a/tests/ui/pattern/usefulness/unstable-gated-patterns.stderr
+++ b/tests/ui/pattern/usefulness/unstable-gated-patterns.stderr
@@ -5,13 +5,13 @@ LL |     match UnstableEnum::Stable {
    |           ^^^^^^^^^^^^^^^^^^^^ pattern `UnstableEnum::Unstable` not covered
    |
 note: `UnstableEnum` defined here
-  --> $DIR/auxiliary/unstable.rs:11:5
+  --> $DIR/auxiliary/unstable.rs:5:1
    |
 LL | pub enum UnstableEnum {
-   | ---------------------
+   | ^^^^^^^^^^^^^^^^^^^^^
 ...
 LL |     Unstable,
-   |     ^^^^^^^^ not covered
+   |     -------- not covered
    = note: the matched value is of type `UnstableEnum`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
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 de1bf8be885..7386f10a6fb 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
@@ -17,18 +17,18 @@ LL |     match NonExhaustiveEnum::Unit {}
    |           ^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonExhaustiveEnum::Unit`, `NonExhaustiveEnum::Tuple(_)` and `NonExhaustiveEnum::Struct { .. }` not covered
    |
 note: `NonExhaustiveEnum` defined here
-  --> $DIR/enum_same_crate_empty_match.rs:5:5
+  --> $DIR/enum_same_crate_empty_match.rs:4:10
    |
 LL | pub enum NonExhaustiveEnum {
-   |          -----------------
+   |          ^^^^^^^^^^^^^^^^^
 LL |     Unit,
-   |     ^^^^ not covered
+   |     ---- not covered
 LL |
 LL |     Tuple(u32),
-   |     ^^^^^ not covered
+   |     ----- not covered
 LL |
 LL |     Struct { field: u32 }
-   |     ^^^^^^ not covered
+   |     ------ not covered
    = note: the matched value is of type `NonExhaustiveEnum`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
    |
@@ -44,18 +44,18 @@ LL |     match NormalEnum::Unit {}
    |           ^^^^^^^^^^^^^^^^ patterns `NormalEnum::Unit`, `NormalEnum::Tuple(_)` and `NormalEnum::Struct { .. }` not covered
    |
 note: `NormalEnum` defined here
-  --> $DIR/enum_same_crate_empty_match.rs:14:5
+  --> $DIR/enum_same_crate_empty_match.rs:13:10
    |
 LL | pub enum NormalEnum {
-   |          ----------
+   |          ^^^^^^^^^^
 LL |     Unit,
-   |     ^^^^ not covered
+   |     ---- not covered
 LL |
 LL |     Tuple(u32),
-   |     ^^^^^ not covered
+   |     ----- not covered
 LL |
 LL |     Struct { field: u32 }
-   |     ^^^^^^ not covered
+   |     ------ not covered
    = note: the matched value is of type `NormalEnum`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
    |
diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns-dont-lint-on-arm.lint.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns-dont-lint-on-arm.lint.stderr
new file mode 100644
index 00000000000..9192c66143c
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns-dont-lint-on-arm.lint.stderr
@@ -0,0 +1,75 @@
+error: some variants are not matched explicitly
+  --> $DIR/omitted-patterns-dont-lint-on-arm.rs:15:11
+   |
+LL |     match val {
+   |           ^^^ pattern `NonExhaustiveEnum::Struct { .. }` not covered
+   |
+   = help: ensure that all variants are matched explicitly by adding the suggested match arms
+   = note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
+note: the lint level is defined here
+  --> $DIR/omitted-patterns-dont-lint-on-arm.rs:14:12
+   |
+LL |     #[deny(non_exhaustive_omitted_patterns)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: some variants are not matched explicitly
+  --> $DIR/omitted-patterns-dont-lint-on-arm.rs:23:11
+   |
+LL |     match val {
+   |           ^^^ pattern `NonExhaustiveEnum::Struct { .. }` not covered
+   |
+   = help: ensure that all variants are matched explicitly by adding the suggested match arms
+   = note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
+note: the lint level is defined here
+  --> $DIR/omitted-patterns-dont-lint-on-arm.rs:22:27
+   |
+LL |     #[cfg_attr(lint, deny(non_exhaustive_omitted_patterns))]
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: the lint level must be set on the whole match
+  --> $DIR/omitted-patterns-dont-lint-on-arm.rs:34:9
+   |
+LL |         #[deny(non_exhaustive_omitted_patterns)]
+   |                ------------------------------- remove this attribute
+LL |         _ => {}
+   |         ^
+   |
+   = help: it no longer has any effect to set the lint level on an individual match arm
+help: set the lint level on the whole match
+   |
+LL +     #[deny(non_exhaustive_omitted_patterns)]
+LL |     match val {
+   |
+
+warning: the lint level must be set on the whole match
+  --> $DIR/omitted-patterns-dont-lint-on-arm.rs:42:9
+   |
+LL |         #[cfg_attr(lint, deny(non_exhaustive_omitted_patterns))]
+   |                               ------------------------------- remove this attribute
+LL |         _ => {}
+   |         ^
+   |
+   = help: it no longer has any effect to set the lint level on an individual match arm
+help: set the lint level on the whole match
+   |
+LL +     #[deny(non_exhaustive_omitted_patterns)]
+LL |     match val {
+   |
+
+warning: the lint level must be set on the whole match
+  --> $DIR/omitted-patterns-dont-lint-on-arm.rs:50:9
+   |
+LL |         #[cfg_attr(lint, warn(non_exhaustive_omitted_patterns))]
+   |                               ------------------------------- remove this attribute
+LL |         _ => {}
+   |         ^
+   |
+   = help: it no longer has any effect to set the lint level on an individual match arm
+help: set the lint level on the whole match
+   |
+LL +     #[warn(non_exhaustive_omitted_patterns)]
+LL |     match val {
+   |
+
+error: aborting due to 2 previous errors; 3 warnings emitted
+
diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns-dont-lint-on-arm.normal.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns-dont-lint-on-arm.normal.stderr
new file mode 100644
index 00000000000..46093eb9fb3
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns-dont-lint-on-arm.normal.stderr
@@ -0,0 +1,31 @@
+error: some variants are not matched explicitly
+  --> $DIR/omitted-patterns-dont-lint-on-arm.rs:15:11
+   |
+LL |     match val {
+   |           ^^^ pattern `NonExhaustiveEnum::Struct { .. }` not covered
+   |
+   = help: ensure that all variants are matched explicitly by adding the suggested match arms
+   = note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
+note: the lint level is defined here
+  --> $DIR/omitted-patterns-dont-lint-on-arm.rs:14:12
+   |
+LL |     #[deny(non_exhaustive_omitted_patterns)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: the lint level must be set on the whole match
+  --> $DIR/omitted-patterns-dont-lint-on-arm.rs:34:9
+   |
+LL |         #[deny(non_exhaustive_omitted_patterns)]
+   |                ------------------------------- remove this attribute
+LL |         _ => {}
+   |         ^
+   |
+   = help: it no longer has any effect to set the lint level on an individual match arm
+help: set the lint level on the whole match
+   |
+LL +     #[deny(non_exhaustive_omitted_patterns)]
+LL |     match val {
+   |
+
+error: aborting due to previous error; 1 warning emitted
+
diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns-dont-lint-on-arm.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns-dont-lint-on-arm.rs
new file mode 100644
index 00000000000..33f9f56a5bb
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns-dont-lint-on-arm.rs
@@ -0,0 +1,53 @@
+// revisions: normal lint
+// Test that putting the lint level on a match arm emits a warning, as this was previously
+// meaningful and is no longer.
+#![feature(non_exhaustive_omitted_patterns_lint)]
+
+// aux-build:enums.rs
+extern crate enums;
+
+use enums::NonExhaustiveEnum;
+
+fn main() {
+    let val = NonExhaustiveEnum::Unit;
+
+    #[deny(non_exhaustive_omitted_patterns)]
+    match val {
+        //~^ ERROR some variants are not matched explicitly
+        NonExhaustiveEnum::Unit => {}
+        NonExhaustiveEnum::Tuple(_) => {}
+        _ => {}
+    }
+
+    #[cfg_attr(lint, deny(non_exhaustive_omitted_patterns))]
+    match val {
+        //[lint]~^ ERROR some variants are not matched explicitly
+        NonExhaustiveEnum::Unit => {}
+        NonExhaustiveEnum::Tuple(_) => {}
+        _ => {}
+    }
+
+    match val {
+        NonExhaustiveEnum::Unit => {}
+        NonExhaustiveEnum::Tuple(_) => {}
+        #[deny(non_exhaustive_omitted_patterns)]
+        _ => {}
+    }
+    //~^^ WARN lint level must be set on the whole match
+
+    match val {
+        NonExhaustiveEnum::Unit => {}
+        NonExhaustiveEnum::Tuple(_) => {}
+        #[cfg_attr(lint, deny(non_exhaustive_omitted_patterns))]
+        _ => {}
+    }
+    //[lint]~^^ WARN lint level must be set on the whole match
+
+    match val {
+        NonExhaustiveEnum::Unit => {}
+        NonExhaustiveEnum::Tuple(_) => {}
+        #[cfg_attr(lint, warn(non_exhaustive_omitted_patterns))]
+        _ => {}
+    }
+    //[lint]~^^ WARN lint level must be set on the whole match
+}
diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.stderr
index a9c54af0418..c125756a646 100644
--- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.stderr
+++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.stderr
@@ -62,14 +62,14 @@ LL |     match x {}
    |           ^ patterns `UninhabitedVariants::Tuple(_)` and `UninhabitedVariants::Struct { .. }` not covered
    |
 note: `UninhabitedVariants` defined here
-  --> $DIR/auxiliary/uninhabited.rs:17:23
+  --> $DIR/auxiliary/uninhabited.rs:16:1
    |
 LL | pub enum UninhabitedVariants {
-   | ----------------------------
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 LL |     #[non_exhaustive] Tuple(!),
-   |                       ^^^^^ not covered
+   |                       ----- not covered
 LL |     #[non_exhaustive] Struct { x: ! }
-   |                       ^^^^^^ not covered
+   |                       ------ not covered
    = note: the matched value is of type `UninhabitedVariants`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
    |
diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr
index ec2a2f6f055..7a12aca8520 100644
--- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr
+++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr
@@ -43,14 +43,14 @@ LL |     match x {}
    |           ^ patterns `UninhabitedVariants::Tuple(_)` and `UninhabitedVariants::Struct { .. }` not covered
    |
 note: `UninhabitedVariants` defined here
-  --> $DIR/match_same_crate.rs:16:23
+  --> $DIR/match_same_crate.rs:15:10
    |
 LL | pub enum UninhabitedVariants {
-   |          -------------------
+   |          ^^^^^^^^^^^^^^^^^^^
 LL |     #[non_exhaustive] Tuple(!),
-   |                       ^^^^^ not covered
+   |                       ----- not covered
 LL |     #[non_exhaustive] Struct { x: ! }
-   |                       ^^^^^^ not covered
+   |                       ------ not covered
    = note: the matched value is of type `UninhabitedVariants`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
    |
diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr
index b6b777ec56c..19e2546b0da 100644
--- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr
+++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr
@@ -62,14 +62,14 @@ LL |     match x {}
    |           ^ patterns `UninhabitedVariants::Tuple(_)` and `UninhabitedVariants::Struct { .. }` not covered
    |
 note: `UninhabitedVariants` defined here
-  --> $DIR/auxiliary/uninhabited.rs:17:23
+  --> $DIR/auxiliary/uninhabited.rs:16:1
    |
 LL | pub enum UninhabitedVariants {
-   | ----------------------------
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 LL |     #[non_exhaustive] Tuple(!),
-   |                       ^^^^^ not covered
+   |                       ----- not covered
 LL |     #[non_exhaustive] Struct { x: ! }
-   |                       ^^^^^^ not covered
+   |                       ------ not covered
    = note: the matched value is of type `UninhabitedVariants`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
    |