Skip to content

Commit f1c287f

Browse files
committed
move pattern migration internals to the migration module
1 parent e1c6ead commit f1c287f

File tree

2 files changed

+114
-66
lines changed

2 files changed

+114
-66
lines changed

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

+104-9
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,32 @@
22
33
use rustc_data_structures::fx::FxIndexMap;
44
use rustc_errors::MultiSpan;
5-
use rustc_hir::HirId;
5+
use rustc_hir::{BindingMode, ByRef, HirId, Mutability};
66
use rustc_lint as lint;
7-
use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, TyCtxt};
8-
use rustc_span::Span;
7+
use rustc_middle::span_bug;
8+
use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, Ty, TyCtxt};
9+
use rustc_span::{Ident, Span};
910

1011
use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg};
1112
use crate::fluent_generated as fluent;
1213

1314
/// For patterns flagged for migration during HIR typeck, this handles constructing and emitting
1415
/// a diagnostic suggestion.
1516
pub(super) struct PatMigration<'a> {
16-
pub(super) suggestion: Vec<(Span, String)>,
17-
pub(super) ref_pattern_count: usize,
18-
pub(super) binding_mode_count: usize,
17+
suggestion: Vec<(Span, String)>,
18+
ref_pattern_count: usize,
19+
binding_mode_count: usize,
1920
/// Internal state: the ref-mutability of the default binding mode at the subpattern being
2021
/// lowered, with the span where it was introduced. `None` for a by-value default mode.
21-
pub(super) default_mode_span: Option<(Span, ty::Mutability)>,
22+
default_mode_span: Option<(Span, ty::Mutability)>,
2223
/// Labels for where incompatibility-causing by-ref default binding modes were introduced.
2324
// FIXME(ref_pat_eat_one_layer_2024_structural): To track the default binding mode, we duplicate
2425
// logic from HIR typeck (in order to avoid needing to store all changes to the dbm in
2526
// TypeckResults). Since the default binding mode acts differently under this feature gate, the
2627
// labels will be wrong.
27-
pub(super) default_mode_labels: FxIndexMap<Span, Mutability>,
28+
default_mode_labels: FxIndexMap<Span, Mutability>,
2829
/// Information collected from typeck, including spans for subpatterns invalid in Rust 2024.
29-
pub(super) info: &'a Rust2024IncompatiblePatInfo,
30+
info: &'a Rust2024IncompatiblePatInfo,
3031
}
3132

