Skip to content

Commit 1a18daa

Browse files
committed
reword pattern migration diagnostic to make sense in all editions
This aligns the main error message a bit more with the phrasing in the Edition Guide and provides a bit more information on the labels to (hopefully!) aid in understanding. (cherry picked from commit bdc6c4d)
1 parent acec586 commit 1a18daa

File tree

11 files changed

+164
-125
lines changed

11 files changed

+164
-125
lines changed

compiler/rustc_hir_typeck/src/pat.rs

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
699699

700700
// Determine the binding mode...
701701
let bm = match user_bind_annot {
702-
BindingMode(ByRef::No, Mutability::Mut) if matches!(def_br, ByRef::Yes(_)) => {
702+
BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(def_br_mutbl) = def_br => {
703703
if pat.span.at_least_rust_2024()
704704
&& (self.tcx.features().ref_pat_eat_one_layer_2024()
705705
|| self.tcx.features().ref_pat_eat_one_layer_2024_structural())
@@ -719,22 +719,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
719719
// `mut` resets the binding mode on edition <= 2021
720720
self.add_rust_2024_migration_desugared_pat(
721721
pat_info.top_info.hir_id,
722-
pat.span,
722+
pat,
723723
ident.span,
724-
"requires binding by-value, but the implicit default is by-reference",
724+
def_br_mutbl,
725725
);
726726
BindingMode(ByRef::No, Mutability::Mut)
727727
}
728728
}
729729
BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
730730
BindingMode(ByRef::Yes(_), _) => {
731-
if matches!(def_br, ByRef::Yes(_)) {
731+
if let ByRef::Yes(def_br_mutbl) = def_br {
732732
// `ref`/`ref mut` overrides the binding mode on edition <= 2021
733733
self.add_rust_2024_migration_desugared_pat(
734734
pat_info.top_info.hir_id,
735-
pat.span,
735+
pat,
736736
ident.span,
737-
"cannot override to bind by-reference when that is the implicit default",
737+
def_br_mutbl,
738738
);
739739
}
740740
user_bind_annot
@@ -2263,13 +2263,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22632263
}
22642264
} else {
22652265
// Reset binding mode on old editions
2266-
if pat_info.binding_mode != ByRef::No {
2266+
if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
22672267
pat_info.binding_mode = ByRef::No;
22682268
self.add_rust_2024_migration_desugared_pat(
22692269
pat_info.top_info.hir_id,
2270-
pat.span,
2270+
pat,
22712271
inner.span,
2272-
"cannot implicitly match against multiple layers of reference",
2272+
inh_mut,
22732273
)
22742274
}
22752275
}
@@ -2635,33 +2635,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26352635
fn add_rust_2024_migration_desugared_pat(
26362636
&self,
26372637
pat_id: HirId,
2638-
subpat_span: Span,
2638+
subpat: &'tcx Pat<'tcx>,
26392639
cutoff_span: Span,
2640-
detailed_label: &str,
2640+
def_br_mutbl: Mutability,
26412641
) {
26422642
// Try to trim the span we're labeling to just the `&` or binding mode that's an issue.
26432643
// If the subpattern's span is is from an expansion, the emitted label will not be trimmed.
26442644
let source_map = self.tcx.sess.source_map();
26452645
let cutoff_span = source_map
26462646
.span_extend_prev_while(cutoff_span, char::is_whitespace)
26472647
.unwrap_or(cutoff_span);
2648-
// Ensure we use the syntax context and thus edition of `subpat_span`; this will be a hard
2648+
// Ensure we use the syntax context and thus edition of `subpat.span`; this will be a hard
26492649
// error if the subpattern is of edition >= 2024.
2650-
let trimmed_span = subpat_span.until(cutoff_span).with_ctxt(subpat_span.ctxt());
2650+
let trimmed_span = subpat.span.until(cutoff_span).with_ctxt(subpat.span.ctxt());
26512651

26522652
// Only provide a detailed label if the problematic subpattern isn't from an expansion.
26532653
// In the case that it's from a macro, we'll add a more detailed note in the emitter.
2654-
let desc = if subpat_span.from_expansion() {
2655-
"default binding mode is reset within expansion"
2654+
let desc = if subpat.span.from_expansion() {
2655+
"occurs within expansion"
26562656
} else {
2657-
detailed_label
2657+
match def_br_mutbl {
2658+
Mutability::Not => "default binding mode is `ref`",
2659+
Mutability::Mut => "default binding mode is `ref mut`",
2660+
}
26582661
};
26592662

2660-
self.typeck_results
2661-
.borrow_mut()
2662-
.rust_2024_migration_desugared_pats_mut()
2663-
.entry(pat_id)
2664-
.or_default()
2665-
.push((trimmed_span, desc.to_owned()));
2663+
let mut typeck_results = self.typeck_results.borrow_mut();
2664+
let mut table = typeck_results.rust_2024_migration_desugared_pats_mut();
2665+
let info = table.entry(pat_id).or_default();
2666+
2667+
info.labels.push((trimmed_span, desc.to_owned()));
2668+
if matches!(subpat.kind, PatKind::Binding(_, _, _, _)) {
2669+
info.bad_modifiers |= true;
2670+
} else {
2671+
info.bad_ref_pats |= true;
2672+
}
26662673
}
26672674
}

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ pub use self::sty::{
9595
pub use self::trait_def::TraitDef;
9696
pub use self::typeck_results::{
9797
CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, IsIdentity,
98-
TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind,
98+
Rust2024IncompatiblePatInfo, TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind,
9999
};
100100
pub use self::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
101101
use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason};

compiler/rustc_middle/src/ty/typeck_results.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ pub struct TypeckResults<'tcx> {
7373
/// Stores the actual binding mode for all instances of [`BindingMode`].
7474
pat_binding_modes: ItemLocalMap<BindingMode>,
7575

76-
/// Top-level patterns whose match ergonomics need to be desugared by the Rust 2021 -> 2024
77-
/// migration lint. Problematic subpatterns are stored in the `Vec` for the lint to highlight.
78-
rust_2024_migration_desugared_pats: ItemLocalMap<Vec<(Span, String)>>,
76+
/// Top-level patterns incompatible with Rust 2024's match ergonomics. These will be translated
77+
/// to a form valid in all Editions, either as a lint diagnostic or hard error.
78+
rust_2024_migration_desugared_pats: ItemLocalMap<Rust2024IncompatiblePatInfo>,
7979

8080
/// Stores the types which were implicitly dereferenced in pattern binding modes
8181
/// for later usage in THIR lowering. For example,
@@ -420,7 +420,7 @@ impl<'tcx> TypeckResults<'tcx> {
420420

421421
pub fn rust_2024_migration_desugared_pats(
422422
&self,
423-
) -> LocalTableInContext<'_, Vec<(Span, String)>> {
423+
) -> LocalTableInContext<'_, Rust2024IncompatiblePatInfo> {
424424
LocalTableInContext {
425425
hir_owner: self.hir_owner,
426426
data: &self.rust_2024_migration_desugared_pats,
@@ -429,7 +429,7 @@ impl<'tcx> TypeckResults<'tcx> {
429429

430430
pub fn rust_2024_migration_desugared_pats_mut(
431431
&mut self,
432-
) -> LocalTableInContextMut<'_, Vec<(Span, String)>> {
432+
) -> LocalTableInContextMut<'_, Rust2024IncompatiblePatInfo> {
433433
LocalTableInContextMut {
434434
hir_owner: self.hir_owner,
435435
data: &mut self.rust_2024_migration_desugared_pats,
@@ -811,3 +811,15 @@ impl<'tcx> std::fmt::Display for UserTypeKind<'tcx> {
811811
}
812812
}
813813
}
814+
815+
/// Information on a pattern incompatible with Rust 2024, for use by the error/migration diagnostic
816+
/// emitted during THIR construction.
817+
#[derive(TyEncodable, TyDecodable, Debug, HashStable, Default)]
818+
pub struct Rust2024IncompatiblePatInfo {
819+
/// Labels for subpatterns incompatible with Rust 2024.
820+
pub labels: Vec<(Span, String)>,
821+
/// Whether any binding modifiers occur under a non-`move` default binding mode.
822+
pub bad_modifiers: bool,
823+
/// Whether any `&` or `&mut` patterns occur under a non-`move` default binding mode.
824+
pub bad_ref_pats: bool,
825+
}

compiler/rustc_mir_build/messages.ftl

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,16 @@ mir_build_pointer_pattern = function pointers and raw pointers not derived from
285285
286286
mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
287287
288-
mir_build_rust_2024_incompatible_pat = this pattern relies on behavior which may change in edition 2024
288+
mir_build_rust_2024_incompatible_pat = {$bad_modifiers ->
289+
*[true] binding modifiers{$bad_ref_pats ->
290+
*[true] {" "}and reference patterns
291+
[false] {""}
292+
}
293+
[false] reference patterns
294+
} may only be written when the default binding mode is `move`{$is_hard_error ->
295+
*[true] {""}
296+
[false] {" "}in Rust 2024
297+
}
289298
290299
mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly
291300
.attributes = no other attributes may be applied

compiler/rustc_mir_build/src/errors.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,9 @@ pub(crate) enum RustcBoxAttrReason {
10911091
pub(crate) struct Rust2024IncompatiblePat {
10921092
#[subdiagnostic]
10931093
pub(crate) sugg: Rust2024IncompatiblePatSugg,
1094+
pub(crate) bad_modifiers: bool,
1095+
pub(crate) bad_ref_pats: bool,
1096+
pub(crate) is_hard_error: bool,
10941097
}
10951098

10961099
pub(crate) struct Rust2024IncompatiblePatSugg {

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,42 +44,50 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
4444
typeck_results: &'a ty::TypeckResults<'tcx>,
4545
pat: &'tcx hir::Pat<'tcx>,
4646
) -> Box<Pat<'tcx>> {
47-
let migration_labels = typeck_results.rust_2024_migration_desugared_pats().get(pat.hir_id);
47+
let migration_info = typeck_results.rust_2024_migration_desugared_pats().get(pat.hir_id);
4848
let mut pcx = PatCtxt {
4949
tcx,
5050
typing_env,
5151
typeck_results,
52-
rust_2024_migration_suggestion: migration_labels.and(Some(Rust2024IncompatiblePatSugg {
52+
rust_2024_migration_suggestion: migration_info.and(Some(Rust2024IncompatiblePatSugg {
5353
suggestion: Vec::new(),
5454
ref_pattern_count: 0,
5555
binding_mode_count: 0,
5656
})),
5757
};
5858
let result = pcx.lower_pattern(pat);
5959
debug!("pat_from_hir({:?}) = {:?}", pat, result);
60-
if let Some(labels) = migration_labels {
60+
if let Some(info) = migration_info {
6161
let sugg = pcx.rust_2024_migration_suggestion.expect("suggestion should be present");
62-
let mut spans = MultiSpan::from_spans(labels.iter().map(|(span, _)| *span).collect());
63-
for (span, label) in labels {
62+
let mut spans = MultiSpan::from_spans(info.labels.iter().map(|(span, _)| *span).collect());
63+
for (span, label) in &info.labels {
6464
spans.push_span_label(*span, label.clone());
6565
}
6666
// If a relevant span is from at least edition 2024, this is a hard error.
6767
let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());
6868
if is_hard_error {
6969
let mut err =
7070
tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);
71-
if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
71+
if let Some(lint_info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
7272
// provide the same reference link as the lint
73-
err.note(format!("for more information, see {}", info.reference));
73+
err.note(format!("for more information, see {}", lint_info.reference));
7474
}
75+
err.arg("bad_modifiers", info.bad_modifiers);
76+
err.arg("bad_ref_pats", info.bad_ref_pats);
77+
err.arg("is_hard_error", true);
7578
err.subdiagnostic(sugg);
7679
err.emit();
7780
} else {
7881
tcx.emit_node_span_lint(
7982
lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
8083
pat.hir_id,
8184
spans,
82-
Rust2024IncompatiblePat { sugg },
85+
Rust2024IncompatiblePat {
86+
sugg,
87+
bad_modifiers: info.bad_modifiers,
88+
bad_ref_pats: info.bad_ref_pats,
89+
is_hard_error,
90+
},
8391
);
8492
}
8593
}

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

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,22 @@ fn main() {
2323
assert_type_eq(x, &mut 0u8);
2424

2525
let &Foo(mut x) = &Foo(0);
26-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
26+
//~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
2727
//~| WARN: this changes meaning in Rust 2024
2828
assert_type_eq(x, 0u8);
2929

3030
let &mut Foo(mut x) = &mut Foo(0);
31-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
31+
//~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
3232
//~| WARN: this changes meaning in Rust 2024
3333
assert_type_eq(x, 0u8);
3434

3535
let &Foo(ref x) = &Foo(0);
36-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
36+
//~^ 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);
3939

4040
let &mut Foo(ref x) = &mut Foo(0);
41-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
41+
//~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
4242
//~| WARN: this changes meaning in Rust 2024
4343
assert_type_eq(x, &0u8);
4444

@@ -55,22 +55,22 @@ fn main() {
5555
assert_type_eq(x, &0u8);
5656

5757
let &Foo(&x) = &Foo(&0);
58-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
58+
//~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
5959
//~| WARN: this changes meaning in Rust 2024
6060
assert_type_eq(x, 0u8);
6161

6262
let &Foo(&mut x) = &Foo(&mut 0);
63-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
63+
//~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
6464
//~| WARN: this changes meaning in Rust 2024
6565
assert_type_eq(x, 0u8);
6666

6767
let &mut Foo(&x) = &mut Foo(&0);
68-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
68+
//~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
6969
//~| WARN: this changes meaning in Rust 2024
7070
assert_type_eq(x, 0u8);
7171

7272
let &mut Foo(&mut x) = &mut Foo(&mut 0);
73-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
73+
//~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
7474
//~| WARN: this changes meaning in Rust 2024
7575
assert_type_eq(x, 0u8);
7676

@@ -79,25 +79,25 @@ fn main() {
7979
}
8080

8181
if let &&&&&Some(&x) = &&&&&Some(&0u8) {
82-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
82+
//~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
8383
//~| WARN: this changes meaning in Rust 2024
8484
assert_type_eq(x, 0u8);
8585
}
8686

8787
if let &&&&&Some(&mut x) = &&&&&Some(&mut 0u8) {
88-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
88+
//~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
8989
//~| WARN: this changes meaning in Rust 2024
9090
assert_type_eq(x, 0u8);
9191
}
9292

9393
if let &&&&&mut Some(&x) = &&&&&mut Some(&0u8) {
94-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
94+
//~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
9595
//~| WARN: this changes meaning in Rust 2024
9696
assert_type_eq(x, 0u8);
9797
}
9898

9999
if let &mut Some(&mut Some(&mut Some(ref mut x))) = &mut Some(&mut Some(&mut Some(0u8))) {
100-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
100+
//~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
101101
//~| WARN: this changes meaning in Rust 2024
102102
assert_type_eq(x, &mut 0u8);
103103
}
@@ -109,20 +109,20 @@ fn main() {
109109
}
110110

111111
let &Struct { ref a, mut b, ref c } = &Struct { a: 0, b: 0, c: 0 };
112-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
112+
//~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024
113113
//~| WARN: this changes meaning in Rust 2024
114114
assert_type_eq(a, &0u32);
115115
assert_type_eq(b, 0u32);
116116

117117
let &Struct { a: &a, ref b, ref c } = &Struct { a: &0, b: &0, c: &0 };
118-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
118+
//~^ ERROR: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024
119119
//~| WARN: this changes meaning in Rust 2024
120120
assert_type_eq(a, 0u32);
121121
assert_type_eq(b, &&0u32);
122122
assert_type_eq(c, &&0u32);
123123

124124
if let &Struct { a: &Some(a), b: &Some(&b), c: &Some(ref c) } =
125-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
125+
//~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024
126126
//~| WARN: this changes meaning in Rust 2024
127127
&(Struct { a: &Some(&0), b: &Some(&0), c: &Some(&0) })
128128
{
@@ -135,7 +135,7 @@ fn main() {
135135
// The two patterns are the same syntactically, but because they're defined in different
136136
// editions they don't mean the same thing.
137137
&(Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => {
138-
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
138+
//~^ ERROR: binding modifiers may only be written when the default binding mode is `move`
139139
assert_type_eq(x, 0u32);
140140
assert_type_eq(y, 0u32);
141141
}

0 commit comments

Comments
 (0)