summary refs log tree commit diff
diff options
context:
space:
mode:
authordianne <diannes.gm@gmail.com>2025-02-05 04:05:01 -0800
committerdianne <diannes.gm@gmail.com>2025-02-10 23:40:02 -0800
commit909010ebe614fa32c2cd4def33ae1009f194a0b0 (patch)
tree5c02490e14bb40906c506291b5bb59fdd6acaae7
parent2f295779eee42aa0d904c956524c7ad8aa8f06b0 (diff)
downloadrust-909010ebe614fa32c2cd4def33ae1009f194a0b0.tar.gz
rust-909010ebe614fa32c2cd4def33ae1009f194a0b0.zip
try to suggest eliding redundant binding modifiers
(cherry picked from commit b32a5331dcdcc1993fefeff412a20766557e558d)
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs23
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs4
-rw-r--r--compiler/rustc_mir_build/src/errors.rs23
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs58
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed2
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr7
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr14
7 files changed, 82 insertions, 49 deletions
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 3e4b50ff528..fdb50bf4911 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -2651,7 +2651,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let mut typeck_results = self.typeck_results.borrow_mut();
         let mut table = typeck_results.rust_2024_migration_desugared_pats_mut();
-        let info = table.entry(pat_id).or_default();
+        // 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.
@@ -2662,11 +2672,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // so, we may want to inspect the span's source callee or macro backtrace.
             "occurs within macro expansion".to_owned()
         } else {
-            let pat_kind = if matches!(subpat.kind, PatKind::Binding(_, _, _, _)) {
+            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 {
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index c09418c0ef1..df341335208 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -814,7 +814,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, Default)]
+#[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)>,
@@ -822,4 +822,6 @@ 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 31cf02124e8..e2be6975438 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -1098,6 +1098,9 @@ pub(crate) struct Rust2024IncompatiblePat {
 }
 
 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,
@@ -1135,16 +1138,18 @@ impl Subdiagnostic for Rust2024IncompatiblePatSugg {
             } 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,
-        );
+        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 24a7fbe4083..d09017784b1 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -49,13 +49,16 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
         tcx,
         typing_env,
         typeck_results,
-        rust_2024_migration_suggestion: migration_info.and(Some(Rust2024IncompatiblePatSugg {
-            suggestion: Vec::new(),
-            ref_pattern_count: 0,
-            binding_mode_count: 0,
-            default_mode_span: None,
-            default_mode_labels: Default::default(),
-        })),
+        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,
+                default_mode_span: None,
+                default_mode_labels: Default::default(),
+            })
+        }),
     };
     let result = pcx.lower_pattern(pat);
     debug!("pat_from_hir({:?}) = {:?}", pat, result);
@@ -107,27 +110,22 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         if let Some(s) = &mut self.rust_2024_migration_suggestion
             && !adjustments.is_empty()
         {
-            let mut min_mutbl = Mutability::Mut;
-            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 {
-                        Mutability::Not => {
-                            min_mutbl = Mutability::Not;
-                            "&"
-                        }
-                        Mutability::Mut => "&mut ",
-                    }
-                })
-                .collect();
-            s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
-            s.ref_pattern_count += adjustments.len();
+            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));
@@ -423,8 +421,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
                     {
                         // 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 explicit_ba.0 == ByRef::No
+                    if !s.suggest_eliding_modes
+                        && explicit_ba.0 == ByRef::No
                         && let ByRef::Yes(mutbl) = mode.0
                     {
                         let sugg_str = match mutbl {
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 911b42c9ddf..9ddc8912a51 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
@@ -32,7 +32,7 @@ fn main() {
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
-    let &Foo(ref x) = &Foo(0);
+    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);
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 ea3283d3a92..c36f92ecc7e 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
@@ -52,10 +52,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 LL |     let Foo(ref x) = &Foo(0);
    |         ^^^^^^^^^^ this matches on type `&_`
-help: make the implied reference pattern explicit
+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: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
   --> $DIR/migration_lint.rs:40:13
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 aaa448ea334..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
@@ -179,10 +179,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 LL | test_pat_on_type![(ref x,): &(T,)];
    |                   ^^^^^^^^ this matches on type `&_`
-help: make the implied reference pattern explicit
+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: binding modifiers may only be written when the default binding mode is `move`
   --> $DIR/min_match_ergonomics_fail.rs:34:20
@@ -196,10 +197,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
    |
 LL | test_pat_on_type![(ref mut x,): &mut (T,)];
    |                   ^^^^^^^^^^^^ this matches on type `&mut _`
-help: make the implied reference pattern explicit
+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: reference patterns may only be written when the default binding mode is `move`
   --> $DIR/min_match_ergonomics_fail.rs:43:10