Skip to content

Commit f3faaf5

Browse files
authored
Rollup merge of rust-lang#134394 - dianne:clarify-pat-2024-migration, r=compiler-errors
Clarify the match ergonomics 2024 migration lint's output This makes a few changes: - Rather than using the whole pattern as a span for the lint, this collects spans for each problematic default binding mode reset and labels them with why they're problems. - The lint's suggestions are now verbose-styled, so that it's clear what's being suggested vs. what's problematic. - The wording is now less technical, and the hard error version of this diagnostic now links to the same reference material as the lint (currently an unwritten page of the edition guide). I'm not totally confident in the wording or formatting, so I'd appreciate feedback on that in particular. I tried to draw a connection with word choice between the labels and the suggestion, but it might be imprecise, unclear, or cluttered. If so, it might be worth making the labels more terse and adding notes that explain them, but that's harder to read in a way too. cc ```@Nadrieril``` ```@Jules-Bertholet``` Closes rust-lang#133854. For reference, the error from that issue becomes: ``` error: pattern uses features incompatible with edition 2024 --> $DIR/remove-me.rs:6:25 | LL | map.iter().filter(|(&(_x, _y), &_c)| false); | ^ ^ cannot implicitly match against multiple layers of reference | | | cannot implicitly match against multiple layers of reference | help: make the implied reference pattern explicit | LL | map.iter().filter(|&(&(_x, _y), &_c)| false); | + ```
2 parents 0a2d708 + 28c6d0b commit f3faaf5

File tree

10 files changed

+315
-189
lines changed

10 files changed

+315
-189
lines changed

compiler/rustc_hir_typeck/src/pat.rs

+53-18
Original file line numberDiff line numberDiff line change
@@ -717,25 +717,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
717717
BindingMode(def_br, Mutability::Mut)
718718
} else {
719719
// `mut` resets the binding mode on edition <= 2021
720-
*self
721-
.typeck_results
722-
.borrow_mut()
723-
.rust_2024_migration_desugared_pats_mut()
724-
.entry(pat_info.top_info.hir_id)
725-
.or_default() |= pat.span.at_least_rust_2024();
720+
self.add_rust_2024_migration_desugared_pat(
721+
pat_info.top_info.hir_id,
722+
pat.span,
723+
ident.span,
724+
"requires binding by-value, but the implicit default is by-reference",
725+
);
726726
BindingMode(ByRef::No, Mutability::Mut)
727727
}
728728
}
729729
BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
730730
BindingMode(ByRef::Yes(_), _) => {
731731
if matches!(def_br, ByRef::Yes(_)) {
732732
// `ref`/`ref mut` overrides the binding mode on edition <= 2021
733-
*self
734-
.typeck_results
735-
.borrow_mut()
736-
.rust_2024_migration_desugared_pats_mut()
737-
.entry(pat_info.top_info.hir_id)
738-
.or_default() |= pat.span.at_least_rust_2024();
733+
self.add_rust_2024_migration_desugared_pat(
734+
pat_info.top_info.hir_id,
735+
pat.span,
736+
ident.span,
737+
"cannot override to bind by-reference when that is the implicit default",
738+
);
739739
}
740740
user_bind_annot
741741
}
@@ -2265,12 +2265,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22652265
// Reset binding mode on old editions
22662266
if pat_info.binding_mode != ByRef::No {
22672267
pat_info.binding_mode = ByRef::No;
2268-
*self
2269-
.typeck_results
2270-
.borrow_mut()
2271-
.rust_2024_migration_desugared_pats_mut()
2272-
.entry(pat_info.top_info.hir_id)
2273-
.or_default() |= pat.span.at_least_rust_2024();
2268+
self.add_rust_2024_migration_desugared_pat(
2269+
pat_info.top_info.hir_id,
2270+
pat.span,
2271+
inner.span,
2272+
"cannot implicitly match against multiple layers of reference",
2273+
)
22742274
}
22752275
}
22762276

@@ -2629,4 +2629,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26292629
_ => (false, ty),
26302630
}
26312631
}
2632+
2633+
/// Record a pattern that's invalid under Rust 2024 match ergonomics, along with a problematic
2634+
/// span, so that the pattern migration lint can desugar it during THIR construction.
2635+
fn add_rust_2024_migration_desugared_pat(
2636+
&self,
2637+
pat_id: HirId,
2638+
subpat_span: Span,
2639+
cutoff_span: Span,
2640+
detailed_label: &str,
2641+
) {
2642+
// Try to trim the span we're labeling to just the `&` or binding mode that's an issue.
2643+
// If the subpattern's span is is from an expansion, the emitted label will not be trimmed.
2644+
let source_map = self.tcx.sess.source_map();
2645+
let cutoff_span = source_map
2646+
.span_extend_prev_while(cutoff_span, char::is_whitespace)
2647+
.unwrap_or(cutoff_span);
2648+
// Ensure we use the syntax context and thus edition of `subpat_span`; this will be a hard
2649+
// error if the subpattern is of edition >= 2024.
2650+
let trimmed_span = subpat_span.until(cutoff_span).with_ctxt(subpat_span.ctxt());
2651+
2652+
// Only provide a detailed label if the problematic subpattern isn't from an expansion.
2653+
// 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"
2656+
} else {
2657+
detailed_label
2658+
};
2659+
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()));
2666+
}
26322667
}

