Skip to content

Commit 203d310

Browse files
committed
experimentally label the spans for default binding modes
1 parent 4331f55 commit 203d310

File tree

8 files changed

+264
-223
lines changed

8 files changed

+264
-223
lines changed

compiler/rustc_hir_typeck/src/pat.rs

+3-18
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
804804

805805
// Determine the binding mode...
806806
let bm = match user_bind_annot {
807-
BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(def_br_mutbl) = def_br => {
807+
BindingMode(ByRef::No, Mutability::Mut) if matches!(def_br, ByRef::Yes(_)) => {
808808
// Only mention the experimental `mut_ref` feature if if we're in edition 2024 and
809809
// using other experimental matching features compatible with it.
810810
if pat.span.at_least_rust_2024()
@@ -828,20 +828,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
828828
pat_info.top_info.hir_id,
829829
pat,
830830
ident.span,
831-
def_br_mutbl,
832831
);
833832
BindingMode(ByRef::No, Mutability::Mut)
834833
}
835834
}
836835
BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
837836
BindingMode(ByRef::Yes(_), _) => {
838-
if let ByRef::Yes(def_br_mutbl) = def_br {
837+
if matches!(def_br, ByRef::Yes(_)) {
839838
// `ref`/`ref mut` overrides the binding mode on edition <= 2021
840839
self.add_rust_2024_migration_desugared_pat(
841840
pat_info.top_info.hir_id,
842841
pat,
843842
ident.span,
844-
def_br_mutbl,
845843
);
846844
}
847845
user_bind_annot
@@ -2380,7 +2378,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23802378
pat_info.top_info.hir_id,
23812379
pat,
23822380
inner.span,
2383-
inh_mut,
23842381
)
23852382
}
23862383
}
@@ -2772,7 +2769,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27722769
pat_id: HirId,
27732770
subpat: &'tcx Pat<'tcx>,
27742771
cutoff_span: Span,
2775-
def_br_mutbl: Mutability,
27762772
) {
27772773
// Try to trim the span we're labeling to just the `&` or binding mode that's an issue.
27782774
// If the subpattern's span is is from an expansion, the emitted label will not be trimmed.
@@ -2788,8 +2784,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27882784
let mut table = typeck_results.rust_2024_migration_desugared_pats_mut();
27892785
let info = table.entry(pat_id).or_default();
27902786

2791-
info.primary_spans.push(trimmed_span);
2792-
27932787
// Only provide a detailed label if the problematic subpattern isn't from an expansion.
27942788
// In the case that it's from a macro, we'll add a more detailed note in the emitter.
27952789
let from_expansion = subpat.span.from_expansion();
@@ -2807,15 +2801,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
28072801
"this reference pattern"
28082802
}
28092803
};
2810-
info.span_labels.push((trimmed_span, primary_label.to_owned()));
2811-
2812-
if !from_expansion {
2813-
// Add a secondary label covering the whole pattern noting the default binding mode
2814-
let def_br_desc = match def_br_mutbl {
2815-
Mutability::Not => "default binding mode is `ref`",
2816-
Mutability::Mut => "default binding mode is `ref mut`",
2817-
};
2818-
info.span_labels.push((subpat.span, def_br_desc.to_owned()));
2819-
}
2804+
info.primary_labels.push((trimmed_span, primary_label.to_owned()));
28202805
}
28212806
}