3233
impl<'a> PatMigration<'a> {
@@ -84,4 +85,98 @@ impl<'a> PatMigration<'a> {
8485
);
8586
}
8687
}
88+
89+
/// Tracks when we're lowering a pattern that implicitly dereferences the scrutinee.
90+
/// This should only be called when the pattern type adjustments list `adjustments` is
91+
/// non-empty. Returns the prior default binding mode; this should be followed by a call to
92+
/// [`PatMigration::leave_ref`] to restore it when we leave the pattern.
93+
pub(super) fn visit_implicit_derefs<'tcx>(
94+
&mut self,
95+
pat_span: Span,
96+
adjustments: &[Ty<'tcx>],
97+
) -> Option<(Span, Mutability)> {
98+
let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| {
99+
let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
100+
span_bug!(pat_span, "pattern implicitly dereferences a non-ref type");
101+
};
102+
mutbl
103+
});
104+
105+
if !self.info.suggest_eliding_modes {
106+
// If we can't fix the pattern by eliding modifiers, we'll need to make the pattern
107+
// fully explicit. i.e. we'll need to suggest reference patterns for this.
108+
let suggestion_str: String =
109+
implicit_deref_mutbls.clone().map(|mutbl| mutbl.ref_prefix_str()).collect();
110+
self.suggestion.push((pat_span.shrink_to_lo(), suggestion_str));
111+
self.ref_pattern_count += adjustments.len();
112+
}
113+
114+
// Remember if this changed the default binding mode, in case we want to label it.
115+
let min_mutbl = implicit_deref_mutbls.min().unwrap();
116+
if self.default_mode_span.is_none_or(|(_, old_mutbl)| min_mutbl < old_mutbl) {
117+
// This changes the default binding mode to `ref` or `ref mut`. Return the old mode so
118+
// it can be reinstated when we leave the pattern.
119+
self.default_mode_span.replace((pat_span, min_mutbl))
120+
} else {
121+
// This does not change the default binding mode; it was already `ref` or `ref mut`.
122+
self.default_mode_span
123+
}
124+
}
125+
126+
/// Tracks the default binding mode when we're lowering a `&` or `&mut` pattern.
127+
/// Returns the prior default binding mode; this should be followed by a call to
128+
/// [`PatMigration::leave_ref`] to restore it when we leave the pattern.
129+
pub(super) fn visit_explicit_deref(&mut self) -> Option<(Span, Mutability)> {
130+
if let Some((default_mode_span, default_ref_mutbl)) = self.default_mode_span {
131+
// If this eats a by-ref default binding mode, label the binding mode.
132+
self.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
133+
}
134+
// Set the default binding mode to by-value and return the old default binding mode so it
135+
// can be reinstated when we leave the pattern.
136+
self.default_mode_span.take()
137+
}
138+
139+
/// Restores the default binding mode after lowering a pattern that could change it.
140+
/// This should follow a call to either [`PatMigration::visit_explicit_deref`] or
141+
/// [`PatMigration::visit_implicit_derefs`].
142+
pub(super) fn leave_ref(&mut self, old_mode_span: Option<(Span, Mutability)>) {
143+
self.default_mode_span = old_mode_span
144+
}
145+
146+
/// Determines if a binding is relevant to the diagnostic and adjusts the notes/suggestion if
147+
/// so. Bindings are relevant if they have a modifier under a by-ref default mode (invalid in
148+
/// Rust 2024) or if we need to suggest a binding modifier for them.
149+
pub(super) fn visit_binding(
150+
&mut self,
151+
pat_span: Span,
152+
mode: BindingMode,
153+
explicit_ba: BindingMode,
154+
ident: Ident,
155+
) {
156+
if explicit_ba != BindingMode::NONE
157+
&& let Some((default_mode_span, default_ref_mutbl)) = self.default_mode_span
158+
{
159+
// If this overrides a by-ref default binding mode, label the binding mode.
160+
self.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
161+
// If our suggestion is to elide redundnt modes, this will be one of them.
162+
if self.info.suggest_eliding_modes {
163+
self.suggestion.push((pat_span.with_hi(ident.span.lo()), String::new()));
164+
self.binding_mode_count += 1;
165+
}
166+
}
167+
if !self.info.suggest_eliding_modes
168+
&& explicit_ba.0 == ByRef::No
169+
&& let ByRef::Yes(mutbl) = mode.0
170+
{
171+
// If we can't fix the pattern by eliding modifiers, we'll need to make the pattern
172+
// fully explicit. i.e. we'll need to suggest reference patterns for this.
173+
let sugg_str = match mutbl {
174+
Mutability::Not => "ref ",
175+
Mutability::Mut => "ref mut ",
176+
};
177+
self.suggestion
178+
.push((pat_span.with_lo(ident.span.lo()).shrink_to_lo(), sugg_str.to_owned()));
179+
self.binding_mode_count += 1;
180+
}
181+
}
87182
}

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

