diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index ae00bb4e218ab..1895e0abad7e8 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -835,20 +835,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     self.add_rust_2024_migration_desugared_pat(
                         pat_info.top_info.hir_id,
                         pat,
-                        ident.span,
+                        't',
                         def_br_mutbl,
                     );
                     BindingMode(ByRef::No, Mutability::Mut)
                 }
             }
             BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
-            BindingMode(ByRef::Yes(_), _) => {
+            BindingMode(ByRef::Yes(user_br_mutbl), _) => {
                 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,
-                        ident.span,
+                        if user_br_mutbl.is_mut() { 't' } else { 'f' },
                         def_br_mutbl,
                     );
                 }
@@ -2387,7 +2387,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     self.add_rust_2024_migration_desugared_pat(
                         pat_info.top_info.hir_id,
                         pat,
-                        inner.span,
+                        if pat_mutbl.is_mut() { 't' } else { '&' },
                         inh_mut,
                     )
                 }
@@ -2779,55 +2779,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         pat_id: HirId,
         subpat: &'tcx Pat<'tcx>,
-        cutoff_span: Span,
+        final_char: char,
         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, |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
-        // error if the subpattern is of edition >= 2024.
-        let trimmed_span = subpat.span.until(cutoff_span).with_ctxt(subpat.span.ctxt());
+        // Importantly, the edition of the trimmed span should be the same as `subpat.span`; this
+        // will be a hard error if the subpattern is of edition >= 2024.
+        let from_expansion = subpat.span.from_expansion();
+        let trimmed_span = if from_expansion {
+            subpat.span
+        } else {
+            self.tcx.sess.source_map().span_through_char(subpat.span, final_char)
+        };
 
         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(),
-        });
+        // binding mode.
+        let info = table.entry(pat_id).or_default();
 
