Skip to content

Commit 396ebaa

Browse files
committed
cleanup: move pattern migration to a separate file
1 parent 613bdd4 commit 396ebaa

File tree

3 files changed

+118
-72
lines changed

3 files changed

+118
-72
lines changed

compiler/rustc_mir_build/src/errors.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,20 +1097,18 @@ pub(crate) enum MiscPatternSuggestion {
10971097

10981098
#[derive(LintDiagnostic)]
10991099
#[diag(mir_build_rust_2024_incompatible_pat)]
1100-
pub(crate) struct Rust2024IncompatiblePat<'a> {
1100+
pub(crate) struct Rust2024IncompatiblePat {
11011101
#[subdiagnostic]
1102-
pub(crate) sugg: Rust2024IncompatiblePatSugg<'a>,
1102+
pub(crate) sugg: Rust2024IncompatiblePatSugg,
11031103
}
11041104

1105-
pub(crate) struct Rust2024IncompatiblePatSugg<'a> {
1105+
pub(crate) struct Rust2024IncompatiblePatSugg {
11061106
pub(crate) suggestion: Vec<(Span, String)>,
11071107
pub(crate) ref_pattern_count: usize,
11081108
pub(crate) binding_mode_count: usize,
1109-
/// Labeled spans for subpatterns invalid in Rust 2024.
1110-
pub(crate) labels: &'a [(Span, String)],
11111109
}
11121110

1113-
impl<'a> Subdiagnostic for Rust2024IncompatiblePatSugg<'a> {
1111+
impl Subdiagnostic for Rust2024IncompatiblePatSugg {
11141112
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
11151113
self,
11161114
diag: &mut Diag<'_, G>,
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
//! Automatic migration of Rust 2021 patterns to a form valid in both Editions 2021 and 2024.
2+
3+
use rustc_ast::BindingMode;
4+
use rustc_errors::MultiSpan;
5+
use rustc_hir::{ByRef, HirId, Mutability};
6+
use rustc_lint as lint;
7+
use rustc_middle::span_bug;
8+
use rustc_middle::ty::{self, 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+
/// Labeled spans for subpatterns invalid in Rust 2024.
21+
labels: &'a [(Span, String)],
22+
}
23+
24+
impl<'a> PatMigration<'a> {
25+
pub(super) fn new(labels: &'a Vec<(Span, String)>) -> Self {
26+
PatMigration {
27+
suggestion: Vec::new(),
28+
ref_pattern_count: 0,
29+
binding_mode_count: 0,
30+
labels: labels.as_slice(),
31+
}
32+
}
33+
34+
/// On Rust 2024, this emits a hard error. On earlier Editions, this emits the
35+
/// future-incompatibility lint `rust_2024_incompatible_pat`.
36+
pub(super) fn emit<'tcx>(self, tcx: TyCtxt<'tcx>, pat_id: HirId) {
37+
let mut spans = MultiSpan::from_spans(self.labels.iter().map(|(span, _)| *span).collect());
38+
for (span, label) in self.labels {
39+
spans.push_span_label(*span, label.clone());
40+
}
41+
let sugg = Rust2024IncompatiblePatSugg {
42+
suggestion: self.suggestion,
43+
ref_pattern_count: self.ref_pattern_count,
44+
binding_mode_count: self.binding_mode_count,
45+
};
46+
// If a relevant span is from at least edition 2024, this is a hard error.
47+
let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());
48+
if is_hard_error {
49+
let mut err =
50+
tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);
51+
if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
52+
// provide the same reference link as the lint
53+
err.note(format!("for more information, see {}", info.reference));
54+
}
55+
err.subdiagnostic(sugg);
56+
err.emit();
57+
} else {
58+
tcx.emit_node_span_lint(
59+
lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
60+
pat_id,
61+
spans,
62+
Rust2024IncompatiblePat { sugg },
63+
);
64+
}
65+
}
66+
67+
pub(super) fn visit_implicit_derefs<'tcx>(&mut self, pat_span: Span, adjustments: &[Ty<'tcx>]) {
68+
let suggestion_str: String = adjustments
69+
.iter()
70+
.map(|ref_ty| {
71+
let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
72+
span_bug!(pat_span, "pattern implicitly dereferences a non-ref type");
73+
};
74+
75+
mutbl.ref_prefix_str()
76+
})
77+
.collect();
78+
self.suggestion.push((pat_span.shrink_to_lo(), suggestion_str));
79+
self.ref_pattern_count += adjustments.len();
80+
}
81+
82+
pub(super) fn visit_binding(
83+
&mut self,
84+
pat_span: Span,
85+
mode: BindingMode,
86+
explicit_ba: BindingMode,
87+
ident: Ident,
88+
) {
89+
if explicit_ba.0 == ByRef::No
90+
&& let ByRef::Yes(mutbl) = mode.0
91+
{
92+
let sugg_str = match mutbl {
93+
Mutability::Not => "ref ",
94+
Mutability::Mut => "ref mut ",
95+
};
96+
self.suggestion
97+
.push((pat_span.with_lo(ident.span.lo()).shrink_to_lo(), sugg_str.to_owned()));
98+
self.binding_mode_count += 1;
99+
}
100+
}
101+
}

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