compiler/rustc_middle/src/ty/typeck_results.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,8 @@ pub struct TypeckResults<'tcx> {
7474
pat_binding_modes: ItemLocalMap<BindingMode>,
7575

7676
/// Top-level patterns whose match ergonomics need to be desugared by the Rust 2021 -> 2024
77-
/// migration lint. The boolean indicates whether the emitted diagnostic should be a hard error
78-
/// (if any of the incompatible pattern elements are in edition 2024).
79-
rust_2024_migration_desugared_pats: ItemLocalMap<bool>,
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)>>,
8079

8180
/// Stores the types which were implicitly dereferenced in pattern binding modes
8281
/// for later usage in THIR lowering. For example,
@@ -419,14 +418,18 @@ impl<'tcx> TypeckResults<'tcx> {
419418
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments }
420419
}
421420

422-
pub fn rust_2024_migration_desugared_pats(&self) -> LocalTableInContext<'_, bool> {
421+
pub fn rust_2024_migration_desugared_pats(
422+
&self,
423+
) -> LocalTableInContext<'_, Vec<(Span, String)>> {
423424
LocalTableInContext {
424425
hir_owner: self.hir_owner,
425426
data: &self.rust_2024_migration_desugared_pats,
426427
}
427428
}
428429

429-
pub fn rust_2024_migration_desugared_pats_mut(&mut self) -> LocalTableInContextMut<'_, bool> {
430+
pub fn rust_2024_migration_desugared_pats_mut(
431+
&mut self,
432+
) -> LocalTableInContextMut<'_, Vec<(Span, String)>> {
430433
LocalTableInContextMut {
431434
hir_owner: self.hir_owner,
432435
data: &mut self.rust_2024_migration_desugared_pats,

compiler/rustc_mir_build/messages.ftl

+1-1
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ 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 = patterns are not allowed to reset the default binding mode in edition 2024
288+
mir_build_rust_2024_incompatible_pat = this pattern relies on behavior which may change in edition 2024
289289
290290
mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly
291291
.attributes = no other attributes may be applied

compiler/rustc_mir_build/src/errors.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use rustc_errors::codes::*;
22
use rustc_errors::{
33
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
4-
MultiSpan, SubdiagMessageOp, Subdiagnostic,
4+
MultiSpan, SubdiagMessageOp, Subdiagnostic, pluralize,
55
};
66
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
77
use rustc_middle::ty::{self, Ty};
@@ -1088,18 +1088,20 @@ pub(crate) enum RustcBoxAttrReason {
10881088

10891089
#[derive(LintDiagnostic)]
10901090
#[diag(mir_build_rust_2024_incompatible_pat)]
1091-
pub(crate) struct Rust2024IncompatiblePat {
1091+
pub(crate) struct Rust2024IncompatiblePat<'a> {
10921092
#[subdiagnostic]
1093-
pub(crate) sugg: Rust2024IncompatiblePatSugg,
1093+
pub(crate) sugg: Rust2024IncompatiblePatSugg<'a>,
10941094
}
10951095

1096-
pub(crate) struct Rust2024IncompatiblePatSugg {
1096+
pub(crate) struct Rust2024IncompatiblePatSugg<'a> {
10971097
pub(crate) suggestion: Vec<(Span, String)>,
1098-
/// Whether the incompatibility is a hard error because a relevant span is in edition 2024.
1099-
pub(crate) is_hard_error: bool,
1098+
pub(crate) ref_pattern_count: usize,
1099+
pub(crate) binding_mode_count: usize,
1100+
/// Labeled spans for subpatterns invalid in Rust 2024.
1101+
pub(crate) labels: &'a [(Span, String)],
11001102
}
11011103

1102-
impl Subdiagnostic for Rust2024IncompatiblePatSugg {
1104+
impl<'a> Subdiagnostic for Rust2024IncompatiblePatSugg<'a> {
11031105
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
11041106
self,
11051107
diag: &mut Diag<'_, G>,
@@ -1111,6 +1113,16 @@ impl Subdiagnostic for Rust2024IncompatiblePatSugg {
11111113
} else {
11121114
Applicability::MaybeIncorrect
11131115
};
1114-
diag.multipart_suggestion("desugar the match ergonomics", self.suggestion, applicability);
1116+
let plural_derefs = pluralize!(self.ref_pattern_count);
1117+
let and_modes = if self.binding_mode_count > 0 {
1118+
format!(" and variable binding mode{}", pluralize!(self.binding_mode_count))
1119+
} else {
1120+
String::new()
1121+
};
1122+
diag.multipart_suggestion_verbose(
1123+
format!("make the implied reference pattern{plural_derefs}{and_modes} explicit"),
1124+
self.suggestion,
1125+
applicability,
1126+
);
11151127
}
11161128
}

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

+22-7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod const_to_pat;
66
use std::cmp::Ordering;
77

88
use rustc_abi::{FieldIdx, Integer};
9+
use rustc_errors::MultiSpan;
910
use rustc_errors::codes::*;
1011
use rustc_hir::def::{CtorOf, DefKind, Res};
1112
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
@@ -34,7 +35,7 @@ struct PatCtxt<'a, 'tcx> {
3435
typeck_results: &'a ty::TypeckResults<'tcx>,
3536

3637
/// Used by the Rust 2024 migration lint.
37-
rust_2024_migration_suggestion: Option<Rust2024IncompatiblePatSugg>,
38+
rust_2024_migration_suggestion: Option<Rust2024IncompatiblePatSugg<'a>>,
3839
}
3940

4041
pub(super) fn pat_from_hir<'a, 'tcx>(
@@ -50,24 +51,36 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
5051
rust_2024_migration_suggestion: typeck_results
5152
.rust_2024_migration_desugared_pats()
5253
.get(pat.hir_id)
53-
.map(|&is_hard_error| Rust2024IncompatiblePatSugg {
54+
.map(|labels| Rust2024IncompatiblePatSugg {
5455
suggestion: Vec::new(),
55-
is_hard_error,
56+
ref_pattern_count: 0,
57+
binding_mode_count: 0,
58+
labels: labels.as_slice(),
5659
}),
5760
};
5861
let result = pcx.lower_pattern(pat);
5962
debug!("pat_from_hir({:?}) = {:?}", pat, result);
6063
if let Some(sugg) = pcx.rust_2024_migration_suggestion {
61-
if sugg.is_hard_error {
64+
let mut spans = MultiSpan::from_spans(sugg.labels.iter().map(|(span, _)| *span).collect());
65+
for (span, label) in sugg.labels {
66+
spans.push_span_label(*span, label.clone());
67+
}
68+
// If a relevant span is from at least edition 2024, this is a hard error.
69+
let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());
70+
if is_hard_error {
6271
let mut err =
63-
tcx.dcx().struct_span_err(pat.span, fluent::mir_build_rust_2024_incompatible_pat);
72+
tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);
73+
if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
74+
// provide the same reference link as the lint
75+
err.note(format!("for more information, see {}", info.reference));
76+
}
6477
err.subdiagnostic(sugg);
6578
err.emit();
6679
} else {
6780
tcx.emit_node_span_lint(
6881
lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
6982
pat.hir_id,
70-
pat.span,
83+
spans,
7184
Rust2024IncompatiblePat { sugg },
7285
);
7386
}
@@ -133,6 +146,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
133146
})
134147
.collect();
135148
s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
149+
s.ref_pattern_count += adjustments.len();
136150
};
137151

