about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2025-02-07 18:26:27 +0100
committerGitHub <noreply@github.com>2025-02-07 18:26:27 +0100
commit20f9e973d9ecf3cbd76431789ef7a3bc4f7367a0 (patch)
tree2bad81c62944a75df78f902f4a6995e02f9d2cba
parent26b288760e35aeff842c2c871bb3bd19d070c808 (diff)
parentf1e4d94fa4bd253c26610e8d79d5da8b52bad99f (diff)
downloadrust-20f9e973d9ecf3cbd76431789ef7a3bc4f7367a0.tar.gz
rust-20f9e973d9ecf3cbd76431789ef7a3bc4f7367a0.zip
Rollup merge of #136577 - dianne:simple-pat-migration-simplification, r=Nadrieril
Pattern Migration 2024: try to suggest eliding redundant binding modifiers

This is based on #136475. Only the last commit is new.

This is a simpler, more restrictive alternative to #136496, meant to partially address #136047. If a pattern can be migrated to Rust 2024 solely by removing redundant binding modifiers, this will make that suggestion; otherwise, it uses the old suggestion of making the pattern fully explicit.

Relevant tracking issue: #131414

``@rustbot`` label A-diagnostics A-patterns A-edition-2024

r? ``@Nadrieril``
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs78
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs24
-rw-r--r--compiler/rustc_mir_build/messages.ftl11
-rw-r--r--compiler/rustc_mir_build/src/errors.rs60
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs146
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr48
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs33
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr6
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr121
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed132
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs130
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr423
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs14
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr77
15 files changed, 1043 insertions, 262 deletions
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 2e7415cec5d..7c2a2b3fdf7 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -812,7 +812,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Determine the binding mode...
         let bm = match user_bind_annot {
-            BindingMode(ByRef::No, Mutability::Mut) if matches!(def_br, ByRef::Yes(_)) => {
+            BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(def_br_mutbl) = def_br => {
                 // Only mention the experimental `mut_ref` feature if if we're in edition 2024 and
                 // using other experimental matching features compatible with it.
                 if pat.span.at_least_rust_2024()
@@ -834,22 +834,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // `mut` resets the binding mode on edition <= 2021
                     self.add_rust_2024_migration_desugared_pat(
                         pat_info.top_info.hir_id,
-                        pat.span,
+                        pat,
                         ident.span,
-                        "requires binding by-value, but the implicit default is by-reference",
+                        def_br_mutbl,
                     );
                     BindingMode(ByRef::No, Mutability::Mut)
                 }
             }
             BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
             BindingMode(ByRef::Yes(_), _) => {
-                if matches!(def_br, ByRef::Yes(_)) {
+                if let ByRef::Yes(def_br_mutbl) = def_br {
                     // `ref`/`ref mut` overrides the binding mode on edition <= 2021
                     self.add_rust_2024_migration_desugared_pat(
                         pat_info.top_info.hir_id,
-                        pat.span,
+                        pat,
                         ident.span,
-                        "cannot override to bind by-reference when that is the implicit default",
+                        def_br_mutbl,
                     );
                 }
                 user_bind_annot
@@ -2386,9 +2386,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     pat_info.binding_mode = ByRef::No;
                     self.add_rust_2024_migration_desugared_pat(
                         pat_info.top_info.hir_id,
-                        pat.span,
+                        pat,
                         inner.span,
-                        "cannot implicitly match against multiple layers of reference",
+                        inh_mut,
                     )
                 }
             }
@@ -2778,33 +2778,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn add_rust_2024_migration_desugared_pat(
         &self,
         pat_id: HirId,
-        subpat_span: Span,
+        subpat: &'tcx Pat<'tcx>,
         cutoff_span: Span,
-        detailed_label: &str,
+        def_br_mutbl: Mutability,
     ) {
         // Try to trim the span we're labeling to just the `&` or binding mode that's an issue.
         // If the subpattern's span is is from an expansion, the emitted label will not be trimmed.
         let source_map = self.tcx.sess.source_map();
         let cutoff_span = source_map
-            .span_extend_prev_while(cutoff_span, char::is_whitespace)
+            .span_extend_prev_while(cutoff_span, |c| c.is_whitespace() || c == '(')
             .unwrap_or(cutoff_span);
-        // Ensure we use the syntax context and thus edition of `subpat_span`; this will be a hard
+        // Ensure we use the syntax context and thus edition of `subpat.span`; this will be a hard
         // error if the subpattern is of edition >= 2024.
-        let trimmed_span = subpat_span.until(cutoff_span).with_ctxt(subpat_span.ctxt());
+        let trimmed_span = subpat.span.until(cutoff_span).with_ctxt(subpat.span.ctxt());
+
+        let mut typeck_results = self.typeck_results.borrow_mut();
+        let mut table = typeck_results.rust_2024_migration_desugared_pats_mut();
+        // FIXME(ref_pat_eat_one_layer_2024): The migration diagnostic doesn't know how to track the
+        // default binding mode in the presence of Rule 3 or Rule 5. As a consequence, the labels it
+        // gives for default binding modes are wrong, as well as suggestions based on the default
+        // binding mode. This keeps it from making those suggestions, as doing so could panic.
+        let info = table.entry(pat_id).or_insert_with(|| ty::Rust2024IncompatiblePatInfo {
+            primary_labels: Vec::new(),
+            bad_modifiers: false,
+            bad_ref_pats: false,
+            suggest_eliding_modes: !self.tcx.features().ref_pat_eat_one_layer_2024()
+                && !self.tcx.features().ref_pat_eat_one_layer_2024_structural(),
+        });
 
         // Only provide a detailed label if the problematic subpattern isn't from an expansion.
         // In the case that it's from a macro, we'll add a more detailed note in the emitter.
-        let desc = if subpat_span.from_expansion() {
-            "default binding mode is reset within expansion"
+        let from_expansion = subpat.span.from_expansion();
+        let primary_label = if from_expansion {
+            // NB: This wording assumes the only expansions that can produce problematic reference
+            // patterns and bindings are macros. If a desugaring or AST pass is added that can do
+            // so, we may want to inspect the span's source callee or macro backtrace.
+            "occurs within macro expansion".to_owned()
         } else {
-            detailed_label
+            let pat_kind = if let PatKind::Binding(user_bind_annot, _, _, _) = subpat.kind {
+                info.bad_modifiers |= true;
+                // If the user-provided binding modifier doesn't match the default binding mode, we'll
+                // need to suggest reference patterns, which can affect other bindings.
+                // For simplicity, we opt to suggest making the pattern fully explicit.
+                info.suggest_eliding_modes &=
+                    user_bind_annot == BindingMode(ByRef::Yes(def_br_mutbl), Mutability::Not);
+                "binding modifier"
+            } else {
+                info.bad_ref_pats |= true;
+                // For simplicity, we don't try to suggest eliding reference patterns. Thus, we'll
+                // suggest adding them instead, which can affect the types assigned to bindings.
+                // As such, we opt to suggest making the pattern fully explicit.
+                info.suggest_eliding_modes = false;
+                "reference pattern"
+            };
+            let dbm_str = match def_br_mutbl {
+                Mutability::Not => "ref",
+                Mutability::Mut => "ref mut",
+            };
+            format!("{pat_kind} not allowed under `{dbm_str}` default binding mode")
         };
-
-        self.typeck_results
-            .borrow_mut()
-            .rust_2024_migration_desugared_pats_mut()
-            .entry(pat_id)
-            .or_default()
-            .push((trimmed_span, desc.to_owned()));
+        info.primary_labels.push((trimmed_span, primary_label));
     }
 }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 88eea6101b5..6fe1502c66d 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -94,7 +94,7 @@ pub use self::sty::{
 pub use self::trait_def::TraitDef;
 pub use self::typeck_results::{
     CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, IsIdentity,
-    TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind,
+    Rust2024IncompatiblePatInfo, TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind,
 };
 pub use self::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
 use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason};
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index 3c6ed997845..49bdb5e9dc3 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -73,9 +73,9 @@ pub struct TypeckResults<'tcx> {
     /// Stores the actual binding mode for all instances of [`BindingMode`].
     pat_binding_modes: ItemLocalMap<BindingMode>,
 
-    /// Top-level patterns whose match ergonomics need to be desugared by the Rust 2021 -> 2024
-    /// migration lint. Problematic subpatterns are stored in the `Vec` for the lint to highlight.
-    rust_2024_migration_desugared_pats: ItemLocalMap<Vec<(Span, String)>>,
+    /// Top-level patterns incompatible with Rust 2024's match ergonomics. These will be translated
+    /// to a form valid in all Editions, either as a lint diagnostic or hard error.
+    rust_2024_migration_desugared_pats: ItemLocalMap<Rust2024IncompatiblePatInfo>,
 
     /// Stores the types which were implicitly dereferenced in pattern binding modes
     /// for later usage in THIR lowering. For example,
@@ -420,7 +420,7 @@ impl<'tcx> TypeckResults<'tcx> {
 
     pub fn rust_2024_migration_desugared_pats(
         &self,
-    ) -> LocalTableInContext<'_, Vec<(Span, String)>> {
+    ) -> LocalTableInContext<'_, Rust2024IncompatiblePatInfo> {
         LocalTableInContext {
             hir_owner: self.hir_owner,
             data: &self.rust_2024_migration_desugared_pats,
@@ -429,7 +429,7 @@ impl<'tcx> TypeckResults<'tcx> {
 
     pub fn rust_2024_migration_desugared_pats_mut(
         &mut self,
-    ) -> LocalTableInContextMut<'_, Vec<(Span, String)>> {
+    ) -> LocalTableInContextMut<'_, Rust2024IncompatiblePatInfo> {
         LocalTableInContextMut {
             hir_owner: self.hir_owner,
             data: &mut self.rust_2024_migration_desugared_pats,
@@ -811,3 +811,17 @@ impl<'tcx> std::fmt::Display for UserTypeKind<'tcx> {
         }
     }
 }
+
+/// Information on a pattern incompatible with Rust 2024, for use by the error/migration diagnostic
+/// emitted during THIR construction.
+#[derive(TyEncodable, TyDecodable, Debug, HashStable)]
+pub struct Rust2024IncompatiblePatInfo {
+    /// Labeled spans for `&`s, `&mut`s, and binding modifiers incompatible with Rust 2024.
+    pub primary_labels: Vec<(Span, String)>,
+    /// Whether any binding modifiers occur under a non-`move` default binding mode.
+    pub bad_modifiers: bool,
+    /// Whether any `&` or `&mut` patterns occur under a non-`move` default binding mode.
+    pub bad_ref_pats: bool,
+    /// If `true`, we can give a simpler suggestion solely by eliding explicit binding modifiers.
+    pub suggest_eliding_modes: bool,
+}
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index 3cf3c332893..fae159103e7 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -285,7 +285,16 @@ mir_build_pointer_pattern = function pointers and raw pointers not derived from
 
 mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
 
-mir_build_rust_2024_incompatible_pat = this pattern relies on behavior which may change in edition 2024
+mir_build_rust_2024_incompatible_pat = {$bad_modifiers ->
+        *[true] binding modifiers{$bad_ref_pats ->
+            *[true] {" "}and reference patterns
+            [false] {""}
+        }
+        [false] reference patterns
+    } may only be written when the default binding mode is `move`{$is_hard_error ->
+        *[true] {""}
+        [false] {" "}in Rust 2024
+    }
 
 mir_build_static_in_pattern = statics cannot be referenced in patterns
     .label = can't be used in patterns
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index af6f33d9cdd..07bdc59756a 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -1,3 +1,4 @@
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::codes::*;
 use rustc_errors::{
     Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
@@ -1097,41 +1098,70 @@ pub(crate) enum MiscPatternSuggestion {
 
 #[derive(LintDiagnostic)]
 #[diag(mir_build_rust_2024_incompatible_pat)]
-pub(crate) struct Rust2024IncompatiblePat<'a> {
+pub(crate) struct Rust2024IncompatiblePat {
     #[subdiagnostic]
-    pub(crate) sugg: Rust2024IncompatiblePatSugg<'a>,
+    pub(crate) sugg: Rust2024IncompatiblePatSugg,
+    pub(crate) bad_modifiers: bool,
+    pub(crate) bad_ref_pats: bool,
+    pub(crate) is_hard_error: bool,
 }
 
-pub(crate) struct Rust2024IncompatiblePatSugg<'a> {
+pub(crate) struct Rust2024IncompatiblePatSugg {
+    /// If true, our suggestion is to elide explicit binding modifiers.
+    /// If false, our suggestion is to make the pattern fully explicit.
+    pub(crate) suggest_eliding_modes: bool,
     pub(crate) suggestion: Vec<(Span, String)>,
     pub(crate) ref_pattern_count: usize,
     pub(crate) binding_mode_count: usize,
-    /// Labeled spans for subpatterns invalid in Rust 2024.
-    pub(crate) labels: &'a [(Span, String)],
+    /// Internal state: the ref-mutability of the default binding mode at the subpattern being
+    /// lowered, with the span where it was introduced. `None` for a by-value default mode.
+    pub(crate) default_mode_span: Option<(Span, ty::Mutability)>,
+    /// Labels for where incompatibility-causing by-ref default binding modes were introduced.
+    pub(crate) default_mode_labels: FxIndexMap<Span, ty::Mutability>,
 }
 
-impl<'a> Subdiagnostic for Rust2024IncompatiblePatSugg<'a> {
+impl Subdiagnostic for Rust2024IncompatiblePatSugg {
     fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
         self,
         diag: &mut Diag<'_, G>,
         _f: &F,
     ) {
+        // Format and emit explanatory notes about default binding modes. Reversing the spans' order
+        // means if we have nested spans, the innermost ones will be visited first.
+        for (span, def_br_mutbl) in self.default_mode_labels.into_iter().rev() {
+            // Don't point to a macro call site.
+            if !span.from_expansion() {
+                let note_msg = "matching on a reference type with a non-reference pattern changes the default binding mode";
+                let label_msg =
+                    format!("this matches on type `{}_`", def_br_mutbl.ref_prefix_str());
+                let mut label = MultiSpan::from(span);
+                label.push_span_label(span, label_msg);
+                diag.span_note(label, note_msg);
+            }
+        }
+
+        // Format and emit the suggestion.
         let applicability =
             if self.suggestion.iter().all(|(span, _)| span.can_be_used_for_suggestions()) {
                 Applicability::MachineApplicable
             } else {
                 Applicability::MaybeIncorrect
             };
-        let plural_derefs = pluralize!(self.ref_pattern_count);
-        let and_modes = if self.binding_mode_count > 0 {
-            format!(" and variable binding mode{}", pluralize!(self.binding_mode_count))
+        let msg = if self.suggest_eliding_modes {
+            let plural_modes = pluralize!(self.binding_mode_count);
+            format!("remove the unnecessary binding modifier{plural_modes}")
         } else {
-            String::new()
+            let plural_derefs = pluralize!(self.ref_pattern_count);
+            let and_modes = if self.binding_mode_count > 0 {
+                format!(" and variable binding mode{}", pluralize!(self.binding_mode_count))
+            } else {
+                String::new()
+            };
+            format!("make the implied reference pattern{plural_derefs}{and_modes} explicit")
         };
-        diag.multipart_suggestion_verbose(
-            format!("make the implied reference pattern{plural_derefs}{and_modes} explicit"),
-            self.suggestion,
-            applicability,
-        );
+        // FIXME(dianne): for peace of mind, don't risk emitting a 0-part suggestion (that panics!)
+        if !self.suggestion.is_empty() {
+            diag.multipart_suggestion_verbose(msg, self.suggestion, applicability);
+        }
     }
 }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index c862d012f4e..83fef7b0de6 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -35,7 +35,7 @@ struct PatCtxt<'a, 'tcx> {
     typeck_results: &'a ty::TypeckResults<'tcx>,
 
     /// Used by the Rust 2024 migration lint.
-    rust_2024_migration_suggestion: Option<Rust2024IncompatiblePatSugg<'a>>,
+    rust_2024_migration_suggestion: Option<Rust2024IncompatiblePatSugg>,
 }
 
 pub(super) fn pat_from_hir<'a, 'tcx>(
@@ -44,25 +44,30 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
     typeck_results: &'a ty::TypeckResults<'tcx>,
     pat: &'tcx hir::Pat<'tcx>,
 ) -> Box<Pat<'tcx>> {
+    let migration_info = typeck_results.rust_2024_migration_desugared_pats().get(pat.hir_id);
     let mut pcx = PatCtxt {
         tcx,
         typing_env,
         typeck_results,
-        rust_2024_migration_suggestion: typeck_results
-            .rust_2024_migration_desugared_pats()
-            .get(pat.hir_id)
-            .map(|labels| Rust2024IncompatiblePatSugg {
+        rust_2024_migration_suggestion: migration_info.and_then(|info| {
+            Some(Rust2024IncompatiblePatSugg {
+                suggest_eliding_modes: info.suggest_eliding_modes,
                 suggestion: Vec::new(),
                 ref_pattern_count: 0,
                 binding_mode_count: 0,
-                labels: labels.as_slice(),
-            }),
+                default_mode_span: None,
+                default_mode_labels: Default::default(),
+            })
+        }),
     };
     let result = pcx.lower_pattern(pat);
     debug!("pat_from_hir({:?}) = {:?}", pat, result);
-    if let Some(sugg) = pcx.rust_2024_migration_suggestion {
-        let mut spans = MultiSpan::from_spans(sugg.labels.iter().map(|(span, _)| *span).collect());
-        for (span, label) in sugg.labels {
+    if let Some(info) = migration_info
+        && let Some(sugg) = pcx.rust_2024_migration_suggestion
+    {
+        let mut spans =
+            MultiSpan::from_spans(info.primary_labels.iter().map(|(span, _)| *span).collect());
+        for (span, label) in &info.primary_labels {
             spans.push_span_label(*span, label.clone());
         }
         // If a relevant span is from at least edition 2024, this is a hard error.
@@ -70,10 +75,13 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
         if is_hard_error {
             let mut err =
                 tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);
-            if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
+            if let Some(lint_info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
                 // provide the same reference link as the lint
-                err.note(format!("for more information, see {}", info.reference));
+                err.note(format!("for more information, see {}", lint_info.reference));
             }
+            err.arg("bad_modifiers", info.bad_modifiers);
+            err.arg("bad_ref_pats", info.bad_ref_pats);
+            err.arg("is_hard_error", true);
             err.subdiagnostic(sugg);
             err.emit();
         } else {
@@ -81,7 +89,12 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
                 lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
                 pat.hir_id,
                 spans,
-                Rust2024IncompatiblePat { sugg },
+                Rust2024IncompatiblePat {
+                    sugg,
+                    bad_modifiers: info.bad_modifiers,
+                    bad_ref_pats: info.bad_ref_pats,
+                    is_hard_error,
+                },
             );
         }
     }
@@ -90,6 +103,35 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
 
 impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
     fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> {
+        let adjustments: &[Ty<'tcx>] =
+            self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
+
+        let mut opt_old_mode_span = None;
+        if let Some(s) = &mut self.rust_2024_migration_suggestion
+            && !adjustments.is_empty()
+        {
+            let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| {
+                let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
+                    span_bug!(pat.span, "pattern implicitly dereferences a non-ref type");
+                };
+                mutbl
+            });
+
+            if !s.suggest_eliding_modes {
+                let suggestion_str: String =
+                    implicit_deref_mutbls.clone().map(|mutbl| mutbl.ref_prefix_str()).collect();
+                s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
+                s.ref_pattern_count += adjustments.len();
+            }
+
+            // Remember if this changed the default binding mode, in case we want to label it.
+            let min_mutbl = implicit_deref_mutbls.min().unwrap();
+            if s.default_mode_span.is_none_or(|(_, old_mutbl)| min_mutbl < old_mutbl) {
+                opt_old_mode_span = Some(s.default_mode_span);
+                s.default_mode_span = Some((pat.span, min_mutbl));
+            }
+        };
+
         // When implicit dereferences have been inserted in this pattern, the unadjusted lowered
         // pattern has the type that results *after* dereferencing. For example, in this code:
         //
@@ -118,8 +160,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             _ => self.lower_pattern_unadjusted(pat),
         };
 
-        let adjustments: &[Ty<'tcx>] =
-            self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
         let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, ref_ty| {
             debug!("{:?}: wrapping pattern with type {:?}", thir_pat, ref_ty);
             Box::new(Pat {
@@ -130,24 +170,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         });
 
         if let Some(s) = &mut self.rust_2024_migration_suggestion
-            && !adjustments.is_empty()
+            && let Some(old_mode_span) = opt_old_mode_span
         {
-            let suggestion_str: String = adjustments
-                .iter()
-                .map(|ref_ty| {
-                    let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
-                        span_bug!(pat.span, "pattern implicitly dereferences a non-ref type");
-                    };
-
-                    match mutbl {
-                        ty::Mutability::Not => "&",
-                        ty::Mutability::Mut => "&mut ",
-                    }
-                })
-                .collect();
-            s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
-            s.ref_pattern_count += adjustments.len();
-        };
+            s.default_mode_span = old_mode_span;
+        }
 
         adjusted_pat
     }
@@ -340,7 +366,22 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
                 let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
                 PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), mutability }
             }
