Skip to content

Commit a676872

Browse files
committed
Clarify the match ergonomics 2024 migration lint's output
1 parent a4cb3c8 commit a676872

File tree

10 files changed

+301
-189
lines changed

10 files changed

+301
-189
lines changed

compiler/rustc_hir_typeck/src/pat.rs

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

@@ -2630,4 +2630,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26302630
_ => (false, ty),
26312631
}
26322632
}
2633+
2634+
/// Record a pattern that's invalid under Rust 2024 match ergonomics, along with a problematic
2635+
/// span, so that the pattern migration lint can desugar it during THIR construction.
2636+
fn add_rust_2024_migration_desugared_pat(
2637+
&self,
2638+
pat_id: HirId,
2639+
subpat_span: Span,
2640+
cutoff_span: Span,
2641+
detailed_label: &str,
2642+
) {
2643+
// Try to trim the span we're labeling to just the `&` or binding mode that's an issue.
2644+
// If the subpattern span is a macro call site, no trimming will be done.
2645+
let source_map = self.tcx.sess.source_map();
2646+
let cutoff_span = source_map
2647+
.span_extend_prev_while(cutoff_span, char::is_whitespace)
2648+
.unwrap_or(cutoff_span);
2649+
let trimmed_span = subpat_span.until(cutoff_span);
2650+
2651+
// Only provide a detailed label if the problematic subpattern isn't from an expansion.
2652+
// In the case that it's from a macro, we'll add a more detailed note in the emitter.
2653+
let desc = if subpat_span.from_expansion() {
2654+
"default binding mode is reset within expansion"
2655+
} else {
2656+
detailed_label
2657+
};
2658+
2659+
self.typeck_results
2660+
.borrow_mut()
2661+
.rust_2024_migration_desugared_pats_mut()
2662+
.entry(pat_id)
2663+
.or_default()
2664+
.push((trimmed_span, desc.to_owned()));
2665+
}
26332666
}

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 = pattern uses features incompatible with 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};
@@ -1089,18 +1089,20 @@ pub(crate) enum RustcBoxAttrReason {
10891089

10901090
#[derive(LintDiagnostic)]
10911091
#[diag(mir_build_rust_2024_incompatible_pat)]
1092-
pub(crate) struct Rust2024IncompatiblePat {
1092+
pub(crate) struct Rust2024IncompatiblePat<'a> {
10931093
#[subdiagnostic]
1094-
pub(crate) sugg: Rust2024IncompatiblePatSugg,
1094+
pub(crate) sugg: Rust2024IncompatiblePatSugg<'a>,
10951095
}
10961096

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

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

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

+18-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,32 @@ 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);
6473
err.subdiagnostic(sugg);
6574
err.emit();
6675
} else {
6776
tcx.emit_node_span_lint(
6877
lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
6978
pat.hir_id,
70-
pat.span,
79+
spans,
7180
Rust2024IncompatiblePat { sugg },
7281
);
7382
}
@@ -133,6 +142,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
133142
})
134143
.collect();
135144
s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
145+
s.ref_pattern_count += adjustments.len();
136146
};
137147

138148
adjusted_pat
@@ -371,7 +381,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
371381
s.suggestion.push((
372382
pat.span.with_lo(ident.span.lo()).shrink_to_lo(),
373383
sugg_str.to_owned(),
374-
))
384+
));
385+
s.binding_mode_count += 1;
375386
}
376387

377388
// 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: pattern uses features incompatible with 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: pattern uses features incompatible with 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: pattern uses features incompatible with 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: pattern uses features incompatible with 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: pattern uses features incompatible with 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: pattern uses features incompatible with 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: pattern uses features incompatible with 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: pattern uses features incompatible with 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: pattern uses features incompatible with 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: pattern uses features incompatible with 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: pattern uses features incompatible with 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: pattern uses features incompatible with 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: pattern uses features incompatible with 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: pattern uses features incompatible with 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: pattern uses features incompatible with 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: pattern uses features incompatible with edition 2024
139139
assert_type_eq(x, 0u32);
140140
assert_type_eq(y, 0u32);
141141
}

0 commit comments

Comments
 (0)