Skip to content

Commit 909010e

Browse files
committed
try to suggest eliding redundant binding modifiers
(cherry picked from commit b32a533)
1 parent 2f29577 commit 909010e

File tree

7 files changed

+82
-49
lines changed

7 files changed

+82
-49
lines changed

compiler/rustc_hir_typeck/src/pat.rs

+21-2
Original file line numberDiff line numberDiff line change
@@ -2651,7 +2651,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26512651

26522652
let mut typeck_results = self.typeck_results.borrow_mut();
26532653
let mut table = typeck_results.rust_2024_migration_desugared_pats_mut();
2654-
let info = table.entry(pat_id).or_default();
2654+
// FIXME(ref_pat_eat_one_layer_2024): The migration diagnostic doesn't know how to track the
2655+
// default binding mode in the presence of Rule 3 or Rule 5. As a consequence, the labels it
2656+
// gives for default binding modes are wrong, as well as suggestions based on the default
2657+
// binding mode. This keeps it from making those suggestions, as doing so could panic.
2658+
let info = table.entry(pat_id).or_insert_with(|| ty::Rust2024IncompatiblePatInfo {
2659+
primary_labels: Vec::new(),
2660+
bad_modifiers: false,
2661+
bad_ref_pats: false,
2662+
suggest_eliding_modes: !self.tcx.features().ref_pat_eat_one_layer_2024()
2663+
&& !self.tcx.features().ref_pat_eat_one_layer_2024_structural(),
2664+
});
26552665