-            hir::PatKind::Ref(subpattern, _) | hir::PatKind::Box(subpattern) => {
+            hir::PatKind::Ref(subpattern, _) => {
+                // Track the default binding mode for the Rust 2024 migration suggestion.
+                let old_mode_span = self.rust_2024_migration_suggestion.as_mut().and_then(|s| {
+                    if let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span {
+                        // If this eats a by-ref default binding mode, label the binding mode.
+                        s.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
+                    }
+                    s.default_mode_span.take()
+                });
+                let subpattern = self.lower_pattern(subpattern);
+                if let Some(s) = &mut self.rust_2024_migration_suggestion {
+                    s.default_mode_span = old_mode_span;
+                }
+                PatKind::Deref { subpattern }
+            }
+            hir::PatKind::Box(subpattern) => {
                 PatKind::Deref { subpattern: self.lower_pattern(subpattern) }
             }
 
@@ -367,19 +408,32 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
                     .get(pat.hir_id)
                     .expect("missing binding mode");
 
-                if let Some(s) = &mut self.rust_2024_migration_suggestion
-                    && explicit_ba.0 == ByRef::No
-                    && let ByRef::Yes(mutbl) = mode.0
-                {
-                    let sugg_str = match mutbl {
-                        Mutability::Not => "ref ",
-                        Mutability::Mut => "ref mut ",
-                    };
-                    s.suggestion.push((
-                        pat.span.with_lo(ident.span.lo()).shrink_to_lo(),
-                        sugg_str.to_owned(),
-                    ));
-                    s.binding_mode_count += 1;
+                if let Some(s) = &mut self.rust_2024_migration_suggestion {
+                    if explicit_ba != hir::BindingMode::NONE
+                        && let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span
+                    {
+                        // If this overrides a by-ref default binding mode, label the binding mode.
+                        s.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
+                        // If our suggestion is to elide redundnt modes, this will be one of them.
+                        if s.suggest_eliding_modes {
+                            s.suggestion.push((pat.span.with_hi(ident.span.lo()), String::new()));
+                            s.binding_mode_count += 1;
+                        }
+                    }
+                    if !s.suggest_eliding_modes
+                        && explicit_ba.0 == ByRef::No
+                        && let ByRef::Yes(mutbl) = mode.0
+                    {
+                        let sugg_str = match mutbl {
+                            Mutability::Not => "ref ",
+                            Mutability::Mut => "ref mut ",
+                        };
+                        s.suggestion.push((
+                            pat.span.with_lo(ident.span.lo()).shrink_to_lo(),
+                            sugg_str.to_owned(),
+                        ));
+                        s.binding_mode_count += 1;
+                    }
                 }
 
                 // A ref x pattern is the same node used for x, and as such it has
diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr
index 70cdcbd62eb..b7fb70dfd24 100644
--- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr
+++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:60:10
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:54:10
    |
 LL |     let [&mut ref x] = &[&mut 0];
    |          ^^^^^
@@ -10,55 +10,75 @@ help: replace this `&mut` pattern with `&`
 LL |     let [&ref x] = &[&mut 0];
    |          ~
 
-error: this pattern relies on behavior which may change in edition 2024
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:74:10
+error: binding modifiers may only be written when the default binding mode is `move`
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:67:10
    |
 LL |     let [ref mut x] = &[0];
-   |          ^^^^^^^ cannot override to bind by-reference when that is the implicit default
+   |          ^^^^^^^ binding modifier not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:67:9
+   |
+LL |     let [ref mut x] = &[0];
+   |         ^^^^^^^^^^^ this matches on type `&_`
 help: make the implied reference pattern explicit
    |
 LL |     let &[ref mut x] = &[0];
    |         +
 
 error[E0596]: cannot borrow data in a `&` reference as mutable
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:74:10
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:67:10
    |
 LL |     let [ref mut x] = &[0];
    |          ^^^^^^^^^ cannot borrow as mutable
 
-error: this pattern relies on behavior which may change in edition 2024
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:83:10
+error: binding modifiers may only be written when the default binding mode is `move`
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:75:10
    |
 LL |     let [ref x] = &[0];
-   |          ^^^ cannot override to bind by-reference when that is the implicit default
+   |          ^^^ binding modifier not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:75:9
+   |
+LL |     let [ref x] = &[0];
+   |         ^^^^^^^ this matches on type `&_`
 help: make the implied reference pattern explicit
    |
 LL |     let &[ref x] = &[0];
    |         +
 
-error: this pattern relies on behavior which may change in edition 2024
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:88:10
+error: binding modifiers may only be written when the default binding mode is `move`
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:79:10
    |
 LL |     let [ref x] = &mut [0];
-   |          ^^^ cannot override to bind by-reference when that is the implicit default
+   |          ^^^ binding modifier not allowed under `ref mut` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:79:9
+   |
+LL |     let [ref x] = &mut [0];
+   |         ^^^^^^^ this matches on type `&mut _`
 help: make the implied reference pattern explicit
    |
 LL |     let &mut [ref x] = &mut [0];
    |         ++++
 
-error: this pattern relies on behavior which may change in edition 2024
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:93:10
+error: binding modifiers may only be written when the default binding mode is `move`
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:83:10
    |
 LL |     let [ref mut x] = &mut [0];
-   |          ^^^^^^^ cannot override to bind by-reference when that is the implicit default
+   |          ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:83:9
+   |
+LL |     let [ref mut x] = &mut [0];
+   |         ^^^^^^^^^^^ this matches on type `&mut _`
 help: make the implied reference pattern explicit
    |
 LL |     let &mut [ref mut x] = &mut [0];
diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs
index 4c88c0c63ae..4e048570c33 100644
--- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs
+++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs
@@ -13,26 +13,22 @@
 /// The eat-outer variant eats the inherited reference, so binding with `ref` isn't a problem.
 fn errors_from_eating_the_real_reference() {
     let [&ref x] = &[&0];
-    //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024
-    //[structural2024]~| cannot override to bind by-reference when that is the implicit default
+    //[structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move`
     #[cfg(stable2021)] let _: &u32 = x;
     #[cfg(classic2024)] let _: &&u32 = x;
 
     let [&ref x] = &mut [&0];
-    //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024
-    //[structural2024]~| cannot override to bind by-reference when that is the implicit default
+    //[structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move`
     #[cfg(stable2021)] let _: &u32 = x;
     #[cfg(classic2024)] let _: &&u32 = x;
 
     let [&mut ref x] = &mut [&mut 0];
-    //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024
-    //[structural2024]~| cannot override to bind by-reference when that is the implicit default
+    //[structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move`
     #[cfg(stable2021)] let _: &u32 = x;
     #[cfg(classic2024)] let _: &&mut u32 = x;
 
     let [&mut ref mut x] = &mut [&mut 0];
-    //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024
-    //[structural2024]~| cannot override to bind by-reference when that is the implicit default
+    //[structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move`
     #[cfg(stable2021)] let _: &mut u32 = x;
     #[cfg(classic2024)] let _: &mut &mut u32 = x;
 }
@@ -43,15 +39,13 @@ fn errors_from_eating_the_real_reference_caught_in_hir_typeck_on_stable() {
     let [&ref x] = &[&mut 0];
     //[stable2021]~^ ERROR: mismatched types
     //[stable2021]~| types differ in mutability
-    //[structural2024]~^^^ ERROR: this pattern relies on behavior which may change in edition 2024
-    //[structural2024]~| cannot override to bind by-reference when that is the implicit default
+    //[structural2024]~^^^ ERROR: binding modifiers may only be written when the default binding mode is `move`
     #[cfg(classic2024)] let _: &&mut u32 = x;
 
     let [&ref x] = &mut [&mut 0];
     //[stable2021]~^ ERROR: mismatched types
     //[stable2021]~| types differ in mutability
-    //[structural2024]~^^^ ERROR: this pattern relies on behavior which may change in edition 2024
-    //[structural2024]~| cannot override to bind by-reference when that is the implicit default
+    //[structural2024]~^^^ ERROR: binding modifiers may only be written when the default binding mode is `move`
     #[cfg(classic2024)] let _: &&mut u32 = x;
 }
 
@@ -60,8 +54,7 @@ fn errors_dependent_on_eating_order_caught_in_hir_typeck_when_eating_outer() {
     let [&mut ref x] = &[&mut 0];
     //[classic2024]~^ ERROR: mismatched types
     //[classic2024]~| cannot match inherited `&` with `&mut` pattern
-    //[structural2024]~^^^ ERROR: this pattern relies on behavior which may change in edition 2024
-    //[structural2024]~| cannot override to bind by-reference when that is the implicit default
+    //[structural2024]~^^^ ERROR: binding modifiers may only be written when the default binding mode is `move`
     #[cfg(stable2021)] let _: &u32 = x;
 }
 
@@ -73,25 +66,21 @@ fn errors_dependent_on_eating_order_caught_in_hir_typeck_when_eating_outer() {
 fn borrowck_errors_in_old_editions() {
     let [ref mut x] = &[0];
     //~^ ERROR: cannot borrow data in a `&` reference as mutable
-    //[classic2024,structural2024]~| ERROR: this pattern relies on behavior which may change in edition 2024
-    //[classic2024,structural2024]~| cannot override to bind by-reference when that is the implicit default
+    //[classic2024,structural2024]~| ERROR: binding modifiers may only be written when the default binding mode is `move`
 }
 
 /// The remaining tests are purely for testing `ref` bindings in the presence of an inherited
 /// reference. These should always fail on edition 2024 and succeed on edition 2021.
 pub fn main() {
     let [ref x] = &[0];
-    //[classic2024,structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024
-    //[classic2024,structural2024]~| cannot override to bind by-reference when that is the implicit default
+    //[classic2024,structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move`
     #[cfg(stable2021)] let _: &u32 = x;
 
     let [ref x] = &mut [0];
-    //[classic2024,structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024
-    //[classic2024,structural2024]~| cannot override to bind by-reference when that is the implicit default
+    //[classic2024,structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move`
     #[cfg(stable2021)] let _: &u32 = x;
 
     let [ref mut x] = &mut [0];
-    //[classic2024,structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024
-    //[classic2024,structural2024]~| cannot override to bind by-reference when that is the implicit default
+    //[classic2024,structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move`
     #[cfg(stable2021)] let _: &mut u32 = x;
 }
diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr
index a21e4bb5b8f..26095d84605 100644
--- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr
+++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:43:10
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:39:10
    |
 LL |     let [&ref x] = &[&mut 0];
    |          ^^^^^^    --------- this expression has type `&[&mut {integer}; 1]`
@@ -15,7 +15,7 @@ LL +     let [ref x] = &[&mut 0];
    |
 
 error[E0308]: mismatched types
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:50:10
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:45:10
    |
 LL |     let [&ref x] = &mut [&mut 0];
    |          ^^^^^^    ------------- this expression has type `&mut [&mut {integer}; 1]`
@@ -31,7 +31,7 @@ LL +     let [ref x] = &mut [&mut 0];
    |
 
 error[E0596]: cannot borrow data in a `&` reference as mutable
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:74:10
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:67:10
    |
 LL |     let [ref mut x] = &[0];
    |          ^^^^^^^^^ cannot borrow as mutable
diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr
index ee2c831bfcc..31930e8c033 100644
--- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr
+++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr
@@ -1,136 +1,191 @@
-error: this pattern relies on behavior which may change in edition 2024
+error: binding modifiers may only be written when the default binding mode is `move`
   --> $DIR/ref-binding-on-inh-ref-errors.rs:15:11
    |
 LL |     let [&ref x] = &[&0];
-   |           ^^^ cannot override to bind by-reference when that is the implicit default
+   |           ^^^ binding modifier not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:15:9
+   |
+LL |     let [&ref x] = &[&0];
+   |         ^^^^^^^^ this matches on type `&_`
 help: make the implied reference pattern explicit
    |
 LL |     let &[&ref x] = &[&0];
    |         +
 
-error: this pattern relies on behavior which may change in edition 2024
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:21:11
+error: binding modifiers may only be written when the default binding mode is `move`
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:20:11
    |
 LL |     let [&ref x] = &mut [&0];
-   |           ^^^ cannot override to bind by-reference when that is the implicit default
+   |           ^^^ binding modifier not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:20:9
+   |
+LL |     let [&ref x] = &mut [&0];
+   |         ^^^^^^^^ this matches on type `&mut _`
 help: make the implied reference pattern explicit
    |
 LL |     let &mut [&ref x] = &mut [&0];
    |         ++++
 
-error: this pattern relies on behavior which may change in edition 2024
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:27:15
+error: binding modifiers may only be written when the default binding mode is `move`
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:25:15
    |
 LL |     let [&mut ref x] = &mut [&mut 0];
-   |               ^^^ cannot override to bind by-reference when that is the implicit default
+   |               ^^^ binding modifier not allowed under `ref mut` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:25:9
+   |
+LL |     let [&mut ref x] = &mut [&mut 0];
+   |         ^^^^^^^^^^^^ this matches on type `&mut _`
 help: make the implied reference pattern explicit
    |
 LL |     let &mut [&mut ref x] = &mut [&mut 0];
    |         ++++
 
-error: this pattern relies on behavior which may change in edition 2024
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:33:15
+error: binding modifiers may only be written when the default binding mode is `move`
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:30:15
    |
 LL |     let [&mut ref mut x] = &mut [&mut 0];
-   |               ^^^^^^^ cannot override to bind by-reference when that is the implicit default
+   |               ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:30:9
+   |
+LL |     let [&mut ref mut x] = &mut [&mut 0];
+   |         ^^^^^^^^^^^^^^^^ this matches on type `&mut _`
 help: make the implied reference pattern explicit
    |
 LL |     let &mut [&mut ref mut x] = &mut [&mut 0];
    |         ++++
 
-error: this pattern relies on behavior which may change in edition 2024
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:43:11
+error: binding modifiers may only be written when the default binding mode is `move`
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:39:11
    |
 LL |     let [&ref x] = &[&mut 0];
-   |           ^^^ cannot override to bind by-reference when that is the implicit default
+   |           ^^^ binding modifier not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:39:9
+   |
+LL |     let [&ref x] = &[&mut 0];
+   |         ^^^^^^^^ this matches on type `&_`
 help: make the implied reference pattern explicit
    |
 LL |     let &[&ref x] = &[&mut 0];
    |         +
 
-error: this pattern relies on behavior which may change in edition 2024
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:50:11
+error: binding modifiers may only be written when the default binding mode is `move`
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:45:11
    |
 LL |     let [&ref x] = &mut [&mut 0];
-   |           ^^^ cannot override to bind by-reference when that is the implicit default
+   |           ^^^ binding modifier not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:45:9
+   |
+LL |     let [&ref x] = &mut [&mut 0];
+   |         ^^^^^^^^ this matches on type `&mut _`
 help: make the implied reference pattern explicit
    |
 LL |     let &mut [&ref x] = &mut [&mut 0];
    |         ++++
 
-error: this pattern relies on behavior which may change in edition 2024
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:60:15
+error: binding modifiers may only be written when the default binding mode is `move`
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:54:15
    |
 LL |     let [&mut ref x] = &[&mut 0];
-   |               ^^^ cannot override to bind by-reference when that is the implicit default
+   |               ^^^ binding modifier not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:54:9
+   |
+LL |     let [&mut ref x] = &[&mut 0];
+   |         ^^^^^^^^^^^^ this matches on type `&_`
 help: make the implied reference pattern explicit
    |
 LL |     let &[&mut ref x] = &[&mut 0];
    |         +
 
-error: this pattern relies on behavior which may change in edition 2024
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:74:10
+error: binding modifiers may only be written when the default binding mode is `move`
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:67:10
    |
 LL |     let [ref mut x] = &[0];
-   |          ^^^^^^^ cannot override to bind by-reference when that is the implicit default
+   |          ^^^^^^^ binding modifier not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:67:9
+   |
+LL |     let [ref mut x] = &[0];
+   |         ^^^^^^^^^^^ this matches on type `&_`
 help: make the implied reference pattern explicit
    |
 LL |     let &[ref mut x] = &[0];
    |         +
 
 error[E0596]: cannot borrow data in a `&` reference as mutable
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:74:10
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:67:10
    |
 LL |     let [ref mut x] = &[0];
    |          ^^^^^^^^^ cannot borrow as mutable
 
-error: this pattern relies on behavior which may change in edition 2024
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:83:10
+error: binding modifiers may only be written when the default binding mode is `move`
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:75:10
    |
 LL |     let [ref x] = &[0];
-   |          ^^^ cannot override to bind by-reference when that is the implicit default
+   |          ^^^ binding modifier not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:75:9
+   |
+LL |     let [ref x] = &[0];
+   |         ^^^^^^^ this matches on type `&_`
 help: make the implied reference pattern explicit
    |
 LL |     let &[ref x] = &[0];
    |         +
 
-error: this pattern relies on behavior which may change in edition 2024
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:88:10
+error: binding modifiers may only be written when the default binding mode is `move`
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:79:10
    |
 LL |     let [ref x] = &mut [0];
-   |          ^^^ cannot override to bind by-reference when that is the implicit default
+   |          ^^^ binding modifier not allowed under `ref mut` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:79:9
+   |
+LL |     let [ref x] = &mut [0];
+   |         ^^^^^^^ this matches on type `&mut _`
 help: make the implied reference pattern explicit
    |
 LL |     let &mut [ref x] = &mut [0];
    |         ++++
 
-error: this pattern relies on behavior which may change in edition 2024
-  --> $DIR/ref-binding-on-inh-ref-errors.rs:93:10
+error: binding modifiers may only be written when the default binding mode is `move`
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:83:10
    |
 LL |     let [ref mut x] = &mut [0];
-   |          ^^^^^^^ cannot override to bind by-reference when that is the implicit default
+   |          ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/ref-binding-on-inh-ref-errors.rs:83:9
+   |
+LL |     let [ref mut x] = &mut [0];
+   |         ^^^^^^^^^^^ this matches on type `&mut _`
 help: make the implied reference pattern explicit
    |
 LL |     let &mut [ref mut x] = &mut [0];
diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed
index e2b2c987610..0a22e939496 100644
--- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed
+++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed
@@ -23,22 +23,22 @@ fn main() {
     assert_type_eq(x, &mut 0u8);
 
     let &Foo(mut x) = &Foo(0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let &mut Foo(mut x) = &mut Foo(0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
-    let &Foo(ref x) = &Foo(0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    let Foo(x) = &Foo(0);
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, &0u8);
 
     let &mut Foo(ref x) = &mut Foo(0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, &0u8);
 
@@ -55,22 +55,22 @@ fn main() {
     assert_type_eq(x, &0u8);
 
     let &Foo(&x) = &Foo(&0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let &Foo(&mut x) = &Foo(&mut 0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let &mut Foo(&x) = &mut Foo(&0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let &mut Foo(&mut x) = &mut Foo(&mut 0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
@@ -79,25 +79,25 @@ fn main() {
     }
 
     if let &&&&&Some(&x) = &&&&&Some(&0u8) {
-        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+        //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, 0u8);
     }
 
     if let &&&&&Some(&mut x) = &&&&&Some(&mut 0u8) {
-        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+        //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, 0u8);
     }
 
     if let &&&&&mut Some(&x) = &&&&&mut Some(&0u8) {
-        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+        //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, 0u8);
     }
 
     if let &mut Some(&mut Some(&mut Some(ref mut x))) = &mut Some(&mut Some(&mut Some(0u8))) {
-        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+        //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, &mut 0u8);
     }
@@ -109,20 +109,20 @@ fn main() {
     }
 
     let &Struct { ref a, mut b, ref c } = &Struct { a: 0, b: 0, c: 0 };
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(a, &0u32);
     assert_type_eq(b, 0u32);
 
     let &Struct { a: &a, ref b, ref c } = &Struct { a: &0, b: &0, c: &0 };
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(a, 0u32);
     assert_type_eq(b, &&0u32);
     assert_type_eq(c, &&0u32);
 
     if let &Struct { a: &Some(a), b: &Some(&b), c: &Some(ref c) } =
-        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+        //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
         //~| WARN: this changes meaning in Rust 2024
         &(Struct { a: &Some(&0), b: &Some(&0), c: &Some(&0) })
     {
@@ -135,10 +135,108 @@ fn main() {
         // The two patterns are the same syntactically, but because they're defined in different
         // editions they don't mean the same thing.
         &(Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => {
-            //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+            //~^ ERROR: binding modifiers may only be written when the default binding mode is `move`
             assert_type_eq(x, 0u32);
             assert_type_eq(y, 0u32);
         }
         _ => {}
     }
+
+    let &mut [&mut &[ref a]] = &mut [&mut &[0]];
+    //~^ ERROR: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+
+    let &[&(_)] = &[&0];
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+
+    // NB: Most of the following tests are for possible future improvements to migration suggestions
+
+    // Test removing multiple binding modifiers.
+    let Struct { a, b, c } = &Struct { a: 0, b: 0, c: 0 };
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+    assert_type_eq(c, &0u32);
+
+    // Test that we don't change bindings' modes when removing binding modifiers.
+    let &mut Struct { ref a, ref mut b, ref mut c } = &mut Struct { a: 0, b: 0, c: 0 };
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+    assert_type_eq(b, &mut 0u32);
+    assert_type_eq(c, &mut 0u32);
+
+    // Test removing multiple reference patterns of various mutabilities, plus a binding modifier.
+    let &mut &Struct { a: &[ref a], b: &mut [&[ref b]], ref c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 };
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+    assert_type_eq(b, &0u32);
+    assert_type_eq(c, &0u32);
+
+    // Test that we don't change bindings' types when removing reference patterns.
+    let &Foo(&ref a) = &Foo(&0);
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+
+    // Test that we don't change bindings' modes when adding reference paterns (caught early).
+    let &(&a, ref b, &[ref c], &mut [&mut (ref d, &[ref e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]);
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, 0u32);
+    assert_type_eq(b, &0u32);
+    assert_type_eq(c, &0u32);
+    assert_type_eq(d, &0u32);
+    assert_type_eq(e, &0u32);
+
+    // Test that we don't change bindings' modes when adding reference patterns (caught late).
+    let &(ref a, &mut [ref b], &[mut c]) = &(0, &mut [0], &[0]);
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+    assert_type_eq(b, &0u32);
+    assert_type_eq(c, 0u32);
+
+    // Test featuring both additions and removals.
+    let &(&a, &mut (ref b, &[ref c])) = &(&0, &mut (0, &[0]));
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, 0u32);
+    assert_type_eq(b, &0u32);
+    assert_type_eq(c, &0u32);
+
+    // Test that bindings' subpatterns' modes are updated properly.
+    let &[mut a @ ref b] = &[0];
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, 0u32);
+    assert_type_eq(b, &0u32);
+
+    // Test that bindings' subpatterns' modes are checked properly.
+    let &[ref a @ mut b] = &[0];
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+    assert_type_eq(b, 0u32);
+
+    // Test that we respect bindings' subpatterns' types when rewriting `&ref x` to `x`.
+    let [&Foo(&ref a @ ref b), &Foo(&ref c @ d)] = [&Foo(&0); 2];
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+    assert_type_eq(b, &0u32);
+    assert_type_eq(c, &0u32);
+    assert_type_eq(d, 0u32);
+
+    // Test that we respect bindings' subpatterns' modes when rewriting `&ref x` to `x`.
+    let [&Foo(&ref a @ [ref b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &[0u32]);
+    assert_type_eq(b, &0u32);
+    assert_type_eq(c, &[0u32]);
+    assert_type_eq(d, 0u32);
 }
diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs
index 098540adfa2..7a6f2269d44 100644
--- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs
+++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs
@@ -23,22 +23,22 @@ fn main() {
     assert_type_eq(x, &mut 0u8);
 
     let Foo(mut x) = &Foo(0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let Foo(mut x) = &mut Foo(0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let Foo(ref x) = &Foo(0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, &0u8);
 
     let Foo(ref x) = &mut Foo(0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, &0u8);
 
@@ -55,22 +55,22 @@ fn main() {
     assert_type_eq(x, &0u8);
 
     let Foo(&x) = &Foo(&0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let Foo(&mut x) = &Foo(&mut 0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let Foo(&x) = &mut Foo(&0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let Foo(&mut x) = &mut Foo(&mut 0);
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
@@ -79,25 +79,25 @@ fn main() {
     }
 
     if let Some(&x) = &&&&&Some(&0u8) {
-        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+        //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, 0u8);
     }
 
     if let Some(&mut x) = &&&&&Some(&mut 0u8) {
-        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+        //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, 0u8);
     }
 
     if let Some(&x) = &&&&&mut Some(&0u8) {
-        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+        //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, 0u8);
     }
 
     if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) {
-        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+        //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, &mut 0u8);
     }
@@ -109,20 +109,20 @@ fn main() {
     }
 
     let Struct { a, mut b, c } = &Struct { a: 0, b: 0, c: 0 };
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(a, &0u32);
     assert_type_eq(b, 0u32);
 
     let Struct { a: &a, b, ref c } = &Struct { a: &0, b: &0, c: &0 };
-    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+    //~^ ERROR: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(a, 0u32);
     assert_type_eq(b, &&0u32);
     assert_type_eq(c, &&0u32);
 
     if let Struct { a: &Some(a), b: Some(&b), c: Some(c) } =
-        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+        //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
         //~| WARN: this changes meaning in Rust 2024
         &(Struct { a: &Some(&0), b: &Some(&0), c: &Some(&0) })
     {
@@ -135,10 +135,108 @@ fn main() {
         // The two patterns are the same syntactically, but because they're defined in different
         // editions they don't mean the same thing.
         (Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => {
-            //~^ ERROR: this pattern relies on behavior which may change in edition 2024
+            //~^ ERROR: binding modifiers may only be written when the default binding mode is `move`
             assert_type_eq(x, 0u32);
             assert_type_eq(y, 0u32);
         }
         _ => {}
     }
+
+    let [&mut [ref a]] = &mut [&mut &[0]];
+    //~^ ERROR: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+
+    let [&(_)] = &[&0];
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+
+    // NB: Most of the following tests are for possible future improvements to migration suggestions
+
+    // Test removing multiple binding modifiers.
+    let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 };
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+    assert_type_eq(c, &0u32);
+
+    // Test that we don't change bindings' modes when removing binding modifiers.
+    let Struct { ref a, ref mut b, c } = &mut Struct { a: 0, b: 0, c: 0 };
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+    assert_type_eq(b, &mut 0u32);
+    assert_type_eq(c, &mut 0u32);
+
+    // Test removing multiple reference patterns of various mutabilities, plus a binding modifier.
+    let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 };
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+    assert_type_eq(b, &0u32);
+    assert_type_eq(c, &0u32);
+
+    // Test that we don't change bindings' types when removing reference patterns.
+    let Foo(&ref a) = &Foo(&0);
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+
+    // Test that we don't change bindings' modes when adding reference paterns (caught early).
+    let (&a, b, [c], [(d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]);
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, 0u32);
+    assert_type_eq(b, &0u32);
+    assert_type_eq(c, &0u32);
+    assert_type_eq(d, &0u32);
+    assert_type_eq(e, &0u32);
+
+    // Test that we don't change bindings' modes when adding reference patterns (caught late).
+    let (a, [b], [mut c]) = &(0, &mut [0], &[0]);
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+    assert_type_eq(b, &0u32);
+    assert_type_eq(c, 0u32);
+
+    // Test featuring both additions and removals.
+    let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0]));
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, 0u32);
+    assert_type_eq(b, &0u32);
+    assert_type_eq(c, &0u32);
+
+    // Test that bindings' subpatterns' modes are updated properly.
+    let [mut a @ b] = &[0];
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, 0u32);
+    assert_type_eq(b, &0u32);
+
+    // Test that bindings' subpatterns' modes are checked properly.
+    let [a @ mut b] = &[0];
+    //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+    assert_type_eq(b, 0u32);
+
+    // Test that we respect bindings' subpatterns' types when rewriting `&ref x` to `x`.
+    let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2];
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &0u32);
+    assert_type_eq(b, &0u32);
+    assert_type_eq(c, &0u32);
+    assert_type_eq(d, 0u32);
+
+    // Test that we respect bindings' subpatterns' modes when rewriting `&ref x` to `x`.
+    let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+    //~| WARN: this changes meaning in Rust 2024
+    assert_type_eq(a, &[0u32]);
+    assert_type_eq(b, &0u32);
+    assert_type_eq(c, &[0u32]);
+    assert_type_eq(d, 0u32);
 }
diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr
index 83346b9dd4a..191800df07a 100644
--- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr
+++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr
@@ -1,11 +1,16 @@
-error: this pattern relies on behavior which may change in edition 2024
+error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:25:13
    |
 LL |     let Foo(mut x) = &Foo(0);
-   |             ^^^ requires binding by-value, but the implicit default is by-reference
+   |             ^^^ binding modifier not allowed under `ref` default binding mode
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:25:9
+   |
+LL |     let Foo(mut x) = &Foo(0);
+   |         ^^^^^^^^^^ this matches on type `&_`
 note: the lint level is defined here
   --> $DIR/migration_lint.rs:7:9
    |
@@ -16,206 +21,546 @@ help: make the implied reference pattern explicit
 LL |     let &Foo(mut x) = &Foo(0);
    |         +
 
-error: this pattern relies on behavior which may change in edition 2024
+error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:30:13
    |
 LL |     let Foo(mut x) = &mut Foo(0);
-   |             ^^^ requires binding by-value, but the implicit default is by-reference
+   |             ^^^ binding modifier not allowed under `ref mut` default binding mode
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:30:9
+   |
+LL |     let Foo(mut x) = &mut Foo(0);
+   |         ^^^^^^^^^^ this matches on type `&mut _`
 help: make the implied reference pattern explicit
    |
 LL |     let &mut Foo(mut x) = &mut Foo(0);
    |         ++++
 
-error: this pattern relies on behavior which may change in edition 2024
+error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:35:13
    |
 LL |     let Foo(ref x) = &Foo(0);
-   |             ^^^ cannot override to bind by-reference when that is the implicit default
+   |             ^^^ binding modifier not allowed under `ref` default binding mode
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
-help: make the implied reference pattern explicit
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:35:9
+   |
+LL |     let Foo(ref x) = &Foo(0);
+   |         ^^^^^^^^^^ this matches on type `&_`
+help: remove the unnecessary binding modifier
+   |
+LL -     let Foo(ref x) = &Foo(0);
+LL +     let Foo(x) = &Foo(0);
    |
-LL |     let &Foo(ref x) = &Foo(0);
-   |         +
 
-error: this pattern relies on behavior which may change in edition 2024
+error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:40:13
    |
 LL |     let Foo(ref x) = &mut Foo(0);
-   |             ^^^ cannot override to bind by-reference when that is the implicit default
+   |             ^^^ binding modifier not allowed under `ref mut` default binding mode
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:40:9
+   |
+LL |     let Foo(ref x) = &mut Foo(0);
+   |         ^^^^^^^^^^ this matches on type `&mut _`
 help: make the implied reference pattern explicit
    |
 LL |     let &mut Foo(ref x) = &mut Foo(0);
    |         ++++
 
-error: this pattern relies on behavior which may change in edition 2024
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:57:13
    |
 LL |     let Foo(&x) = &Foo(&0);
-   |             ^ cannot implicitly match against multiple layers of reference
+   |             ^ reference pattern not allowed under `ref` default binding mode
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:57:9
+   |
+LL |     let Foo(&x) = &Foo(&0);
+   |         ^^^^^^^ this matches on type `&_`
 help: make the implied reference pattern explicit
    |
 LL |     let &Foo(&x) = &Foo(&0);
    |         +
 
-error: this pattern relies on behavior which may change in edition 2024
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:62:13
    |
 LL |     let Foo(&mut x) = &Foo(&mut 0);
-   |             ^^^^ cannot implicitly match against multiple layers of reference
+   |             ^^^^ reference pattern not allowed under `ref` default binding mode
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:62:9
+   |
+LL |     let Foo(&mut x) = &Foo(&mut 0);
+   |         ^^^^^^^^^^^ this matches on type `&_`
 help: make the implied reference pattern explicit
    |
 LL |     let &Foo(&mut x) = &Foo(&mut 0);
    |         +
 
-error: this pattern relies on behavior which may change in edition 2024
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:67:13
    |
 LL |     let Foo(&x) = &mut Foo(&0);
-   |             ^ cannot implicitly match against multiple layers of reference
+   |             ^ reference pattern not allowed under `ref mut` default binding mode
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:67:9
+   |
+LL |     let Foo(&x) = &mut Foo(&0);
+   |         ^^^^^^^ this matches on type `&mut _`
 help: make the implied reference pattern explicit
    |
 LL |     let &mut Foo(&x) = &mut Foo(&0);
    |         ++++
 
-error: this pattern relies on behavior which may change in edition 2024
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:72:13
    |
 LL |     let Foo(&mut x) = &mut Foo(&mut 0);
-   |             ^^^^ cannot implicitly match against multiple layers of reference
+   |             ^^^^ reference pattern not allowed under `ref mut` default binding mode
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:72:9
+   |
+LL |     let Foo(&mut x) = &mut Foo(&mut 0);
+   |         ^^^^^^^^^^^ this matches on type `&mut _`
 help: make the implied reference pattern explicit
    |
 LL |     let &mut Foo(&mut x) = &mut Foo(&mut 0);
    |         ++++
 
-error: this pattern relies on behavior which may change in edition 2024
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:81:17
    |
 LL |     if let Some(&x) = &&&&&Some(&0u8) {
-   |                 ^ cannot implicitly match against multiple layers of reference
+   |                 ^ reference pattern not allowed under `ref` default binding mode
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:81:12
+   |
+LL |     if let Some(&x) = &&&&&Some(&0u8) {
+   |            ^^^^^^^^ this matches on type `&_`
 help: make the implied reference patterns explicit
    |
 LL |     if let &&&&&Some(&x) = &&&&&Some(&0u8) {
    |            +++++
 
-error: this pattern relies on behavior which may change in edition 2024
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:87:17
    |
 LL |     if let Some(&mut x) = &&&&&Some(&mut 0u8) {
-   |                 ^^^^ cannot implicitly match against multiple layers of reference
+   |                 ^^^^ reference pattern not allowed under `ref` default binding mode
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:87:12
+   |
+LL |     if let Some(&mut x) = &&&&&Some(&mut 0u8) {
+   |            ^^^^^^^^^^^^ this matches on type `&_`
 help: make the implied reference patterns explicit
    |
 LL |     if let &&&&&Some(&mut x) = &&&&&Some(&mut 0u8) {
    |            +++++
 
-error: this pattern relies on behavior which may change in edition 2024
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:93:17
    |
 LL |     if let Some(&x) = &&&&&mut Some(&0u8) {
-   |                 ^ cannot implicitly match against multiple layers of reference
+   |                 ^ reference pattern not allowed under `ref` default binding mode
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:93:12
+   |
+LL |     if let Some(&x) = &&&&&mut Some(&0u8) {
+   |            ^^^^^^^^ this matches on type `&_`
 help: make the implied reference patterns explicit
    |
 LL |     if let &&&&&mut Some(&x) = &&&&&mut Some(&0u8) {
    |            ++++++++
 
-error: this pattern relies on behavior which may change in edition 2024
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:99:17
    |
 LL |     if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) {
-   |                 ^^^^ cannot implicitly match against multiple layers of reference
+   |                 ^^^^ reference pattern not allowed under `ref mut` default binding mode
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:99:12
+   |
+LL |     if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) {
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&mut _`
 help: make the implied reference patterns and variable binding mode explicit
    |
 LL |     if let &mut Some(&mut Some(&mut Some(ref mut x))) = &mut Some(&mut Some(&mut Some(0u8))) {
    |            ++++                ++++      +++++++
 
-error: this pattern relies on behavior which may change in edition 2024
+error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:111:21
    |
 LL |     let Struct { a, mut b, c } = &Struct { a: 0, b: 0, c: 0 };
-   |                     ^^^ requires binding by-value, but the implicit default is by-reference
+   |                     ^^^ binding modifier not allowed under `ref` default binding mode
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:111:9
+   |
+LL |     let Struct { a, mut b, c } = &Struct { a: 0, b: 0, c: 0 };
+   |         ^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
 help: make the implied reference pattern and variable binding modes explicit
    |
 LL |     let &Struct { ref a, mut b, ref c } = &Struct { a: 0, b: 0, c: 0 };
    |         +         +++           +++
 
-error: this pattern relies on behavior which may change in edition 2024
+error: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:117:21
    |
 LL |     let Struct { a: &a, b, ref c } = &Struct { a: &0, b: &0, c: &0 };
-   |                     ^      ^^^ cannot override to bind by-reference when that is the implicit default
+   |                     ^      ^^^ binding modifier not allowed under `ref` default binding mode
    |                     |
-   |                     cannot implicitly match against multiple layers of reference
+   |                     reference pattern not allowed under `ref` default binding mode
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:117:9
+   |
+LL |     let Struct { a: &a, b, ref c } = &Struct { a: &0, b: &0, c: &0 };
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
 help: make the implied reference pattern and variable binding mode explicit
    |
 LL |     let &Struct { a: &a, ref b, ref c } = &Struct { a: &0, b: &0, c: &0 };
    |         +                +++
 
-error: this pattern relies on behavior which may change in edition 2024
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:124:24
    |
 LL |     if let Struct { a: &Some(a), b: Some(&b), c: Some(c) } =
-   |                        ^                 ^ cannot implicitly match against multiple layers of reference
+   |                        ^                 ^ reference pattern not allowed under `ref` default binding mode
    |                        |
-   |                        cannot implicitly match against multiple layers of reference
+   |                        reference pattern not allowed under `ref` default binding mode
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:124:12
+   |
+LL |     if let Struct { a: &Some(a), b: Some(&b), c: Some(c) } =
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
 help: make the implied reference patterns and variable binding mode explicit
    |
 LL |     if let &Struct { a: &Some(a), b: &Some(&b), c: &Some(ref c) } =
    |            +                         +             +     +++
 
-error: this pattern relies on behavior which may change in edition 2024
+error: binding modifiers may only be written when the default binding mode is `move`
   --> $DIR/migration_lint.rs:137:15
    |
 LL |         (Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => {
-   |               ^^^     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ default binding mode is reset within expansion
+   |               ^^^     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ occurs within macro expansion
    |               |
-   |               requires binding by-value, but the implicit default is by-reference
+   |               binding modifier not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:137:9
+   |
+LL |         (Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => {
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
    = note: this error originates in the macro `migration_lint_macros::mixed_edition_pat` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: make the implied reference pattern explicit
    |
 LL |         &(Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => {
    |         +
 
-error: aborting due to 16 previous errors
+error: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024
+  --> $DIR/migration_lint.rs:145:10
+   |
+LL |     let [&mut [ref a]] = &mut [&mut &[0]];
+   |          ^^^^  ^^^ binding modifier not allowed under `ref` default binding mode
+   |          |
+   |          reference pattern not allowed under `ref mut` default binding mode
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:145:15
+   |
+LL |     let [&mut [ref a]] = &mut [&mut &[0]];
+   |               ^^^^^^^ this matches on type `&_`
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:145:9
+   |
+LL |     let [&mut [ref a]] = &mut [&mut &[0]];
+   |         ^^^^^^^^^^^^^^ this matches on type `&mut _`
+help: make the implied reference patterns explicit
+   |
+LL |     let &mut [&mut &[ref a]] = &mut [&mut &[0]];
+   |         ++++       +
+
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+  --> $DIR/migration_lint.rs:150:10
+   |
+LL |     let [&(_)] = &[&0];
+   |          ^ reference pattern not allowed under `ref` default binding mode
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:150:9
+   |
+LL |     let [&(_)] = &[&0];
+   |         ^^^^^^ this matches on type `&_`
+help: make the implied reference pattern explicit
+   |
+LL |     let &[&(_)] = &[&0];
+   |         +
+
+error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
+  --> $DIR/migration_lint.rs:157:18
+   |
+LL |     let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 };
+   |                  ^^^    ^^^ binding modifier not allowed under `ref` default binding mode
+   |                  |
+   |                  binding modifier not allowed under `ref` default binding mode
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:157:9
+   |
+LL |     let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 };
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
+help: remove the unnecessary binding modifiers
+   |
+LL -     let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 };
+LL +     let Struct { a, b, c } = &Struct { a: 0, b: 0, c: 0 };
+   |
+
+error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
+  --> $DIR/migration_lint.rs:164:18
+   |
+LL |     let Struct { ref a, ref mut b, c } = &mut Struct { a: 0, b: 0, c: 0 };
+   |                  ^^^    ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode
+   |                  |
+   |                  binding modifier not allowed under `ref mut` default binding mode
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:164:9
+   |
+LL |     let Struct { ref a, ref mut b, c } = &mut Struct { a: 0, b: 0, c: 0 };
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&mut _`
+help: make the implied reference pattern and variable binding mode explicit
+   |
+LL |     let &mut Struct { ref a, ref mut b, ref mut c } = &mut Struct { a: 0, b: 0, c: 0 };
+   |         ++++                            +++++++
+
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+  --> $DIR/migration_lint.rs:172:21
+   |
+LL |     let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 };
+   |                     ^            ^^^^ reference pattern not allowed under `ref` default binding mode
+   |                     |
+   |                     reference pattern not allowed under `ref` default binding mode
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:172:9
+   |
+LL |     let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 };
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
+help: make the implied reference patterns and variable binding modes explicit
+   |
+LL |     let &mut &Struct { a: &[ref a], b: &mut [&[ref b]], ref c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 };
+   |         ++++++                               + +++      +++
+
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+  --> $DIR/migration_lint.rs:180:13
+   |
+LL |     let Foo(&ref a) = &Foo(&0);
+   |             ^ reference pattern not allowed under `ref` default binding mode
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:180:9
+   |
+LL |     let Foo(&ref a) = &Foo(&0);
+   |         ^^^^^^^^^^^ this matches on type `&_`
+help: make the implied reference pattern explicit
+   |
+LL |     let &Foo(&ref a) = &Foo(&0);
+   |         +
+
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+  --> $DIR/migration_lint.rs:186:10
+   |
+LL |     let (&a, b, [c], [(d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]);
+   |          ^ reference pattern not allowed under `ref` default binding mode
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:186:9
+   |
+LL |     let (&a, b, [c], [(d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
+help: make the implied reference patterns and variable binding modes explicit
+   |
+LL |     let &(&a, ref b, &[ref c], &mut [&mut (ref d, &[ref e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]);
+   |         +     +++    + +++     ++++  ++++  +++    + +++
+
+error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
+  --> $DIR/migration_lint.rs:196:19
+   |
+LL |     let (a, [b], [mut c]) = &(0, &mut [0], &[0]);
+   |                   ^^^ binding modifier not allowed under `ref` default binding mode
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:196:9
+   |
+LL |     let (a, [b], [mut c]) = &(0, &mut [0], &[0]);
+   |         ^^^^^^^^^^^^^^^^^ this matches on type `&_`
+help: make the implied reference patterns and variable binding modes explicit
+   |
+LL |     let &(ref a, &mut [ref b], &[mut c]) = &(0, &mut [0], &[0]);
+   |         + +++    ++++  +++     +
+
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+  --> $DIR/migration_lint.rs:204:10
+   |
+LL |     let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0]));
+   |          ^       ^ reference pattern not allowed under `ref` default binding mode
+   |          |
+   |          reference pattern not allowed under `ref` default binding mode
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:204:9
+   |
+LL |     let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0]));
+   |         ^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
+help: make the implied reference patterns and variable binding mode explicit
+   |
+LL |     let &(&a, &mut (ref b, &[ref c])) = &(&0, &mut (0, &[0]));
+   |         +     ++++  +++
+
+error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
+  --> $DIR/migration_lint.rs:212:10
+   |
+LL |     let [mut a @ b] = &[0];
+   |          ^^^ binding modifier not allowed under `ref` default binding mode
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:212:9
+   |
+LL |     let [mut a @ b] = &[0];
+   |         ^^^^^^^^^^^ this matches on type `&_`
+help: make the implied reference pattern and variable binding mode explicit
+   |
+LL |     let &[mut a @ ref b] = &[0];
+   |         +         +++
+
+error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
+  --> $DIR/migration_lint.rs:219:14
+   |
+LL |     let [a @ mut b] = &[0];
+   |              ^^^ binding modifier not allowed under `ref` default binding mode
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:219:9
+   |
+LL |     let [a @ mut b] = &[0];
+   |         ^^^^^^^^^^^ this matches on type `&_`
+help: make the implied reference pattern and variable binding mode explicit
+   |
+LL |     let &[ref a @ mut b] = &[0];
+   |         + +++
+
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+  --> $DIR/migration_lint.rs:226:14
+   |
+LL |     let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2];
+   |              ^                    ^ reference pattern not allowed under `ref` default binding mode
+   |              |
+   |              reference pattern not allowed under `ref` default binding mode
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:226:31
+   |
+LL |     let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2];
+   |                               ^^^^^^^^^^^^^^^ this matches on type `&_`
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:226:10
+   |
+LL |     let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2];
+   |          ^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
+help: make the implied reference patterns explicit
+   |
+LL |     let [&Foo(&ref a @ ref b), &Foo(&ref c @ d)] = [&Foo(&0); 2];
+   |          +                     +
+
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+  --> $DIR/migration_lint.rs:235:14
+   |
+LL |     let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
+   |              ^                      ^ reference pattern not allowed under `ref` default binding mode
+   |              |
+   |              reference pattern not allowed under `ref` default binding mode
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:235:33
+   |
+LL |     let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
+   |                                 ^^^^^^^^^^^^^^^^^ this matches on type `&_`
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/migration_lint.rs:235:10
+   |
+LL |     let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
+   |          ^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
+help: make the implied reference patterns explicit
+   |
+LL |     let [&Foo(&ref a @ [ref b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
+   |          +                       +
+
+error: aborting due to 29 previous errors
 
diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs
index 5ba554fc6e5..4dc04d90aaf 100644
--- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs
+++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs
@@ -21,17 +21,17 @@ macro_rules! test_pat_on_type {
 }
 
 test_pat_on_type![(&x,): &(T,)]; //~ ERROR mismatched types
-test_pat_on_type![(&x,): &(&T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024
+test_pat_on_type![(&x,): &(&T,)]; //~ ERROR reference patterns may only be written when the default binding mode is `move`
 test_pat_on_type![(&x,): &(&mut T,)]; //~ ERROR mismatched types
 test_pat_on_type![(&mut x,): &(&T,)]; //~ ERROR mismatched types
-test_pat_on_type![(&mut x,): &(&mut T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024
+test_pat_on_type![(&mut x,): &(&mut T,)]; //~ ERROR reference patterns may only be written when the default binding mode is `move`
 test_pat_on_type![(&x,): &&mut &(T,)]; //~ ERROR mismatched types
 test_pat_on_type![Foo { f: (&x,) }: Foo]; //~ ERROR mismatched types
 test_pat_on_type![Foo { f: (&x,) }: &mut Foo]; //~ ERROR mismatched types
-test_pat_on_type![Foo { f: &(x,) }: &Foo]; //~ ERROR this pattern relies on behavior which may change in edition 2024
-test_pat_on_type![(mut x,): &(T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024
-test_pat_on_type![(ref x,): &(T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024
-test_pat_on_type![(ref mut x,): &mut (T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024
+test_pat_on_type![Foo { f: &(x,) }: &Foo]; //~ ERROR reference patterns may only be written when the default binding mode is `move`
+test_pat_on_type![(mut x,): &(T,)]; //~ ERROR binding modifiers may only be written when the default binding mode is `move`
+test_pat_on_type![(ref x,): &(T,)]; //~ ERROR binding modifiers may only be written when the default binding mode is `move`
+test_pat_on_type![(ref mut x,): &mut (T,)]; //~ ERROR binding modifiers may only be written when the default binding mode is `move`
 
 fn get<X>() -> X {
     unimplemented!()
@@ -40,6 +40,6 @@ fn get<X>() -> X {
 // Make sure this works even when the underlying type is inferred. This test passes on rust stable.
 fn infer<X: Copy>() -> X {
     match &get() {
-        (&x,) => x, //~ ERROR this pattern relies on behavior which may change in edition 2024
+        (&x,) => x, //~ ERROR reference patterns may only be written when the default binding mode is `move`
     }
 }
diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr
index affdca1d449..0c6b2ff3a2f 100644
--- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr
+++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr
@@ -99,85 +99,122 @@ LL - test_pat_on_type![Foo { f: (&x,) }: &mut Foo];
 LL + test_pat_on_type![Foo { f: (x,) }: &mut Foo];
    |
 
-error: this pattern relies on behavior which may change in edition 2024
+error: reference patterns may only be written when the default binding mode is `move`
   --> $DIR/min_match_ergonomics_fail.rs:24:20
    |
 LL | test_pat_on_type![(&x,): &(&T,)];
-   |                    ^ cannot implicitly match against multiple layers of reference
+   |                    ^ reference pattern not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/min_match_ergonomics_fail.rs:24:19
+   |
+LL | test_pat_on_type![(&x,): &(&T,)];
+   |                   ^^^^^ this matches on type `&_`
 help: make the implied reference pattern explicit
    |
 LL | test_pat_on_type![&(&x,): &(&T,)];
    |                   +
 
-error: this pattern relies on behavior which may change in edition 2024
+error: reference patterns may only be written when the default binding mode is `move`
   --> $DIR/min_match_ergonomics_fail.rs:27:20
    |
 LL | test_pat_on_type![(&mut x,): &(&mut T,)];
-   |                    ^^^^ cannot implicitly match against multiple layers of reference
+   |                    ^^^^ reference pattern not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/min_match_ergonomics_fail.rs:27:19
+   |
+LL | test_pat_on_type![(&mut x,): &(&mut T,)];
+   |                   ^^^^^^^^^ this matches on type `&_`
 help: make the implied reference pattern explicit
    |
 LL | test_pat_on_type![&(&mut x,): &(&mut T,)];
    |                   +
 
-error: this pattern relies on behavior which may change in edition 2024
+error: reference patterns may only be written when the default binding mode is `move`
   --> $DIR/min_match_ergonomics_fail.rs:31:28
    |
 LL | test_pat_on_type![Foo { f: &(x,) }: &Foo];
-   |                            ^ cannot implicitly match against multiple layers of reference
+   |                            ^ reference pattern not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/min_match_ergonomics_fail.rs:31:19
+   |
+LL | test_pat_on_type![Foo { f: &(x,) }: &Foo];
+   |                   ^^^^^^^^^^^^^^^^ this matches on type `&_`
 help: make the implied reference pattern explicit
    |
 LL | test_pat_on_type![&Foo { f: &(x,) }: &Foo];
    |                   +
 
-error: this pattern relies on behavior which may change in edition 2024
+error: binding modifiers may only be written when the default binding mode is `move`
   --> $DIR/min_match_ergonomics_fail.rs:32:20
    |
 LL | test_pat_on_type![(mut x,): &(T,)];
-   |                    ^^^ requires binding by-value, but the implicit default is by-reference
+   |                    ^^^ binding modifier not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/min_match_ergonomics_fail.rs:32:19
+   |
+LL | test_pat_on_type![(mut x,): &(T,)];
+   |                   ^^^^^^^^ this matches on type `&_`
 help: make the implied reference pattern explicit
    |
 LL | test_pat_on_type![&(mut x,): &(T,)];
    |                   +
 
-error: this pattern relies on behavior which may change in edition 2024
+error: binding modifiers may only be written when the default binding mode is `move`
   --> $DIR/min_match_ergonomics_fail.rs:33:20
    |
 LL | test_pat_on_type![(ref x,): &(T,)];
-   |                    ^^^ cannot override to bind by-reference when that is the implicit default
+   |                    ^^^ binding modifier not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
-help: make the implied reference pattern explicit
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/min_match_ergonomics_fail.rs:33:19
+   |
+LL | test_pat_on_type![(ref x,): &(T,)];
+   |                   ^^^^^^^^ this matches on type `&_`
+help: remove the unnecessary binding modifier
+   |
+LL - test_pat_on_type![(ref x,): &(T,)];
+LL + test_pat_on_type![(x,): &(T,)];
    |
-LL | test_pat_on_type![&(ref x,): &(T,)];
-   |                   +
 
-error: this pattern relies on behavior which may change in edition 2024
+error: binding modifiers may only be written when the default binding mode is `move`
   --> $DIR/min_match_ergonomics_fail.rs:34:20
    |
 LL | test_pat_on_type![(ref mut x,): &mut (T,)];
-   |                    ^^^^^^^ cannot override to bind by-reference when that is the implicit default
+   |                    ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
-help: make the implied reference pattern explicit
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/min_match_ergonomics_fail.rs:34:19
+   |
+LL | test_pat_on_type![(ref mut x,): &mut (T,)];
+   |                   ^^^^^^^^^^^^ this matches on type `&mut _`
+help: remove the unnecessary binding modifier
+   |
+LL - test_pat_on_type![(ref mut x,): &mut (T,)];
+LL + test_pat_on_type![(x,): &mut (T,)];
    |
-LL | test_pat_on_type![&mut (ref mut x,): &mut (T,)];
-   |                   ++++
 
-error: this pattern relies on behavior which may change in edition 2024
+error: reference patterns may only be written when the default binding mode is `move`
   --> $DIR/min_match_ergonomics_fail.rs:43:10
    |
 LL |         (&x,) => x,
-   |          ^ cannot implicitly match against multiple layers of reference
+   |          ^ reference pattern not allowed under `ref` default binding mode
    |
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+note: matching on a reference type with a non-reference pattern changes the default binding mode
+  --> $DIR/min_match_ergonomics_fail.rs:43:9
+   |
+LL |         (&x,) => x,
+   |         ^^^^^ this matches on type `&_`
 help: make the implied reference pattern explicit
    |
 LL |         &(&x,) => x,