Lines changed: 13 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,16 @@
22
33
mod check_match;
44
mod const_to_pat;
5+
mod migration;
56

67
use std::cmp::Ordering;
78

89
use rustc_abi::{FieldIdx, Integer};
9-
use rustc_errors::MultiSpan;
1010
use rustc_errors::codes::*;
1111
use rustc_hir::def::{CtorOf, DefKind, Res};
1212
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
13-
use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd};
13+
use rustc_hir::{self as hir, RangeEnd};
1414
use rustc_index::Idx;
15-
use rustc_lint as lint;
1615
use rustc_middle::mir::interpret::LitToConstInput;
1716
use rustc_middle::thir::{
1817
Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
@@ -25,8 +24,8 @@ use rustc_span::{ErrorGuaranteed, Span};
2524
use tracing::{debug, instrument};
2625

2726
pub(crate) use self::check_match::check_match;
27+
use self::migration::PatMigration;
2828
use crate::errors::*;
29-
use crate::fluent_generated as fluent;
3029
use crate::thir::util::UserAnnotatedTyHelpers;
3130

3231
struct PatCtxt<'a, 'tcx> {
@@ -35,7 +34,7 @@ struct PatCtxt<'a, 'tcx> {
3534
typeck_results: &'a ty::TypeckResults<'tcx>,
3635

3736
/// Used by the Rust 2024 migration lint.
38-
rust_2024_migration_suggestion: Option<Rust2024IncompatiblePatSugg<'a>>,
37+
rust_2024_migration: Option<PatMigration<'a>>,
3938
}
4039

4140
pub(super) fn pat_from_hir<'a, 'tcx>(
@@ -48,42 +47,15 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
4847
tcx,
4948
typing_env,
5049
typeck_results,
51-
rust_2024_migration_suggestion: typeck_results
50+
rust_2024_migration: typeck_results
5251
.rust_2024_migration_desugared_pats()
5352
.get(pat.hir_id)
54-
.map(|labels| Rust2024IncompatiblePatSugg {
55-
suggestion: Vec::new(),
56-
ref_pattern_count: 0,
57-
binding_mode_count: 0,
58-
labels: labels.as_slice(),
59-
}),
53+
.map(PatMigration::new),
6054
};
6155
let result = pcx.lower_pattern(pat);
6256
debug!("pat_from_hir({:?}) = {:?}", pat, result);
63-
if let Some(sugg) = pcx.rust_2024_migration_suggestion {
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 {
71-
let mut err =
72-
tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);
73-
if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
74-
// provide the same reference link as the lint
75-
err.note(format!("for more information, see {}", info.reference));
76-
}
77-
err.subdiagnostic(sugg);
78-
err.emit();
79-
} else {
80-
tcx.emit_node_span_lint(
81-
lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
82-
pat.hir_id,
83-
spans,
84-
Rust2024IncompatiblePat { sugg },
85-
);
86-
}
57+
if let Some(m) = pcx.rust_2024_migration {
58+
m.emit(tcx, pat.hir_id);
8759
}
8860
result
8961
}
@@ -129,25 +101,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
129101
})
130102
});
131103

132-
if let Some(s) = &mut self.rust_2024_migration_suggestion
104+
if let Some(m) = &mut self.rust_2024_migration
133105
&& !adjustments.is_empty()
134106
{
135-
let suggestion_str: String = adjustments
136-
.iter()
137-
.map(|ref_ty| {
138-
let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
139-
span_bug!(pat.span, "pattern implicitly dereferences a non-ref type");
140-
};
141-
142-
match mutbl {
143-
ty::Mutability::Not => "&",
144-
ty::Mutability::Mut => "&mut ",
145-
}
146-
})
147-
.collect();
148-
s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
149-
s.ref_pattern_count += adjustments.len();
150-
};
107+
m.visit_implicit_derefs(pat.span, adjustments);
108+
}
151109

152110
adjusted_pat
153111
}
@@ -364,19 +322,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
364322
.get(pat.hir_id)
365323
.expect("missing binding mode");
366324

367-
if let Some(s) = &mut self.rust_2024_migration_suggestion
368-
&& explicit_ba.0 == ByRef::No
369-
&& let ByRef::Yes(mutbl) = mode.0
370-
{
371-
let sugg_str = match mutbl {
372-
Mutability::Not => "ref ",
373-
Mutability::Mut => "ref mut ",
374-
};
375-
s.suggestion.push((
376-
pat.span.with_lo(ident.span.lo()).shrink_to_lo(),
377-
sugg_str.to_owned(),
378-
));
379-
s.binding_mode_count += 1;
325+
if let Some(m) = &mut self.rust_2024_migration {
326+
m.visit_binding(pat.span, mode, explicit_ba, ident);
380327
}
381328

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

0 commit comments

Comments
 (0)