138152
adjusted_pat
@@ -371,7 +385,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
371385
s.suggestion.push((
372386
pat.span.with_lo(ident.span.lo()).shrink_to_lo(),
373387
sugg_str.to_owned(),
374-
))
388+
));
389+
s.binding_mode_count += 1;
375390
}
376391

377392
// A ref x pattern is the same node used for x, and as such it has

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

+16-16
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: patterns are not allowed to reset the default binding mode
26+
//~^ ERROR: this pattern relies on behavior which may change in edition 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: patterns are not allowed to reset the default binding mode
31+
//~^ ERROR: this pattern relies on behavior which may change in edition 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: patterns are not allowed to reset the default binding mode
36+
//~^ ERROR: this pattern relies on behavior which may change in edition 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: patterns are not allowed to reset the default binding mode
41+
//~^ ERROR: this pattern relies on behavior which may change in edition 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: patterns are not allowed to reset the default binding mode
58+
//~^ ERROR: this pattern relies on behavior which may change in edition 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: patterns are not allowed to reset the default binding mode
63+
//~^ ERROR: this pattern relies on behavior which may change in edition 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: patterns are not allowed to reset the default binding mode
68+
//~^ ERROR: this pattern relies on behavior which may change in edition 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: patterns are not allowed to reset the default binding mode
73+
//~^ ERROR: this pattern relies on behavior which may change in edition 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: patterns are not allowed to reset the default binding mode
82+
//~^ ERROR: this pattern relies on behavior which may change in edition 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: patterns are not allowed to reset the default binding mode
88+
//~^ ERROR: this pattern relies on behavior which may change in edition 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: patterns are not allowed to reset the default binding mode
94+
//~^ ERROR: this pattern relies on behavior which may change in edition 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: patterns are not allowed to reset the default binding mode
100+
//~^ ERROR: this pattern relies on behavior which may change in edition 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: patterns are not allowed to reset the default binding mode
112+
//~^ ERROR: this pattern relies on behavior which may change in edition 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: patterns are not allowed to reset the default binding mode
118+
//~^ ERROR: this pattern relies on behavior which may change in edition 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: patterns are not allowed to reset the default binding mode
125+
//~^ ERROR: this pattern relies on behavior which may change in edition 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: patterns are not allowed to reset the default binding mode
138+
//~^ ERROR: this pattern relies on behavior which may change in edition 2024
139139
assert_type_eq(x, 0u32);
140140
assert_type_eq(y, 0u32);
141141
}

0 commit comments

Comments
 (0)