-        let pat_kind = if let PatKind::Binding(user_bind_annot, _, _, _) = subpat.kind {
+        let pat_kind = if matches!(subpat.kind, PatKind::Binding(..)) {
             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"
         };
         // 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 from_expansion = subpat.span.from_expansion();
         let primary_label = if from_expansion {
-            // We can't suggest eliding modifiers within expansions.
-            info.suggest_eliding_modes = false;
             // 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.
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index b8c73d2584379..f923205e631aa 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -812,7 +812,7 @@ 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)]
+#[derive(TyEncodable, TyDecodable, Debug, Default, HashStable)]
 pub struct Rust2024IncompatiblePatInfo {
     /// Labeled spans for `&`s, `&mut`s, and binding modifiers incompatible with Rust 2024.
     pub primary_labels: Vec<(Span, String)>,
@@ -820,6 +820,4 @@ pub struct Rust2024IncompatiblePatInfo {
     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/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index f1753be845d4f..89a7a7bb0e11a 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -1097,34 +1097,41 @@ pub(crate) enum MiscPatternSuggestion {
 
 #[derive(LintDiagnostic)]
 #[diag(mir_build_rust_2024_incompatible_pat)]
-pub(crate) struct Rust2024IncompatiblePat {
+pub(crate) struct Rust2024IncompatiblePat<'m> {
     #[subdiagnostic]
-    pub(crate) sugg: Rust2024IncompatiblePatSugg,
+    pub(crate) sugg: Rust2024IncompatiblePatSugg<'m>,
     pub(crate) bad_modifiers: bool,
     pub(crate) bad_ref_pats: bool,
     pub(crate) is_hard_error: bool,
 }
 
-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) struct Rust2024IncompatiblePatSugg<'m> {
     pub(crate) suggestion: Vec<(Span, String)>,
+    /// If `Some(..)`, we provide a suggestion about either adding or removing syntax.
+    /// If `None`, we suggest both additions and removals; use a generic wording for simplicity.
+    pub(crate) kind: Option<Rust2024IncompatiblePatSuggKind>,
     pub(crate) ref_pattern_count: usize,
     pub(crate) binding_mode_count: usize,
     /// Labels for where incompatibility-causing by-ref default binding modes were introduced.
-    pub(crate) default_mode_labels: FxIndexMap<Span, ty::Mutability>,
+    pub(crate) default_mode_labels: &'m FxIndexMap<Span, ty::Mutability>,
 }
 
-impl Subdiagnostic for Rust2024IncompatiblePatSugg {
+pub(crate) enum Rust2024IncompatiblePatSuggKind {
+    Subtractive,
+    Additive,
+}
+
+impl<'m> Subdiagnostic for Rust2024IncompatiblePatSugg<'m> {
     fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
         self,
         diag: &mut Diag<'_, G>,
         _f: &F,
     ) {
+        use Rust2024IncompatiblePatSuggKind::*;
+
         // 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() {
+        for (&span, &def_br_mutbl) in self.default_mode_labels.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";
@@ -1143,17 +1150,33 @@ impl Subdiagnostic for Rust2024IncompatiblePatSugg {
             } else {
                 Applicability::MaybeIncorrect
             };
-        let msg = if self.suggest_eliding_modes {
-            let plural_modes = pluralize!(self.binding_mode_count);
-            format!("remove the unnecessary binding modifier{plural_modes}")
-        } else {
-            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 let Some(kind) = self.kind {
+            let derefs = if self.ref_pattern_count > 0 {
+                format!("reference pattern{}", pluralize!(self.ref_pattern_count))
             } else {
                 String::new()
             };
-            format!("make the implied reference pattern{plural_derefs}{and_modes} explicit")
+            let modes = if self.binding_mode_count > 0 {
+                match kind {
+                    Subtractive => {
+                        format!("binding modifier{}", pluralize!(self.binding_mode_count))
+                    }
+                    Additive => {
+                        format!("variable binding mode{}", pluralize!(self.binding_mode_count))
+                    }
+                }
+            } else {
+                String::new()
+            };
+            let and = if !derefs.is_empty() && !modes.is_empty() { " and " } else { "" };
+            match kind {
+                Subtractive => format!("remove the unnecessary {derefs}{and}{modes}"),
+                Additive => {
+                    format!("make the implied {derefs}{and}{modes} explicit")
+                }
+            }
+        } else {
+            "rewrite the pattern".to_owned()
         };
         // FIXME(dianne): for peace of mind, don't risk emitting a 0-part suggestion (that panics!)
         if !self.suggestion.is_empty() {
diff --git a/compiler/rustc_mir_build/src/thir/pattern/migration.rs b/compiler/rustc_mir_build/src/thir/pattern/migration.rs
index bd7787b643d57..12b6afabadfae 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/migration.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/migration.rs
@@ -2,24 +2,30 @@
 
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::MultiSpan;
-use rustc_hir::{BindingMode, ByRef, HirId, Mutability};
+use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability};
+use rustc_index::IndexVec;
 use rustc_lint as lint;
 use rustc_middle::span_bug;
 use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, Ty, TyCtxt};
+use rustc_span::source_map::SourceMap;
 use rustc_span::{Ident, Span};
 
-use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg};
+use crate::errors::{
+    Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg, Rust2024IncompatiblePatSuggKind,
+};
 use crate::fluent_generated as fluent;
 
 /// For patterns flagged for migration during HIR typeck, this handles constructing and emitting
 /// a diagnostic suggestion.
-pub(super) struct PatMigration<'a> {
-    suggestion: Vec<(Span, String)>,
-    ref_pattern_count: usize,
-    binding_mode_count: usize,
-    /// 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.
-    default_mode_span: Option<(Span, ty::Mutability)>,
+pub(super) struct PatMigration<'a, 'tcx> {
+    /// All the variable bindings encountered in lowering the pattern, along with whether to
+    /// suggest adding/removing them.
+    bindings: IndexVec<PatBindingIdx, PatBinding>,
+    /// All the dereferences encountered in lowering the pattern, along with how their corresponding
+    /// patterns affect the default binding mode, and whether to suggest adding/removing them.
+    derefs: IndexVec<PatDerefIdx, PatDeref<'a, 'tcx>>,
+    /// Internal state: the innermost deref above the pattern currently being lowered.
+    innermost_deref: Option<PatDerefIdx>,
     /// Labels for where incompatibility-causing by-ref default binding modes were introduced.
     // FIXME(ref_pat_eat_one_layer_2024_structural): To track the default binding mode, we duplicate
     // logic from HIR typeck (in order to avoid needing to store all changes to the dbm in
@@ -30,13 +36,95 @@ pub(super) struct PatMigration<'a> {
     info: &'a Rust2024IncompatiblePatInfo,
 }
 
-impl<'a> PatMigration<'a> {
+rustc_index::newtype_index! {
+    struct PatBindingIdx {}
+}
+
+rustc_index::newtype_index! {
+    struct PatDerefIdx {}
+}
+
+struct PatBinding {
+    /// The span of the binding modifier (empty if no explicit modifier was provided).
+    span: Span,
+    /// The actual binding mode of this binding.
+    mode: BindingMode,
+    /// Whether to include a binding modifier (e.g. `ref` or `mut`) in the suggested pattern.
+    suggest: bool,
+    /// The next binding in the innermost enclosing deref's list of bindings.
+    next_sibling: Option<PatBindingIdx>,
+}
+
+struct PatDeref<'a, 'tcx> {
+    /// The span of the pattern where this deref occurs (implicitly or explicitly).
+    span: Span,
+    /// The mutability of the ref pattern (or for implicit derefs, of the reference type).
+    // FIXME(ref_pattern_eat_one_layer_2024): Under RFC 3627's Rule 5, a `&` pattern can match a
+    // `&mut` type or `ref mut` binding mode. Thus, an omitted `&` could result in a `ref mut`
+    // default binding mode. We may want to track both the pattern and ref type's mutabilities.
+    mutbl: Mutability,
+    /// Whether this span is for a potentially-removable explicitly-provided deref, or an implicit
+    /// dereference which we can potentially suggest making explicit.
+    kind: PatDerefKind<'a, 'tcx>,
+    /// Whether to include this as a `&` or `&mut` in the suggested pattern.
+    suggest: bool,
+    /// The default binding mode for variables under this deref in the user's pattern.
+    real_default_mode: ByRef,
+    /// The default binding mode for variable under this deref in our suggestion.
+    sugg_default_mode: ByRef,
+    /// The span that introduced the current default binding mode, or `None` for the top-level pat.
+    default_mode_origin: Option<Span>,
+    /// Whether this is an instance of `&ref x` which we may be able to simplify to `x`.
+    /// Stores the HIR id of the binding pattern `ref x`, to identify it later.
+    simplify_deref_ref: Option<HirId>,
+    /// The next deref above this. Since we can't suggest using `&` or `&mut` on a by-ref default
+    /// binding mode, a suggested deref's ancestors must also all be suggested.
+    // FIXME(ref_pat_eat_one_layer_2024): By suggesting `&` and `&mut` patterns that can eat the
+    // default binding mode, we'll be able to make more local suggestions. That may make this forest
+    // structure unnecessary.
+    parent: Option<PatDerefIdx>,
+    /// The head of the linked list of child derefs directly under this. When we suggest a `&`
+    /// pattern, any implicit `&mut` children will go from producing a `ref` default binding mode
+    /// to `ref mut`, so we check recursively in that case to see if any bindings would change.
+    // FIXME(ref_pat_eat_one_layer_2024_structural): Aside from this maybe being unnecessary if we
+    // can make more local suggestions (see the above fixme), RFC 3627's Rule 3 should also obsolete
+    // this (see the comments on `propagate_default_mode_change`).
+    first_child: Option<PatDerefIdx>,
+    /// The next child in their parents' linked list of children.
+    next_sibling: Option<PatDerefIdx>,
+    /// The head of the linked list of bindings directly under this deref. If we suggest this
+    /// deref, we'll also need to suggest binding modifiers for any by-ref bindings.
+    first_binding: Option<PatBindingIdx>,
+}
+
+enum PatDerefKind<'a, 'tcx> {
+    /// For dereferences from lowering `&` and `&mut` patterns
+    Explicit { inner_span: Span },
+    /// For dereferences inserted by match ergonomics
+    Implicit { ref_tys: &'a [Ty<'tcx>] },
+}
+
+/// Assuming the input is a slice of reference types implicitly dereferenced by match ergonomics
+/// (stored in [`ty::TypeckResults::pat_adjustments`]), iterate over their reference mutabilities.
+/// A span is provided for debugging purposes.
+fn iter_ref_mutbls<'a, 'tcx>(
+    span: Span,
+    ref_tys: &'a [Ty<'tcx>],
+) -> impl Iterator<Item = Mutability> + use<'a, 'tcx> {
+    ref_tys.iter().map(move |ref_ty| {
+        let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
+            span_bug!(span, "pattern implicitly dereferences a non-ref type");
+        };
+        mutbl
+    })
+}
+
+impl<'a, 'tcx> PatMigration<'a, 'tcx> {
     pub(super) fn new(info: &'a Rust2024IncompatiblePatInfo) -> Self {
         PatMigration {
-            suggestion: Vec::new(),
-            ref_pattern_count: 0,
-            binding_mode_count: 0,
-            default_mode_span: None,
+            bindings: IndexVec::new(),
+            derefs: IndexVec::new(),
+            innermost_deref: None,
             default_mode_labels: Default::default(),
             info,
         }
@@ -44,19 +132,13 @@ impl<'a> PatMigration<'a> {
 
     /// On Rust 2024, this emits a hard error. On earlier Editions, this emits the
     /// future-incompatibility lint `rust_2024_incompatible_pat`.
-    pub(super) fn emit<'tcx>(self, tcx: TyCtxt<'tcx>, pat_id: HirId) {
+    pub(super) fn emit(self, tcx: TyCtxt<'tcx>, pat_id: HirId) {
         let mut spans =
             MultiSpan::from_spans(self.info.primary_labels.iter().map(|(span, _)| *span).collect());
         for (span, label) in self.info.primary_labels.iter() {
             spans.push_span_label(*span, label.clone());
         }
-        let sugg = Rust2024IncompatiblePatSugg {
-            suggest_eliding_modes: self.info.suggest_eliding_modes,
-            suggestion: self.suggestion,
-            ref_pattern_count: self.ref_pattern_count,
-            binding_mode_count: self.binding_mode_count,
-            default_mode_labels: self.default_mode_labels,
-        };
+        let sugg = self.build_suggestion(tcx.sess.source_map());
         // If a relevant span is from at least edition 2024, this is a hard error.
         let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());
         if is_hard_error {
@@ -86,61 +168,224 @@ impl<'a> PatMigration<'a> {
         }
     }
 
-    /// Tracks when we're lowering a pattern that implicitly dereferences the scrutinee.
-    /// This should only be called when the pattern type adjustments list `adjustments` is
-    /// non-empty. Returns the prior default binding mode; this should be followed by a call to
-    /// [`PatMigration::leave_ref`] to restore it when we leave the pattern.
-    pub(super) fn visit_implicit_derefs<'tcx>(
-        &mut self,
-        pat_span: Span,
-        adjustments: &[Ty<'tcx>],
-    ) -> Option<(Span, Mutability)> {
-        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
+    /// When lowering a reference pattern or a binding with a modifier, this checks if the default
+    /// binding mode is by-ref, and if so, adds a labeled note to the diagnostic with the origin of
+    /// the current default binding mode.
+    fn add_default_mode_label_if_needed(&mut self) {
+        if let ByRef::Yes(ref_mutbl) = self.real_default_mode() {
+            // The by-ref default binding mode must have come from an implicit deref. If there was a
+            // problem in tracking that for the diagnostic, try to avoid ICE on release builds.
+            debug_assert!(
+                self.innermost_deref
+                    .is_some_and(|ix| self.derefs[ix].default_mode_origin.is_some())
+            );
+            if let Some(ix) = self.innermost_deref
+                && let Some(span) = self.derefs[ix].default_mode_origin
+            {
+                self.default_mode_labels.insert(span, ref_mutbl);
+            }
+        }
+    }
+
+    fn build_suggestion<'m>(&'m self, source_map: &SourceMap) -> Rust2024IncompatiblePatSugg<'m> {
+        let mut removed_modifiers = 0;
+        let mut added_modifiers = 0;
+        let modes = self.bindings.iter().filter_map(|binding| {
+            if binding.mode == BindingMode::NONE {
+                // This binding mode is written as the empty string; no need to suggest.
+                None
+            } else {
+                if !binding.suggest && !binding.span.is_empty() {
+                    // This binding is in the source but not the suggestion; suggest removing it.
+                    removed_modifiers += 1;
+                    Some((binding.span, String::new()))
+                } else if binding.suggest && binding.span.is_empty() {
+                    // This binding is in the suggestion but not the source; suggest adding it.
+                    added_modifiers += 1;
+                    Some((binding.span, binding.mode.prefix_str().to_owned()))
+                } else {
+                    // This binding is as it should be.
+                    None
+                }
+            }
+        });
+
+        let mut removed_ref_pats = 0;
+        let mut added_ref_pats = 0;
+        let derefs = self.derefs.iter().filter_map(|deref| match deref.kind {
+            PatDerefKind::Explicit { inner_span } if !deref.suggest => {
+                // This is a ref pattern in the source but not the suggestion; suggest removing it.
+                removed_ref_pats += 1;
+                // Avoid eating the '(' in `&(...)`
+                let span = source_map.span_until_char(deref.span.with_hi(inner_span.lo()), '(');
+                // But *do* eat the ' ' in `&mut [...]`
+                Some((source_map.span_extend_while_whitespace(span), String::new()))
+            }
+            PatDerefKind::Implicit { ref_tys } if deref.suggest => {
+                // This is a ref pattern in the suggestion but not the source; suggest adding it.
+                let ref_pat_str =
+                    iter_ref_mutbls(deref.span, ref_tys).map(Mutability::ref_prefix_str).collect();
+                added_ref_pats += ref_tys.len();
+                Some((deref.span.shrink_to_lo(), ref_pat_str))
+            }
+            _ => None,
         });
 
-        if !self.info.suggest_eliding_modes {
-            // If we can't fix the pattern by eliding modifiers, we'll need to make the pattern
-            // fully explicit. i.e. we'll need to suggest reference patterns for this.
-            let suggestion_str: String =
-                implicit_deref_mutbls.clone().map(|mutbl| mutbl.ref_prefix_str()).collect();
-            self.suggestion.push((pat_span.shrink_to_lo(), suggestion_str));
-            self.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 self.default_mode_span.is_none_or(|(_, old_mutbl)| min_mutbl < old_mutbl) {
-            // This changes the default binding mode to `ref` or `ref mut`. Return the old mode so
-            // it can be reinstated when we leave the pattern.
-            self.default_mode_span.replace((pat_span, min_mutbl))
+        let suggestion = modes.chain(derefs).collect();
+        let (kind, binding_mode_count, ref_pattern_count) =
+            if added_modifiers == 0 && added_ref_pats == 0 {
+                let kind = Rust2024IncompatiblePatSuggKind::Subtractive;
+                (Some(kind), removed_modifiers, removed_ref_pats)
+            } else if removed_modifiers == 0 && removed_ref_pats == 0 {
+                (Some(Rust2024IncompatiblePatSuggKind::Additive), added_modifiers, added_ref_pats)
+            } else {
+                (None, 0, 0)
+            };
+        Rust2024IncompatiblePatSugg {
+            suggestion,
+            kind,
+            binding_mode_count,
+            ref_pattern_count,
+            default_mode_labels: &self.default_mode_labels,
+        }
+    }
+
+    /// The default binding mode at the current point in the pattern the user wrote.
+    fn real_default_mode(&self) -> ByRef {
+        if let Some(current_ix) = self.innermost_deref {
+            self.derefs[current_ix].real_default_mode
         } else {
-            // This does not change the default binding mode; it was already `ref` or `ref mut`.
-            self.default_mode_span
+            ByRef::No
         }
     }
 
+    /// The default binding mode at the current point in the pattern we're suggesting.
+    fn sugg_default_mode(&self) -> ByRef {
+        if let Some(deref_ix) = self.innermost_deref {
+            self.derefs[deref_ix].sugg_default_mode
+        } else {
+            ByRef::No
+        }
+    }
+
+    /// Tracks when we're lowering a pattern that implicitly dereferences the scrutinee.
+    /// This should only be called when the pattern type adjustments list `ref_tys` is non-empty.
+    /// This should be followed by a call to [`PatMigration::leave_ref`] when we leave the pattern.
+    pub(super) fn visit_implicit_derefs(&mut self, pat: &hir::Pat<'_>, ref_tys: &'a [Ty<'tcx>]) {
+        // The effective mutability of this (as far as the default binding mode goes) is `ref` if
+        // any of `ref_tys` are shared, and `ref mut` if they're all mutable.
+        let mutbl = iter_ref_mutbls(pat.span, ref_tys)
+            .min()
+            .expect("`ref_tys` should have at least one element");
+        self.push_deref(pat.span, mutbl, PatDerefKind::Implicit { ref_tys });
+    }
+
     /// Tracks the default binding mode when we're lowering a `&` or `&mut` pattern.
-    /// Returns the prior default binding mode; this should be followed by a call to
-    /// [`PatMigration::leave_ref`] to restore it when we leave the pattern.
-    pub(super) fn visit_explicit_deref(&mut self) -> Option<(Span, Mutability)> {
-        if let Some((default_mode_span, default_ref_mutbl)) = self.default_mode_span {
-            // If this eats a by-ref default binding mode, label the binding mode.
-            self.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
-        }
-        // Set the default binding mode to by-value and return the old default binding mode so it
-        // can be reinstated when we leave the pattern.
-        self.default_mode_span.take()
+    /// This should be followed by a call to [`PatMigration::leave_ref`] when we leave the pattern.
+    // FIXME(ref_pat_eat_one_layer_2024): This assumes reference patterns correspond to real
+    // dereferences. If reference patterns can match the default binding mode alone, we may need to
+    // check `TypeckResults::skipped_ref_pats` to tell if this pattern corresponds to an implicit
+    // dereference we've already visited.
+    pub(super) fn visit_explicit_deref(
+        &mut self,
+        pat_span: Span,
+        mutbl: Mutability,
+        subpat: &hir::Pat<'_>,
+    ) {
+        // If this eats a by-ref default binding mode, label the binding mode.
+        self.add_default_mode_label_if_needed();
+        // This sets the default binding mode to by-value in the user's pattern, but we'll try to
+        // suggest removing it.
+        let my_ix =
+            self.push_deref(pat_span, mutbl, PatDerefKind::Explicit { inner_span: subpat.span });
+
+        // If this is inside a macro expansion, we won't be able to remove it.
+        if pat_span.from_expansion() {
+            self.add_derefs_to_suggestion(self.innermost_deref);
+            return;
+        }
+
+        // If the subpattern is a binding, removing this reference pattern would change its type.
+        // FIXME(ref_pat_eat_one_layer_2024): This assumes ref pats can't eat the binding mode
+        // alone. Depending on the pattern typing rules in use, we can be more precise here.
+        if let hir::PatKind::Binding(explicit_ba, _, _, _) = subpat.kind {
+            if explicit_ba == BindingMode(ByRef::Yes(mutbl), Mutability::Not) {
+                // If the binding has a `ref` modifier, we can elide both this `&` and the `ref`;
+                // i.e. we can simplify `&ref x` to `x`, as long as all parent derefs are explicit.
+                // NB: We don't rewrite `&ref x @ ...` to `x @ &...`, so we may end up needing to
+                // reinstate this `&` later if the binding's subpattern requires it.
+                // FIXME(ref_pat_eat_one_layer_2024): With RFC 3627's Rule 5, `&` patterns can match
+                // `&mut` types; we'll have to check the mutability of the type rather than the
+                // pattern to see whether we can elide it.
+                self.derefs[my_ix].simplify_deref_ref = Some(subpat.hir_id);
+                self.add_derefs_to_suggestion(self.derefs[my_ix].parent);
+            } else {
+                // Otherwise, we need to suggest including this `&` as well.
+                self.add_derefs_to_suggestion(self.innermost_deref);
+            }
+        }
+    }
+
+    /// Adds a deref to our deref-forest, so that we can track the default binding mode and
+    /// propagate binding mode changes when we suggest adding patterns.
+    /// See [`PatMigration::propagate_default_mode_change`].
+    fn push_deref(
+        &mut self,
+        span: Span,
+        mutbl: Mutability,
+        kind: PatDerefKind<'a, 'tcx>,
+    ) -> PatDerefIdx {
+        let parent = self.innermost_deref;
+        // Get the new default binding mode in the pattern the user wrote.
+        let real_default_mode = match kind {
+            PatDerefKind::Implicit { .. } => match self.real_default_mode() {
+                ByRef::Yes(old_mutbl) => ByRef::Yes(Ord::min(mutbl, old_mutbl)),
+                ByRef::No => ByRef::Yes(mutbl),
+            },
+            PatDerefKind::Explicit { .. } => ByRef::No,
+        };
+        // If this keeps the default binding mode the same, it shares a mode origin with its
+        // parent. If it changes the default binding mode, its mode origin is itself.
+        let default_mode_origin = if real_default_mode == self.real_default_mode() {
+            parent.and_then(|p| self.derefs[p].default_mode_origin)
+        } else {
+            Some(span)
+        };
+        // Get the default binding mode in the suggestion, assuming we don't include a reference
+        // pattern for this deref. We may add one later if necessary.
+        let sugg_default_mode = ByRef::Yes(match self.sugg_default_mode() {
+            ByRef::Yes(parent_mutbl) => Ord::min(mutbl, parent_mutbl),
+            ByRef::No => mutbl,
+        });
+        let my_ix = self.derefs.push(PatDeref {
+            span,
+            mutbl,
+            kind,
+            suggest: false,
+            sugg_default_mode,
+            real_default_mode,
+            default_mode_origin,
+            simplify_deref_ref: None,
+            parent,
+            next_sibling: parent.and_then(|p| self.derefs[p].first_child),
+            first_child: None,
+            first_binding: None,
+        });
+        if let Some(p) = parent {
+            self.derefs[p].first_child = Some(my_ix);
+        }
+        self.innermost_deref = Some(my_ix);
+        my_ix
     }
 
     /// Restores the default binding mode after lowering a pattern that could change it.
     /// This should follow a call to either [`PatMigration::visit_explicit_deref`] or
     /// [`PatMigration::visit_implicit_derefs`].
-    pub(super) fn leave_ref(&mut self, old_mode_span: Option<(Span, Mutability)>) {
-        self.default_mode_span = old_mode_span
+    pub(super) fn leave_ref(&mut self) {
+        debug_assert!(self.innermost_deref.is_some(), "entering/leaving refs should be paired");
+        if let Some(child_ix) = self.innermost_deref {
+            self.innermost_deref = self.derefs[child_ix].parent;
+        }
     }
 
     /// Determines if a binding is relevant to the diagnostic and adjusts the notes/suggestion if
@@ -148,35 +393,109 @@ impl<'a> PatMigration<'a> {
     /// Rust 2024) or if we need to suggest a binding modifier for them.
     pub(super) fn visit_binding(
         &mut self,
-        pat_span: Span,
+        pat: &hir::Pat<'_>,
         mode: BindingMode,
         explicit_ba: BindingMode,
         ident: Ident,
     ) {
-        if explicit_ba != BindingMode::NONE
-            && let Some((default_mode_span, default_ref_mutbl)) = self.default_mode_span
-        {
+        if explicit_ba != BindingMode::NONE {
             // If this overrides a by-ref default binding mode, label the binding mode.
-            self.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 self.info.suggest_eliding_modes {
-                self.suggestion.push((pat_span.with_hi(ident.span.lo()), String::new()));
-                self.binding_mode_count += 1;
+            self.add_default_mode_label_if_needed();
+        }
+
+        // As a special case, we may simplify `&ref x` to `x`; check our parent to see if we can.
+        // The default binding mode will always be by-move in this case.
+        let simplify_deref_ref = self.innermost_deref.is_some_and(|p| {
+            self.derefs[p].simplify_deref_ref.is_some_and(|binding_id| pat.hir_id == binding_id)
+        });
+
+        // Otherwise, if `mode` doesn't match the default, we'll need to specify its binding
+        // modifiers explicitly, which in turn necessitates a by-move default binding mode.
+        // Additionally, if this is inside a macro expansion, we won't be able to change it. If a
+        // binding modifier is missing inside the expansion, there's not much we can do, but we can
+        // avoid suggestions to elide binding modifiers that are explicit within expansions.
+        let suggest = !simplify_deref_ref
+            && mode != BindingMode(self.sugg_default_mode(), Mutability::Not)
+            || pat.span.from_expansion() && explicit_ba != BindingMode::NONE;
+
+        // Track the binding
+        let span = if explicit_ba == BindingMode::NONE {
+            pat.span.shrink_to_lo()
+        } else {
+            pat.span.with_hi(ident.span.lo())
+        };
+        // If we're not already suggesting an explicit binding modifier for this binding, we may
+        // need to later, if adding reference patterns above it changes the default binding mode.
+        // In that case, track it as a child of the innermost dereference above it.
+        let parent_deref = if suggest { None } else { self.innermost_deref };
+        let next_sibling = parent_deref.and_then(|p| self.derefs[p].first_binding);
+        let bind_ix = self.bindings.push(PatBinding { span, mode, suggest, next_sibling });
+        if let Some(p) = parent_deref {
+            self.derefs[p].first_binding = Some(bind_ix);
+        }
+
+        // If there was a mismatch, add `&`s to make sure we're in a by-move default binding mode.
+        if suggest {
+            self.add_derefs_to_suggestion(self.innermost_deref);
+        }
+    }
+
+    /// Include a deref and all its ancestors in the suggestion. If this would change the mode of
+    /// a binding, we include a binding modifier for it in the suggestion, which may in turn
+    /// require including more explicit dereferences, etc.
+    fn add_derefs_to_suggestion(&mut self, mut opt_ix: Option<PatDerefIdx>) {
+        while let Some(ix) = opt_ix {
+            let deref = &mut self.derefs[ix];
+            if deref.suggest {
+                // If this is already marked as suggested, its ancestors will be too.
+                break;
             }
+            deref.suggest = true;
+            deref.sugg_default_mode = ByRef::No;
+            deref.simplify_deref_ref = None;
+            opt_ix = deref.parent;
+            let propagate_downstream_ref_mut = deref.mutbl.is_not();
+            self.propagate_default_mode_change(ix, propagate_downstream_ref_mut);
         }
-        if !self.info.suggest_eliding_modes
-            && explicit_ba.0 == ByRef::No
-            && let ByRef::Yes(mutbl) = mode.0
-        {
-            // If we can't fix the pattern by eliding modifiers, we'll need to make the pattern
-            // fully explicit. i.e. we'll need to suggest reference patterns for this.
-            let sugg_str = match mutbl {
-                Mutability::Not => "ref ",
-                Mutability::Mut => "ref mut ",
-            };
-            self.suggestion
-                .push((pat_span.with_lo(ident.span.lo()).shrink_to_lo(), sugg_str.to_owned()));
-            self.binding_mode_count += 1;
+    }
+
+    /// If including a `&` or `&mut` pattern in our suggestion would change the binding mode of any
+    /// variables, add any necessary binding modifiers and reference patterns to keep them the same.
+    fn propagate_default_mode_change(&mut self, start_ix: PatDerefIdx, propagate_ref_mut: bool) {
+        // After suggesting a deref, any immediate-child bindings will by default be by-value, so
+        // we'll need to suggest modifiers if they should be by-ref. Likewise, if suggesting a `&`
+        // changes the ref-mutability of a downstream binding under an implicit `&mut`, we'll need
+        // to add a binding modifier and `&mut` patterns.
+        let mut opt_bind_ix = self.derefs[start_ix].first_binding;
+        while let Some(bind_ix) = opt_bind_ix {
+            let binding = &mut self.bindings[bind_ix];
+            opt_bind_ix = binding.next_sibling;
+            // FIXME(ref_pat_eat_one_layer_2024_structural): With RFC 3627's Rule 3, an implicit
+            // `&mut` under a `&` pattern won't set the default binding mode to `ref mut`, so we
+            // won't need to do any mutability checks or ref-mutability propagation. We'd only call
+            // this on `&`/`&mut` patterns we suggest, not their descendants, so we can assume the
+            // default binding mode is by-move and that the deref is already suggested.
+            if binding.mode.0 != self.derefs[start_ix].sugg_default_mode {
+                binding.suggest = true;
+                self.add_derefs_to_suggestion(Some(start_ix));
+            }
+        }
+
+        // If we change an implicit dereference of a shared reference to a `&` pattern, any implicit
+        // derefs of `&mut` references in children (until we hit another implicit `&`) will now
+        // produce a `ref mut` default binding mode instead of `ref`. We'll need to recur in case
+        // any downstream bindings' modes are changed.
+        // FIXME(ref_pat_eat_one_layer_2024_structural): See the above fixme. This can all go.
+        if propagate_ref_mut {
+            let mut opt_child_ix = self.derefs[start_ix].first_child;
+            while let Some(child_ix) = opt_child_ix {
+                let child = &mut self.derefs[child_ix];
+                opt_child_ix = child.next_sibling;
+                if child.mutbl.is_mut() {
+                    child.sugg_default_mode = ByRef::Yes(Mutability::Mut);
+                    self.propagate_default_mode_change(child_ix, true);
+                }
+            }
         }
     }
 }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 8dc3f998e0916..c3e0d76dc9479 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -34,7 +34,7 @@ struct PatCtxt<'a, 'tcx> {
     typeck_results: &'a ty::TypeckResults<'tcx>,
 
     /// Used by the Rust 2024 migration lint.
-    rust_2024_migration: Option<PatMigration<'a>>,
+    rust_2024_migration: Option<PatMigration<'a, 'tcx>>,
 }
 
 pub(super) fn pat_from_hir<'a, 'tcx>(
@@ -66,11 +66,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
 
         // Track the default binding mode for the Rust 2024 migration suggestion.
-        let mut opt_old_mode_span = None;
         if let Some(s) = &mut self.rust_2024_migration
             && !adjustments.is_empty()
         {
-            opt_old_mode_span = s.visit_implicit_derefs(pat.span, adjustments);
+            s.visit_implicit_derefs(pat, adjustments);
         }
 
         // When implicit dereferences have been inserted in this pattern, the unadjusted lowered
@@ -113,7 +112,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         if let Some(s) = &mut self.rust_2024_migration
             && !adjustments.is_empty()
         {
-            s.leave_ref(opt_old_mode_span);
+            s.leave_ref();
         }
 
         adjusted_pat
@@ -307,13 +306,14 @@ 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::Ref(subpattern, mutbl) => {
                 // Track the default binding mode for the Rust 2024 migration suggestion.
-                let opt_old_mode_span =
-                    self.rust_2024_migration.as_mut().and_then(|s| s.visit_explicit_deref());
+                if let Some(s) = &mut self.rust_2024_migration {
+                    s.visit_explicit_deref(pat.span, mutbl, subpattern);
+                }
                 let subpattern = self.lower_pattern(subpattern);
                 if let Some(s) = &mut self.rust_2024_migration {
-                    s.leave_ref(opt_old_mode_span);
+                    s.leave_ref();
                 }
                 PatKind::Deref { subpattern }
             }
@@ -344,8 +344,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
                     .get(pat.hir_id)
                     .expect("missing binding mode");
 
-                if let Some(s) = &mut self.rust_2024_migration {
-                    s.visit_binding(pat.span, mode, explicit_ba, ident);
+                if let Some(m) = &mut self.rust_2024_migration {
+                    m.visit_binding(pat, mode, explicit_ba, ident);
                 }
 
                 // 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/auxiliary/migration_lint_macros.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/auxiliary/migration_lint_macros.rs
index b18f87fd56995..fcde98812270d 100644
--- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/auxiliary/migration_lint_macros.rs
+++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/auxiliary/migration_lint_macros.rs
@@ -16,3 +16,10 @@ macro_rules! bind_ref {
         ref $foo
     };
 }
+
+#[macro_export]
+macro_rules! match_ref {
+    ($p:pat) => {
+        &$p
+    };
+}
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 56125be2d6fc8..cdc707013f607 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
@@ -46,10 +46,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 LL |     let [ref x] = &[0];
    |         ^^^^^^^ this matches on type `&_`
-help: make the implied reference pattern explicit
+help: remove the unnecessary binding modifier
+   |
+LL -     let [ref x] = &[0];
+LL +     let [x] = &[0];
    |
-LL |     let &[ref x] = &[0];
-   |         +
 
 error: binding modifiers may only be written when the default binding mode is `move`
   --> $DIR/ref-binding-on-inh-ref-errors.rs:79:10
@@ -80,10 +81,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 LL |     let [ref mut x] = &mut [0];
    |         ^^^^^^^^^^^ this matches on type `&mut _`
-help: make the implied reference pattern explicit
+help: remove the unnecessary binding modifier
+   |
+LL -     let [ref mut x] = &mut [0];
+LL +     let [x] = &mut [0];
    |
-LL |     let &mut [ref mut x] = &mut [0];
-   |         ++++
 
 error: aborting due to 6 previous errors
 
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 31930e8c03371..9070f085066f8 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
@@ -10,10 +10,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 LL |     let [&ref x] = &[&0];
    |         ^^^^^^^^ this matches on type `&_`
-help: make the implied reference pattern explicit
+help: rewrite the pattern
+   |
+LL -     let [&ref x] = &[&0];
+LL +     let &[x] = &[&0];
    |
-LL |     let &[&ref x] = &[&0];
-   |         +
 
 error: binding modifiers may only be written when the default binding mode is `move`
   --> $DIR/ref-binding-on-inh-ref-errors.rs:20:11
@@ -27,10 +28,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 LL |     let [&ref x] = &mut [&0];
    |         ^^^^^^^^ this matches on type `&mut _`
-help: make the implied reference pattern explicit
+help: rewrite the pattern
+   |
+LL -     let [&ref x] = &mut [&0];
+LL +     let &mut [x] = &mut [&0];
    |
-LL |     let &mut [&ref x] = &mut [&0];
-   |         ++++
 
 error: binding modifiers may only be written when the default binding mode is `move`
   --> $DIR/ref-binding-on-inh-ref-errors.rs:25:15
@@ -61,10 +63,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 LL |     let [&mut ref mut x] = &mut [&mut 0];
    |         ^^^^^^^^^^^^^^^^ this matches on type `&mut _`
-help: make the implied reference pattern explicit
+help: rewrite the pattern
+   |
+LL -     let [&mut ref mut x] = &mut [&mut 0];
+LL +     let &mut [x] = &mut [&mut 0];
    |
-LL |     let &mut [&mut ref mut x] = &mut [&mut 0];
-   |         ++++
 
 error: binding modifiers may only be written when the default binding mode is `move`
   --> $DIR/ref-binding-on-inh-ref-errors.rs:39:11
@@ -78,10 +81,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 LL |     let [&ref x] = &[&mut 0];
    |         ^^^^^^^^ this matches on type `&_`
-help: make the implied reference pattern explicit
+help: rewrite the pattern
+   |
+LL -     let [&ref x] = &[&mut 0];
+LL +     let &[x] = &[&mut 0];
    |
-LL |     let &[&ref x] = &[&mut 0];
-   |         +
 
 error: binding modifiers may only be written when the default binding mode is `move`
   --> $DIR/ref-binding-on-inh-ref-errors.rs:45:11
@@ -95,10 +99,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 LL |     let [&ref x] = &mut [&mut 0];
    |         ^^^^^^^^ this matches on type `&mut _`
-help: make the implied reference pattern explicit
+help: rewrite the pattern
+   |
+LL -     let [&ref x] = &mut [&mut 0];
+LL +     let &mut [x] = &mut [&mut 0];
    |
-LL |     let &mut [&ref x] = &mut [&mut 0];
-   |         ++++
 
 error: binding modifiers may only be written when the default binding mode is `move`
   --> $DIR/ref-binding-on-inh-ref-errors.rs:54:15
@@ -152,10 +157,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 LL |     let [ref x] = &[0];
    |         ^^^^^^^ this matches on type `&_`
-help: make the implied reference pattern explicit
+help: remove the unnecessary binding modifier
+   |
+LL -     let [ref x] = &[0];
+LL +     let [x] = &[0];
    |
-LL |     let &[ref x] = &[0];
-   |         +
 
 error: binding modifiers may only be written when the default binding mode is `move`
   --> $DIR/ref-binding-on-inh-ref-errors.rs:79:10
@@ -186,10 +192,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 LL |     let [ref mut x] = &mut [0];
    |         ^^^^^^^^^^^ this matches on type `&mut _`
-help: make the implied reference pattern explicit
+help: remove the unnecessary binding modifier
+   |
+LL -     let [ref mut x] = &mut [0];
+LL +     let [x] = &mut [0];
    |
-LL |     let &mut [ref mut x] = &mut [0];
-   |         ++++
 
 error: aborting due to 12 previous errors
 
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 e35896f32ad7d..34eb6500ec279 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
@@ -96,7 +96,7 @@ fn main() {
         assert_type_eq(x, 0u8);
     }
 
-    if let &mut Some(&mut Some(&mut Some(ref mut x))) = &mut Some(&mut Some(&mut Some(0u8))) {
+    if let Some(Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) {
         //~^ 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);
@@ -121,7 +121,7 @@ fn main() {
     assert_type_eq(b, &&0u32);
     assert_type_eq(c, &&0u32);
 
-    if let &Struct { a: &Some(a), b: &Some(&b), c: &Some(ref c) } =
+    if let &Struct { a: &Some(a), b: &Some(&b), c: Some(c) } =
         //~^ 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) })
@@ -142,17 +142,15 @@ fn main() {
         _ => {}
     }
 
-    let &mut [&mut &[ref a]] = &mut [&mut &[0]];
+    let [[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];
+    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
@@ -169,7 +167,7 @@ fn main() {
     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 };
+    let Struct { a: [a], b: [[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);
@@ -177,13 +175,13 @@ fn main() {
     assert_type_eq(c, &0u32);
 
     // Test that we don't change bindings' types when removing reference patterns.
-    let &Foo(&ref a) = &Foo(&0);
+    let &Foo(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])]);
+    let &(&a, ref b, [c], &mut [&mut (ref 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);
@@ -201,7 +199,7 @@ fn main() {
     assert_type_eq(c, 0u32);
 
     // Test featuring both additions and removals.
-    let &(&a, &mut (ref b, &[ref c])) = &(&0, &mut (0, &[0]));
+    let &(&a, &mut (ref b, [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);
@@ -223,7 +221,7 @@ fn main() {
     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];
+    let [&Foo(a @ 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);
@@ -232,7 +230,7 @@ fn main() {
     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];
+    let [&Foo(a @ [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]);
@@ -244,4 +242,20 @@ fn main() {
     let &[migration_lint_macros::bind_ref!(a)] = &[0];
     //~^ ERROR: binding modifiers may only be written when the default binding mode is `move`
     assert_type_eq(a, &0u32);
+
+    let &[migration_lint_macros::match_ref!(a)] = &[&0];
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move`
+    assert_type_eq(a, 0u32);
+
+    // Test that we use the correct span when labeling a `&` whose subpattern is from an expansion.
+    // Also test that we don't simplify `&ref x` to `x` when the `ref` is from an expansion.
+    let &[&migration_lint_macros::bind_ref!(a)] = &[&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 simplify `&ref x` to `x` when the `&` is from an expansion.
+    let &[migration_lint_macros::match_ref!(ref a)] = &[&0];
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move`
+    assert_type_eq(a, &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 10a23e6f2fa18..ee3c6f8a49955 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
@@ -151,8 +151,6 @@ fn main() {
     //~^ 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
@@ -244,4 +242,20 @@ fn main() {
     let [migration_lint_macros::bind_ref!(a)] = &[0];
     //~^ ERROR: binding modifiers may only be written when the default binding mode is `move`
     assert_type_eq(a, &0u32);
+
+    let [migration_lint_macros::match_ref!(a)] = &[&0];
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move`
+    assert_type_eq(a, 0u32);
+
+    // Test that we use the correct span when labeling a `&` whose subpattern is from an expansion.
+    // Also test that we don't simplify `&ref x` to `x` when the `ref` is from an expansion.
+    let [&migration_lint_macros::bind_ref!(a)] = &[&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 simplify `&ref x` to `x` when the `&` is from an expansion.
+    let [migration_lint_macros::match_ref!(ref a)] = &[&0];
+    //~^ ERROR: reference patterns may only be written when the default binding mode is `move`
+    assert_type_eq(a, &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 3dd91c86a3b8f..e157b2d22b7d3 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
@@ -215,10 +215,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 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
+help: remove the unnecessary reference pattern
+   |
+LL -     if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) {
+LL +     if let Some(Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) {
    |
-LL |     if let &mut Some(&mut Some(&mut Some(ref mut x))) = &mut Some(&mut Some(&mut Some(0u8))) {
-   |            ++++                ++++      +++++++
 
 error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:111:21
@@ -273,10 +274,10 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 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
+help: make the implied reference patterns explicit
    |
-LL |     if let &Struct { a: &Some(a), b: &Some(&b), c: &Some(ref c) } =
-   |            +                         +             +     +++
+LL |     if let &Struct { a: &Some(a), b: &Some(&b), c: Some(c) } =
+   |            +                         +
 
 error: binding modifiers may only be written when the default binding mode is `move`
   --> $DIR/migration_lint.rs:137:15
@@ -318,10 +319,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 LL |     let [&mut [ref a]] = &mut [&mut &[0]];
    |         ^^^^^^^^^^^^^^ this matches on type `&mut _`
-help: make the implied reference patterns explicit
+help: remove the unnecessary reference pattern and binding modifier
+   |
+LL -     let [&mut [ref a]] = &mut [&mut &[0]];
+LL +     let [[a]] = &mut [&mut &[0]];
    |
-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
@@ -336,13 +338,14 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 LL |     let [&(_)] = &[&0];
    |         ^^^^^^ this matches on type `&_`
-help: make the implied reference pattern explicit
+help: remove the unnecessary reference pattern
+   |
+LL -     let [&(_)] = &[&0];
+LL +     let [(_)] = &[&0];
    |
-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
+  --> $DIR/migration_lint.rs:155: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
@@ -352,7 +355,7 @@ LL |     let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 };
    = 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
+  --> $DIR/migration_lint.rs:155:9
    |
 LL |     let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 };
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
@@ -363,7 +366,7 @@ 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
+  --> $DIR/migration_lint.rs:162: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
@@ -373,7 +376,7 @@ LL |     let Struct { ref a, ref mut b, c } = &mut Struct { a: 0, b: 0, c: 0 };
    = 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
+  --> $DIR/migration_lint.rs:162:9
    |
 LL |     let Struct { ref a, ref mut b, c } = &mut Struct { a: 0, b: 0, c: 0 };
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&mut _`
@@ -383,7 +386,7 @@ LL |     let &mut Struct { ref a, ref mut b, ref mut c } = &mut Struct { a: 0, b
    |         ++++                            +++++++
 
 error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
-  --> $DIR/migration_lint.rs:172:21
+  --> $DIR/migration_lint.rs:170: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
@@ -393,17 +396,18 @@ LL |     let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0],
    = 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
+  --> $DIR/migration_lint.rs:170: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
+help: remove the unnecessary reference patterns and binding modifier
+   |
+LL -     let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 };
+LL +     let Struct { a: [a], b: [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 };
    |
-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
+  --> $DIR/migration_lint.rs:178:13
    |
 LL |     let Foo(&ref a) = &Foo(&0);
    |             ^ reference pattern not allowed under `ref` default binding mode
@@ -411,17 +415,18 @@ LL |     let Foo(&ref a) = &Foo(&0);
    = 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
+  --> $DIR/migration_lint.rs:178:9
    |
 LL |     let Foo(&ref a) = &Foo(&0);
    |         ^^^^^^^^^^^ this matches on type `&_`
-help: make the implied reference pattern explicit
+help: rewrite the pattern
+   |
+LL -     let Foo(&ref a) = &Foo(&0);
+LL +     let &Foo(a) = &Foo(&0);
    |
-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
+  --> $DIR/migration_lint.rs:184:10
    |
 LL |     let (&a, b, [c], [(d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]);
    |          ^ reference pattern not allowed under `ref` default binding mode
@@ -429,17 +434,17 @@ LL |     let (&a, b, [c], [(d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]);
    = 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
+  --> $DIR/migration_lint.rs:184: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])]);
-   |         +     +++    + +++     ++++  ++++  +++    + +++
+LL |     let &(&a, ref b, [c], &mut [&mut (ref d, [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
+  --> $DIR/migration_lint.rs:194:19
    |
 LL |     let (a, [b], [mut c]) = &(0, &mut [0], &[0]);
    |                   ^^^ binding modifier not allowed under `ref` default binding mode
@@ -447,7 +452,7 @@ LL |     let (a, [b], [mut c]) = &(0, &mut [0], &[0]);
    = 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
+  --> $DIR/migration_lint.rs:194:9
    |
 LL |     let (a, [b], [mut c]) = &(0, &mut [0], &[0]);
    |         ^^^^^^^^^^^^^^^^^ this matches on type `&_`
@@ -457,7 +462,7 @@ 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
+  --> $DIR/migration_lint.rs:202:10
    |
 LL |     let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0]));
    |          ^       ^ reference pattern not allowed under `ref` default binding mode
@@ -467,17 +472,18 @@ LL |     let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0]));
    = 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
+  --> $DIR/migration_lint.rs:202: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
+help: rewrite the pattern
+   |
+LL -     let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0]));
+LL +     let &(&a, &mut (ref b, [c])) = &(&0, &mut (0, &[0]));
    |
-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
+  --> $DIR/migration_lint.rs:210:10
    |
 LL |     let [mut a @ b] = &[0];
    |          ^^^ binding modifier not allowed under `ref` default binding mode
@@ -485,7 +491,7 @@ LL |     let [mut a @ b] = &[0];
    = 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
+  --> $DIR/migration_lint.rs:210:9
    |
 LL |     let [mut a @ b] = &[0];
    |         ^^^^^^^^^^^ this matches on type `&_`
@@ -495,7 +501,7 @@ 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
+  --> $DIR/migration_lint.rs:217:14
    |
 LL |     let [a @ mut b] = &[0];
    |              ^^^ binding modifier not allowed under `ref` default binding mode
@@ -503,7 +509,7 @@ LL |     let [a @ mut b] = &[0];
    = 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
+  --> $DIR/migration_lint.rs:217:9
    |
 LL |     let [a @ mut b] = &[0];
    |         ^^^^^^^^^^^ this matches on type `&_`
@@ -513,7 +519,7 @@ 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
+  --> $DIR/migration_lint.rs:224:14
    |
 LL |     let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2];
    |              ^                    ^ reference pattern not allowed under `ref` default binding mode
@@ -523,22 +529,23 @@ LL |     let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2];
    = 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
+  --> $DIR/migration_lint.rs:224: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
+  --> $DIR/migration_lint.rs:224: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
+help: rewrite the pattern
+   |
+LL -     let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2];
+LL +     let [&Foo(a @ b), &Foo(&ref c @ d)] = [&Foo(&0); 2];
    |
-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
+  --> $DIR/migration_lint.rs:233:14
    |
 LL |     let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
    |              ^                      ^ reference pattern not allowed under `ref` default binding mode
@@ -548,29 +555,30 @@ LL |     let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
    = 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
+  --> $DIR/migration_lint.rs:233: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
+  --> $DIR/migration_lint.rs:233: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
+help: rewrite the pattern
+   |
+LL -     let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
+LL +     let [&Foo(a @ [b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
    |
-LL |     let [&Foo(&ref a @ [ref b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
-   |          +                       +
 
 error: binding modifiers may only be written when the default binding mode is `move`
-  --> $DIR/migration_lint.rs:244:10
+  --> $DIR/migration_lint.rs:242:10
    |
 LL |     let [migration_lint_macros::bind_ref!(a)] = &[0];
    |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ occurs within macro expansion
    |
    = 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:244:9
+  --> $DIR/migration_lint.rs:242:9
    |
 LL |     let [migration_lint_macros::bind_ref!(a)] = &[0];
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
@@ -580,5 +588,59 @@ help: make the implied reference pattern explicit
 LL |     let &[migration_lint_macros::bind_ref!(a)] = &[0];
    |         +
 
-error: aborting due to 30 previous errors
+error: reference patterns may only be written when the default binding mode is `move`
+  --> $DIR/migration_lint.rs:246:10
+   |
+LL |     let [migration_lint_macros::match_ref!(a)] = &[&0];
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ occurs within macro expansion
+   |
+   = 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:246:9
+   |
+LL |     let [migration_lint_macros::match_ref!(a)] = &[&0];
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
+   = note: this error originates in the macro `migration_lint_macros::match_ref` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: make the implied reference pattern explicit
+   |
+LL |     let &[migration_lint_macros::match_ref!(a)] = &[&0];
+   |         +
+
+error: reference patterns may only be written when the default binding mode is `move` in Rust 2024
+  --> $DIR/migration_lint.rs:252:10
+   |
+LL |     let [&migration_lint_macros::bind_ref!(a)] = &[&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:252:9
+   |
+LL |     let [&migration_lint_macros::bind_ref!(a)] = &[&0];
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
+help: make the implied reference pattern explicit
+   |
+LL |     let &[&migration_lint_macros::bind_ref!(a)] = &[&0];
+   |         +
+
+error: reference patterns may only be written when the default binding mode is `move`
+  --> $DIR/migration_lint.rs:258:10
+   |
+LL |     let [migration_lint_macros::match_ref!(ref a)] = &[&0];
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ occurs within macro expansion
+   |
+   = 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:258:9
+   |
+LL |     let [migration_lint_macros::match_ref!(ref a)] = &[&0];
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_`
+   = note: this error originates in the macro `migration_lint_macros::match_ref` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: make the implied reference pattern explicit
+   |
+LL |     let &[migration_lint_macros::match_ref!(ref a)] = &[&0];
+   |         +
+
+error: aborting due to 33 previous errors