Skip to content

Commit 6ff2d19

Browse files
committed
experimentally label the spans for default binding modes
(cherry picked from commit 203d310)
1 parent 2ee601c commit 6ff2d19

File tree

6 files changed

+205
-164
lines changed

6 files changed

+205
-164
lines changed

compiler/rustc_hir_typeck/src/pat.rs

Lines changed: 4 additions & 19 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 let ByRef::Yes(def_br_mutbl) = def_br => {
702+
BindingMode(ByRef::No, Mutability::Mut) if matches!(def_br, ByRef::Yes(_)) => {
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())
@@ -721,20 +721,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
721721
pat_info.top_info.hir_id,
722722
pat,
723723
ident.span,
724-
def_br_mutbl,
725724
);
726725
BindingMode(ByRef::No, Mutability::Mut)
727726
}
728727
}
729728
BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
730729
BindingMode(ByRef::Yes(_), _) => {
731-
if let ByRef::Yes(def_br_mutbl) = def_br {
730+
if matches!(def_br, ByRef::Yes(_)) {
732731
// `ref`/`ref mut` overrides the binding mode on edition <= 2021
733732
self.add_rust_2024_migration_desugared_pat(
734733
pat_info.top_info.hir_id,
735734
pat,
736735
ident.span,
737-
def_br_mutbl,
738736
);
739737
}
740738
user_bind_annot
@@ -2263,13 +2261,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22632261
}
22642262
} else {
22652263
// Reset binding mode on old editions
2266-
if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
2264+
if pat_info.binding_mode != ByRef::No {
22672265
pat_info.binding_mode = ByRef::No;
22682266
self.add_rust_2024_migration_desugared_pat(
22692267
pat_info.top_info.hir_id,
22702268
pat,
22712269
inner.span,
2272-
inh_mut,
22732270
)
22742271
}
22752272
}
@@ -2637,7 +2634,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26372634
pat_id: HirId,
26382635
subpat: &'tcx Pat<'tcx>,
26392636
cutoff_span: Span,
2640-
def_br_mutbl: Mutability,
26412637
) {
26422638
// Try to trim the span we're labeling to just the `&` or binding mode that's an issue.
26432639
// If the subpattern's span is is from an expansion, the emitted label will not be trimmed.
@@ -2653,8 +2649,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26532649
let mut table = typeck_results.rust_2024_migration_desugared_pats_mut();
26542650
let info = table.entry(pat_id).or_default();
26552651

2656-
info.primary_spans.push(trimmed_span);
2657-
26582652
// Only provide a detailed label if the problematic subpattern isn't from an expansion.
26592653
// In the case that it's from a macro, we'll add a more detailed note in the emitter.
26602654
let from_expansion = subpat.span.from_expansion();
@@ -2672,15 +2666,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26722666
"this reference pattern"
26732667
}
26742668
};
2675-
info.span_labels.push((trimmed_span, primary_label.to_owned()));
2676-
2677-
if !from_expansion {
2678-
// Add a secondary label covering the whole pattern noting the default binding mode
2679-
let def_br_desc = match def_br_mutbl {
2680-
Mutability::Not => "default binding mode is `ref`",
2681-
Mutability::Mut => "default binding mode is `ref mut`",
2682-
};
2683-
info.span_labels.push((subpat.span, def_br_desc.to_owned()));
2684-
}
2669+
info.primary_labels.push((trimmed_span, primary_label.to_owned()));
26852670
}
26862671
}

compiler/rustc_middle/src/ty/typeck_results.rs

Lines changed: 2 additions & 4 deletions
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

Lines changed: 6 additions & 0 deletions
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,
@@ -1100,6 +1101,11 @@ pub(crate) struct Rust2024IncompatiblePatSugg {
11001101
pub(crate) suggestion: Vec<(Span, String)>,
11011102
pub(crate) ref_pattern_count: usize,
11021103
pub(crate) binding_mode_count: usize,
1104+
/// Internal state: the ref-mutability of the default binding mode at the subpattern being
1105+
/// lowered, with the span where it was introduced. `None` for a by-value default mode.
1106+
pub(crate) default_mode_span: Option<(Span, ty::Mutability)>,
1107+
/// Labels for where incompatibility-causing by-ref default binding modes were introduced.
1108+
pub(crate) default_mode_labels: FxIndexMap<Span, ty::Mutability>,
11031109
}
11041110

11051111
impl Subdiagnostic for Rust2024IncompatiblePatSugg {

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

Lines changed: 88 additions & 35 deletions
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
}
@@ -353,7 +384,22 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
353384
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
354385
PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), mutability }
355386
}
356-
hir::PatKind::Ref(subpattern, _) | hir::PatKind::Box(subpattern) => {
387+
hir::PatKind::Ref(subpattern, _) => {
388+
// Track the default binding mode for the Rust 2024 migration suggestion.
389+
let old_mode_span = self.rust_2024_migration_suggestion.as_mut().and_then(|s| {
390+
if let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span {
391+
// If this eats a by-ref default binding mode, label the binding mode.
392+
s.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
393+
}
394+
s.default_mode_span.take()
395+
});
396+
let subpattern = self.lower_pattern(subpattern);
397+
if let Some(s) = &mut self.rust_2024_migration_suggestion {
398+
s.default_mode_span = old_mode_span;
399+
}
400+
PatKind::Deref { subpattern }
401+
}
402+
hir::PatKind::Box(subpattern) => {
357403
PatKind::Deref { subpattern: self.lower_pattern(subpattern) }
358404
}
359405

@@ -380,19 +426,26 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
380426
.get(pat.hir_id)
381427
.expect("missing binding mode");
382428

383-
if let Some(s) = &mut self.rust_2024_migration_suggestion
384-
&& explicit_ba.0 == ByRef::No
385-
&& let ByRef::Yes(mutbl) = mode.0
386-
{
387-
let sugg_str = match mutbl {
388-
Mutability::Not => "ref ",
389-
Mutability::Mut => "ref mut ",
390-
};
391-
s.suggestion.push((
392-
pat.span.with_lo(ident.span.lo()).shrink_to_lo(),
393-
sugg_str.to_owned(),
394-
));
395-
s.binding_mode_count += 1;
429+
if let Some(s) = &mut self.rust_2024_migration_suggestion {
430+
if explicit_ba != hir::BindingMode::NONE
431+
&& let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span
432+
{
433+
// If this overrides a by-ref default binding mode, label the binding mode.
434+
s.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
435+
}
436+
if explicit_ba.0 == ByRef::No
437+
&& let ByRef::Yes(mutbl) = mode.0
438+
{
439+
let sugg_str = match mutbl {
440+
Mutability::Not => "ref ",
441+
Mutability::Mut => "ref mut ",
442+
};
443+
s.suggestion.push((
444+
pat.span.with_lo(ident.span.lo()).shrink_to_lo(),
445+
sugg_str.to_owned(),
446+
));
447+
s.binding_mode_count += 1;
448+
}
396449
}
397450

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

0 commit comments

Comments
 (0)