compiler/rustc_middle/src/ty/typeck_results.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -816,10 +816,8 @@ impl<'tcx> std::fmt::Display for UserTypeKind<'tcx> {
816816
/// emitted during THIR construction.
817817
#[derive(TyEncodable, TyDecodable, Debug, HashStable, Default)]
818818
pub struct Rust2024IncompatiblePatInfo {
819-
/// Spans for `&`s, `&mut`s, and binding modifiers incompatible with Rust 2024.
820-
pub primary_spans: Vec<Span>,
821-
/// Labels for the primary spans and their patterns, to provide additional context.
822-
pub span_labels: Vec<(Span, String)>,
819+
/// Labeled spans for `&`s, `&mut`s, and binding modifiers incompatible with Rust 2024.
820+
pub primary_labels: Vec<(Span, String)>,
823821
/// Whether any binding modifiers occur under a non-`move` default binding mode.
824822
pub bad_modifiers: bool,
825823
/// Whether any `&` or `&mut` patterns occur under a non-`move` default binding mode.

compiler/rustc_mir_build/src/errors.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use rustc_data_structures::fx::FxIndexMap;
12
use rustc_errors::codes::*;
23
use rustc_errors::{
34
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
@@ -1109,6 +1110,11 @@ pub(crate) struct Rust2024IncompatiblePatSugg {
11091110
pub(crate) suggestion: Vec<(Span, String)>,
11101111
pub(crate) ref_pattern_count: usize,
11111112
pub(crate) binding_mode_count: usize,
1113+
/// Internal state: the ref-mutability of the default binding mode at the subpattern being
1114+
/// lowered, with the span where it was introduced. `None` for a by-value default mode.
1115+
pub(crate) default_mode_span: Option<(Span, ty::Mutability)>,
1116+
/// Labels for where incompatibility-causing by-ref default binding modes were introduced.
1117+
pub(crate) default_mode_labels: FxIndexMap<Span, ty::Mutability>,
11121118
}
11131119

11141120
impl Subdiagnostic for Rust2024IncompatiblePatSugg {

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

+88-35
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,29 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
5353
suggestion: Vec::new(),
5454
ref_pattern_count: 0,
5555
binding_mode_count: 0,
56+
default_mode_span: None,
57+
default_mode_labels: Default::default(),
5658
})),
5759
};
5860
let result = pcx.lower_pattern(pat);
5961
debug!("pat_from_hir({:?}) = {:?}", pat, result);
6062
if let Some(info) = migration_info {
6163
let sugg = pcx.rust_2024_migration_suggestion.expect("suggestion should be present");
62-
let mut spans = MultiSpan::from_spans(info.primary_spans.clone());
63-
for (span, label) in &info.span_labels {
64+
let mut spans =
65+
MultiSpan::from_spans(info.primary_labels.iter().map(|(span, _)| *span).collect());
66+
for (span, label) in &info.primary_labels {
6467
spans.push_span_label(*span, label.clone());
6568
}
69+
for (span, label_mutbl) in &sugg.default_mode_labels {
70+
// Don't point to a macro call site.
71+
if !span.from_expansion() {
72+
let label = match label_mutbl {
73+
Mutability::Not => "default binding mode is `ref`",
74+
Mutability::Mut => "default binding mode is `ref mut`",
75+
};
76+
spans.push_span_label(*span, label.to_owned())
77+
}
78+
}
6679
// If a relevant span is from at least edition 2024, this is a hard error.
6780
let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());
6881
if is_hard_error {
@@ -96,6 +109,40 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
96109

97110
impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
98111
fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> {
112+
let adjustments: &[Ty<'tcx>] =
113+
self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
114+
115+
let mut opt_old_mode_span = None;
116+
if let Some(s) = &mut self.rust_2024_migration_suggestion
117+
&& !adjustments.is_empty()
118+
{
119+
let mut min_mutbl = Mutability::Mut;
120+
let suggestion_str: String = adjustments
121+
.iter()
122+
.map(|ref_ty| {
123+
let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
124+
span_bug!(pat.span, "pattern implicitly dereferences a non-ref type");
125+
};
126+
127+
match mutbl {
128+
Mutability::Not => {
129+
min_mutbl = Mutability::Not;
130+
"&"
131+
}
132+
Mutability::Mut => "&mut ",
133+
}
134+
})
135+
.collect();
136+
s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
137+
s.ref_pattern_count += adjustments.len();
138+
139+
// Remember if this changed the default binding mode, in case we want to label it.
140+
if s.default_mode_span.is_none_or(|(_, old_mutbl)| min_mutbl < old_mutbl) {
141+
opt_old_mode_span = Some(s.default_mode_span);
142+
s.default_mode_span = Some((pat.span, min_mutbl));
143+
}
144+
};
145+
99146
// When implicit dereferences have been inserted in this pattern, the unadjusted lowered
100147
// pattern has the type that results *after* dereferencing. For example, in this code:
101148
//
@@ -124,8 +171,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
124171
_ => self.lower_pattern_unadjusted(pat),
125172
};
126173

127-
let adjustments: &[Ty<'tcx>] =
128-
self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
129174
let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, ref_ty| {
130175
debug!("{:?}: wrapping pattern with type {:?}", thir_pat, ref_ty);
131176
Box::new(Pat {
@@ -136,24 +181,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
136181
});
137182

138183
if let Some(s) = &mut self.rust_2024_migration_suggestion
139-
&& !adjustments.is_empty()
184+
&& let Some(old_mode_span) = opt_old_mode_span
140185
{
141-
let suggestion_str: String = adjustments
142-
.iter()
143-
.map(|ref_ty| {
144-
let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
145-
span_bug!(pat.span, "pattern implicitly dereferences a non-ref type");
146-
};
147-
148-
match mutbl {
149-
ty::Mutability::Not => "&",
150-
ty::Mutability::Mut => "&mut ",
151-
}
152-
})
153-
.collect();
154-
s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
155-
s.ref_pattern_count += adjustments.len();
156-
};
186+
s.default_mode_span = old_mode_span;
187+
}
157188

158189
adjusted_pat
159190
}
@@ -343,7 +374,22 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
343374
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
344375
PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), mutability }
345376
}
346-
hir::PatKind::Ref(subpattern, _) | hir::PatKind::Box(subpattern) => {
377+
hir::PatKind::Ref(subpattern, _) => {
378+
// Track the default binding mode for the Rust 2024 migration suggestion.
379+
let old_mode_span = self.rust_2024_migration_suggestion.as_mut().and_then(|s| {
380+
if let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span {
381+
// If this eats a by-ref default binding mode, label the binding mode.
382+
s.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
383+
}
384+
s.default_mode_span.take()
385+
});
386+
let subpattern = self.lower_pattern(subpattern);
387+
if let Some(s) = &mut self.rust_2024_migration_suggestion {
388+
s.default_mode_span = old_mode_span;
389+
}
390+
PatKind::Deref { subpattern }
391+
}
392+
hir::PatKind::Box(subpattern) => {
347393
PatKind::Deref { subpattern: self.lower_pattern(subpattern) }
348394
}
349395

@@ -370,19 +416,26 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
370416
.get(pat.hir_id)
371417
.expect("missing binding mode");
372418

373-
if let Some(s) = &mut self.rust_2024_migration_suggestion
374-
&& explicit_ba.0 == ByRef::No
375-
&& let ByRef::Yes(mutbl) = mode.0
376-
{
377-
let sugg_str = match mutbl {
378-
Mutability::Not => "ref ",
379-
Mutability::Mut => "ref mut ",
380-
};
381-
s.suggestion.push((
382-
pat.span.with_lo(ident.span.lo()).shrink_to_lo(),
383-
sugg_str.to_owned(),
384-
));
385-
s.binding_mode_count += 1;
419+
if let Some(s) = &mut self.rust_2024_migration_suggestion {
420+
if explicit_ba != hir::BindingMode::NONE
421+
&& let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span
422+
{
423+
// If this overrides a by-ref default binding mode, label the binding mode.
424+
s.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
425+
}
426+
if explicit_ba.0 == ByRef::No
427+
&& let ByRef::Yes(mutbl) = mode.0
428+
{
429+
let sugg_str = match mutbl {
430+
Mutability::Not => "ref ",
431+
Mutability::Mut => "ref mut ",
432+
};
433+
s.suggestion.push((
434+
pat.span.with_lo(ident.span.lo()).shrink_to_lo(),
435+
sugg_str.to_owned(),
436+
));
437+
s.binding_mode_count += 1;
438+
}
386439
}
387440

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

tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr

+16-16
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ error: binding modifiers may only be written when the default binding mode is `m
1414
--> $DIR/ref-binding-on-inh-ref-errors.rs:67:10
1515
|
1616
LL | let [ref mut x] = &[0];
17-
| ^^^^^^^--
18-
| |
19-
| this binding modifier
20-
| default binding mode is `ref`
17+
| -^^^^^^^---
18+
| ||
19+
| |this binding modifier
20+
| default binding mode is `ref`
2121
|
2222
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
2323
help: make the implied reference pattern explicit
@@ -35,10 +35,10 @@ error: binding modifiers may only be written when the default binding mode is `m
3535
--> $DIR/ref-binding-on-inh-ref-errors.rs:75:10
3636
|
3737
LL | let [ref x] = &[0];
38-
| ^^^--
39-
| |
40-
| this binding modifier
41-
| default binding mode is `ref`
38+
| -^^^---
39+
| ||
40+
| |this binding modifier
41+
| default binding mode is `ref`
4242
|
4343
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
4444
help: make the implied reference pattern explicit
@@ -50,10 +50,10 @@ error: binding modifiers may only be written when the default binding mode is `m
5050
--> $DIR/ref-binding-on-inh-ref-errors.rs:79:10
5151
|
5252
LL | let [ref x] = &mut [0];
53-
| ^^^--
54-
| |
55-
| this binding modifier
56-
| default binding mode is `ref mut`
53+
| -^^^---
54+
| ||
55+
| |this binding modifier
56+
| default binding mode is `ref mut`
5757
|
5858
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
5959
help: make the implied reference pattern explicit
@@ -65,10 +65,10 @@ error: binding modifiers may only be written when the default binding mode is `m
6565
--> $DIR/ref-binding-on-inh-ref-errors.rs:83:10
6666
|
6767
LL | let [ref mut x] = &mut [0];
68-
| ^^^^^^^--
69-
| |
70-
| this binding modifier
71-
| default binding mode is `ref mut`
68+
| -^^^^^^^---
69+
| ||
70+
| |this binding modifier
71+
| default binding mode is `ref mut`
7272
|
7373
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
7474
help: make the implied reference pattern explicit

0 commit comments

Comments
 (0)