+10-57
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_abi::{FieldIdx, Integer};
1111
use rustc_errors::codes::*;
1212
use rustc_hir::def::{CtorOf, DefKind, Res};
1313
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
14-
use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd};
14+
use rustc_hir::{self as hir, RangeEnd};
1515
use rustc_index::Idx;
1616
use rustc_middle::mir::interpret::LitToConstInput;
1717
use rustc_middle::thir::{
@@ -65,31 +65,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
6565
let adjustments: &[Ty<'tcx>] =
6666
self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
6767

68+
// Track the default binding mode for the Rust 2024 migration suggestion.
6869
let mut opt_old_mode_span = None;
6970
if let Some(s) = &mut self.rust_2024_migration
7071
&& !adjustments.is_empty()
7172
{
72-
let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| {
73-
let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
74-
span_bug!(pat.span, "pattern implicitly dereferences a non-ref type");
75-
};
76-
mutbl
77-
});
78-
79-
if !s.info.suggest_eliding_modes {
80-
let suggestion_str: String =
81-
implicit_deref_mutbls.clone().map(|mutbl| mutbl.ref_prefix_str()).collect();
82-
s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
83-
s.ref_pattern_count += adjustments.len();
84-
}
85-
86-
// Remember if this changed the default binding mode, in case we want to label it.
87-
let min_mutbl = implicit_deref_mutbls.min().unwrap();
88-
if s.default_mode_span.is_none_or(|(_, old_mutbl)| min_mutbl < old_mutbl) {
89-
opt_old_mode_span = Some(s.default_mode_span);
90-
s.default_mode_span = Some((pat.span, min_mutbl));
91-
}
92-
};
73+
opt_old_mode_span = s.visit_implicit_derefs(pat.span, adjustments);
74+
}
9375

9476
// When implicit dereferences have been inserted in this pattern, the unadjusted lowered
9577
// pattern has the type that results *after* dereferencing. For example, in this code:
@@ -129,9 +111,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
129111
});
130112

131113
if let Some(s) = &mut self.rust_2024_migration
132-
&& let Some(old_mode_span) = opt_old_mode_span
114+
&& !adjustments.is_empty()
133115
{
134-
s.default_mode_span = old_mode_span;
116+
s.leave_ref(opt_old_mode_span);
135117
}
136118

137119
adjusted_pat
@@ -327,16 +309,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
327309
}
328310
hir::PatKind::Ref(subpattern, _) => {
329311
// Track the default binding mode for the Rust 2024 migration suggestion.
330-
let old_mode_span = self.rust_2024_migration.as_mut().and_then(|s| {
331-
if let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span {
332-
// If this eats a by-ref default binding mode, label the binding mode.
333-
s.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
334-
}
335-
s.default_mode_span.take()
336-
});
312+
let opt_old_mode_span =
313+
self.rust_2024_migration.as_mut().and_then(|s| s.visit_explicit_deref());
337314
let subpattern = self.lower_pattern(subpattern);
338315
if let Some(s) = &mut self.rust_2024_migration {
339-
s.default_mode_span = old_mode_span;
316+
s.leave_ref(opt_old_mode_span);
340317
}
341318
PatKind::Deref { subpattern }
342319
}
@@ -368,31 +345,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
368345
.expect("missing binding mode");
369346

370347
if let Some(s) = &mut self.rust_2024_migration {
371-
if explicit_ba != hir::BindingMode::NONE
372-
&& let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span
373-
{
374-
// If this overrides a by-ref default binding mode, label the binding mode.
375-
s.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
376-
// If our suggestion is to elide redundnt modes, this will be one of them.
377-
if s.info.suggest_eliding_modes {
378-
s.suggestion.push((pat.span.with_hi(ident.span.lo()), String::new()));
379-
s.binding_mode_count += 1;
380-
}
381-
}
382-
if !s.info.suggest_eliding_modes
383-
&& explicit_ba.0 == ByRef::No
384-
&& let ByRef::Yes(mutbl) = mode.0
385-
{
386-
let sugg_str = match mutbl {
387-
Mutability::Not => "ref ",
388-
Mutability::Mut => "ref mut ",
389-
};
390-
s.suggestion.push((
391-
pat.span.with_lo(ident.span.lo()).shrink_to_lo(),
392-
sugg_str.to_owned(),
393-
));
394-
s.binding_mode_count += 1;
395-
}
348+
s.visit_binding(pat.span, mode, explicit_ba, ident);
396349
}
397350

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

0 commit comments

Comments
 (0)