26562666
// Only provide a detailed label if the problematic subpattern isn't from an expansion.
26572667
// 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> {
26622672
// so, we may want to inspect the span's source callee or macro backtrace.
26632673
"occurs within macro expansion".to_owned()
26642674
} else {
2665-
let pat_kind = if matches!(subpat.kind, PatKind::Binding(_, _, _, _)) {
2675+
let pat_kind = if let PatKind::Binding(user_bind_annot, _, _, _) = subpat.kind {
26662676
info.bad_modifiers |= true;
2677+
// If the user-provided binding modifier doesn't match the default binding mode, we'll
2678+
// need to suggest reference patterns, which can affect other bindings.
2679+
// For simplicity, we opt to suggest making the pattern fully explicit.
2680+
info.suggest_eliding_modes &=
2681+
user_bind_annot == BindingMode(ByRef::Yes(def_br_mutbl), Mutability::Not);
26672682
"binding modifier"
26682683
} else {
26692684
info.bad_ref_pats |= true;
2685+
// For simplicity, we don't try to suggest eliding reference patterns. Thus, we'll
2686+
// suggest adding them instead, which can affect the types assigned to bindings.
2687+
// As such, we opt to suggest making the pattern fully explicit.
2688+
info.suggest_eliding_modes = false;
26702689
"reference pattern"
26712690
};
26722691
let dbm_str = match def_br_mutbl {

compiler/rustc_middle/src/ty/typeck_results.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -814,12 +814,14 @@ impl<'tcx> std::fmt::Display for UserTypeKind<'tcx> {
814814

815815
/// Information on a pattern incompatible with Rust 2024, for use by the error/migration diagnostic
816816
/// emitted during THIR construction.
817-
#[derive(TyEncodable, TyDecodable, Debug, HashStable, Default)]
817+
#[derive(TyEncodable, TyDecodable, Debug, HashStable)]
818818
pub struct Rust2024IncompatiblePatInfo {
819819
/// Labeled spans for `&`s, `&mut`s, and binding modifiers incompatible with Rust 2024.
820820
pub primary_labels: Vec<(Span, String)>,
821821
/// Whether any binding modifiers occur under a non-`move` default binding mode.
822822
pub bad_modifiers: bool,
823823
/// Whether any `&` or `&mut` patterns occur under a non-`move` default binding mode.
824824
pub bad_ref_pats: bool,
825+
/// If `true`, we can give a simpler suggestion solely by eliding explicit binding modifiers.
826+
pub suggest_eliding_modes: bool,
825827
}

compiler/rustc_mir_build/src/errors.rs

+14-9
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,9 @@ pub(crate) struct Rust2024IncompatiblePat {
10981098
}
10991099

11001100
pub(crate) struct Rust2024IncompatiblePatSugg {
1101+
/// If true, our suggestion is to elide explicit binding modifiers.
1102+
/// If false, our suggestion is to make the pattern fully explicit.
1103+
pub(crate) suggest_eliding_modes: bool,
11011104
pub(crate) suggestion: Vec<(Span, String)>,
11021105
pub(crate) ref_pattern_count: usize,
11031106
pub(crate) binding_mode_count: usize,
@@ -1135,16 +1138,18 @@ impl Subdiagnostic for Rust2024IncompatiblePatSugg {
11351138
} else {
11361139
Applicability::MaybeIncorrect
11371140
};
1138-
let plural_derefs = pluralize!(self.ref_pattern_count);
1139-
let and_modes = if self.binding_mode_count > 0 {
1140-
format!(" and variable binding mode{}", pluralize!(self.binding_mode_count))
1141+
let msg = if self.suggest_eliding_modes {
1142+
let plural_modes = pluralize!(self.binding_mode_count);
1143+
format!("remove the unnecessary binding modifier{plural_modes}")
11411144
} else {
1142-
String::new()
1145+
let plural_derefs = pluralize!(self.ref_pattern_count);
1146+
let and_modes = if self.binding_mode_count > 0 {
1147+
format!(" and variable binding mode{}", pluralize!(self.binding_mode_count))
1148+
} else {
1149+
String::new()
1150+
};
1151+
format!("make the implied reference pattern{plural_derefs}{and_modes} explicit")
11431152
};
1144-
diag.multipart_suggestion_verbose(
1145-
format!("make the implied reference pattern{plural_derefs}{and_modes} explicit"),
1146-
self.suggestion,
1147-
applicability,
1148-
);
1153+
diag.multipart_suggestion_verbose(msg, self.suggestion, applicability);
11491154
}
11501155
}

compiler/rustc_mir_build/src/thir/pattern/mod.rs

+31-27
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,16 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
4949
tcx,
5050
typing_env,
5151
typeck_results,
52-
rust_2024_migration_suggestion: migration_info.and(Some(Rust2024IncompatiblePatSugg {
53-
suggestion: Vec::new(),
54-
ref_pattern_count: 0,
55-
binding_mode_count: 0,
56-
default_mode_span: None,
57-
default_mode_labels: Default::default(),
58-
})),
52+
rust_2024_migration_suggestion: migration_info.and_then(|info| {
53+
Some(Rust2024IncompatiblePatSugg {
54+
suggest_eliding_modes: info.suggest_eliding_modes,
55+
suggestion: Vec::new(),
56+
ref_pattern_count: 0,
57+
binding_mode_count: 0,
58+
default_mode_span: None,
59+
default_mode_labels: Default::default(),
60+
})
61+
}),
5962
};
6063
let result = pcx.lower_pattern(pat);
6164
debug!("pat_from_hir({:?}) = {:?}", pat, result);
@@ -107,27 +110,22 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
107110
if let Some(s) = &mut self.rust_2024_migration_suggestion
108111
&& !adjustments.is_empty()
109112
{
110-
let mut min_mutbl = Mutability::Mut;
111-
let suggestion_str: String = adjustments
112-
.iter()
113-
.map(|ref_ty| {
114-
let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
115-
span_bug!(pat.span, "pattern implicitly dereferences a non-ref type");
116-
};
117-
118-
match mutbl {
119-
Mutability::Not => {
120-
min_mutbl = Mutability::Not;
121-
"&"
122-
}
123-
Mutability::Mut => "&mut ",
124-
}
125-
})
126-
.collect();
127-
s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
128-
s.ref_pattern_count += adjustments.len();
113+
let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| {
114+
let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
115+
span_bug!(pat.span, "pattern implicitly dereferences a non-ref type");
116+
};
117+
mutbl
118+
});
119+
120+
if !s.suggest_eliding_modes {
121+
let suggestion_str: String =
122+
implicit_deref_mutbls.clone().map(|mutbl| mutbl.ref_prefix_str()).collect();
123+
s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
124+
s.ref_pattern_count += adjustments.len();
125+
}
129126

130127
// Remember if this changed the default binding mode, in case we want to label it.
128+
let min_mutbl = implicit_deref_mutbls.min().unwrap();
131129
if s.default_mode_span.is_none_or(|(_, old_mutbl)| min_mutbl < old_mutbl) {
132130
opt_old_mode_span = Some(s.default_mode_span);
133131
s.default_mode_span = Some((pat.span, min_mutbl));
@@ -423,8 +421,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
423421
{
424422
// If this overrides a by-ref default binding mode, label the binding mode.
425423
s.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
424+
// If our suggestion is to elide redundnt modes, this will be one of them.
425+
if s.suggest_eliding_modes {
426+
s.suggestion.push((pat.span.with_hi(ident.span.lo()), String::new()));
427+
s.binding_mode_count += 1;
428+
}
426429
}
427-
if explicit_ba.0 == ByRef::No
430+
if !s.suggest_eliding_modes
431+
&& explicit_ba.0 == ByRef::No
428432
&& let ByRef::Yes(mutbl) = mode.0
429433
{
430434
let sugg_str = match mutbl {

tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ fn main() {
3232
//~| WARN: this changes meaning in Rust 2024
3333
assert_type_eq(x, 0u8);
3434

35-
let &Foo(ref x) = &Foo(0);
35+
let Foo(x) = &Foo(0);
3636
//~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
3737
//~| WARN: this changes meaning in Rust 2024
3838
assert_type_eq(x, &0u8);

tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr

+4-3
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
5252
|
5353
LL | let Foo(ref x) = &Foo(0);
5454
| ^^^^^^^^^^ this matches on type `&_`
55-
help: make the implied reference pattern explicit
55+
help: remove the unnecessary binding modifier
56+
|
57+
LL - let Foo(ref x) = &Foo(0);
58+
LL + let Foo(x) = &Foo(0);
5659
|
57-
LL | let &Foo(ref x) = &Foo(0);
58-
| +
5960

6061
error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
6162
--> $DIR/migration_lint.rs:40:13

tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr

+8-6
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,11 @@ note: matching on a reference type with a non-reference pattern changes the defa
179179
|
180180
LL | test_pat_on_type![(ref x,): &(T,)];
181181
| ^^^^^^^^ this matches on type `&_`
182-
help: make the implied reference pattern explicit
182+
help: remove the unnecessary binding modifier
183+
|
184+
LL - test_pat_on_type![(ref x,): &(T,)];
185+
LL + test_pat_on_type![(x,): &(T,)];
183186
|
184-
LL | test_pat_on_type![&(ref x,): &(T,)];
185-
| +
186187

187188
error: binding modifiers may only be written when the default binding mode is `move`
188189
--> $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
196197
|
197198
LL | test_pat_on_type![(ref mut x,): &mut (T,)];
198199
| ^^^^^^^^^^^^ this matches on type `&mut _`
199-
help: make the implied reference pattern explicit
200+
help: remove the unnecessary binding modifier
201+
|
202+
LL - test_pat_on_type![(ref mut x,): &mut (T,)];
203+
LL + test_pat_on_type![(x,): &mut (T,)];
200204
|
201-
LL | test_pat_on_type![&mut (ref mut x,): &mut (T,)];
202-
| ++++
203205

204206
error: reference patterns may only be written when the default binding mode is `move`
205207
--> $DIR/min_match_ergonomics_fail.rs:43:10

0 commit comments

Comments
 (0)