Skip to content

Commit 3ec5d8b

Browse files
authored
Unrolled build for rust-lang#136817
Rollup merge of rust-lang#136817 - dianne:clean-and-comment-pat-migration, r=Nadrieril Pattern Migration 2024: clean up and comment This follows up on rust-lang#136577 by moving the pattern migration logic to its own module, removing a bit of unnecessary complexity, and adding comments. Since there's quite a bit of pattern migration logic now (and potentially more in rust-lang#136496), I think it makes sense to keep it separate from THIR construction, at least as much as is convenient. r? ``@Nadrieril``
2 parents d5eb31c + f1c287f commit 3ec5d8b

File tree

3 files changed

+205
-114
lines changed

3 files changed

+205
-114
lines changed

compiler/rustc_mir_build/src/errors.rs

-3
Original file line numberDiff line numberDiff line change
@@ -1113,9 +1113,6 @@ pub(crate) struct Rust2024IncompatiblePatSugg {
11131113
pub(crate) suggestion: Vec<(Span, String)>,
11141114
pub(crate) ref_pattern_count: usize,
11151115
pub(crate) binding_mode_count: usize,
1116-
/// Internal state: the ref-mutability of the default binding mode at the subpattern being
1117-
/// lowered, with the span where it was introduced. `None` for a by-value default mode.
1118-
pub(crate) default_mode_span: Option<(Span, ty::Mutability)>,
11191116
/// Labels for where incompatibility-causing by-ref default binding modes were introduced.
11201117
pub(crate) default_mode_labels: FxIndexMap<Span, ty::Mutability>,
11211118
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
//! Automatic migration of Rust 2021 patterns to a form valid in both Editions 2021 and 2024.
2+
3+
use rustc_data_structures::fx::FxIndexMap;
4+
use rustc_errors::MultiSpan;
5+
use rustc_hir::{BindingMode, ByRef, HirId, Mutability};
6+
use rustc_lint as lint;
7+
use rustc_middle::span_bug;
8+
use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, Ty, TyCtxt};
9+
use rustc_span::{Ident, Span};
10+
11+
use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg};
12+
use crate::fluent_generated as fluent;
13+
14+
/// For patterns flagged for migration during HIR typeck, this handles constructing and emitting
15+
/// a diagnostic suggestion.
16+
pub(super) struct PatMigration<'a> {
17+
suggestion: Vec<(Span, String)>,
18+
ref_pattern_count: usize,
19+
binding_mode_count: usize,
20+
/// Internal state: the ref-mutability of the default binding mode at the subpattern being
21+
/// lowered, with the span where it was introduced. `None` for a by-value default mode.
22+
default_mode_span: Option<(Span, ty::Mutability)>,
23+
/// Labels for where incompatibility-causing by-ref default binding modes were introduced.
24+
// FIXME(ref_pat_eat_one_layer_2024_structural): To track the default binding mode, we duplicate
25+
// logic from HIR typeck (in order to avoid needing to store all changes to the dbm in
26+
// TypeckResults). Since the default binding mode acts differently under this feature gate, the
27+
// labels will be wrong.
28+
default_mode_labels: FxIndexMap<Span, Mutability>,
29+
/// Information collected from typeck, including spans for subpatterns invalid in Rust 2024.
30+
info: &'a Rust2024IncompatiblePatInfo,
31+
}
32+
33+
impl<'a> PatMigration<'a> {
34+
pub(super) fn new(info: &'a Rust2024IncompatiblePatInfo) -> Self {
35+
PatMigration {
36+
suggestion: Vec::new(),
37+
ref_pattern_count: 0,
38+
binding_mode_count: 0,
39+
default_mode_span: None,
40+
default_mode_labels: Default::default(),
41+
info,
42+
}
43+
}
44+
45+
/// On Rust 2024, this emits a hard error. On earlier Editions, this emits the
46+
/// future-incompatibility lint `rust_2024_incompatible_pat`.
47+
pub(super) fn emit<'tcx>(self, tcx: TyCtxt<'tcx>, pat_id: HirId) {
48+
let mut spans =
49+
MultiSpan::from_spans(self.info.primary_labels.iter().map(|(span, _)| *span).collect());
50+
for (span, label) in self.info.primary_labels.iter() {
51+
spans.push_span_label(*span, label.clone());
52+
}
53+
let sugg = Rust2024IncompatiblePatSugg {
54+
suggest_eliding_modes: self.info.suggest_eliding_modes,
55+
suggestion: self.suggestion,
56+
ref_pattern_count: self.ref_pattern_count,
57+
binding_mode_count: self.binding_mode_count,
58+
default_mode_labels: self.default_mode_labels,
59+
};
60+
// If a relevant span is from at least edition 2024, this is a hard error.
61+
let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());
62+
if is_hard_error {
63+
let mut err =
64+
tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);
65+
if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
66+
// provide the same reference link as the lint
67+
err.note(format!("for more information, see {}", info.reference));
68+
}
69+
err.arg("bad_modifiers", self.info.bad_modifiers);
70+
err.arg("bad_ref_pats", self.info.bad_ref_pats);
71+
err.arg("is_hard_error", true);
72+
err.subdiagnostic(sugg);
73+
err.emit();
74+
} else {
75+
tcx.emit_node_span_lint(
76+
lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
77+
pat_id,
78+
spans,
79+
Rust2024IncompatiblePat {
80+
sugg,
81+
bad_modifiers: self.info.bad_modifiers,
82+
bad_ref_pats: self.info.bad_ref_pats,
83+
is_hard_error,
84+
},
85+
);
86+
}
87+
}
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+
}
182+
}

0 commit comments

Comments
 (0)