1
Fork 0

move pattern migration setup/emitting to a separate module

This commit is contained in:
dianne 2025-02-09 04:11:23 -08:00
parent 80c091958f
commit e1c6eade16
3 changed files with 104 additions and 61 deletions

View file

@ -1113,9 +1113,6 @@ pub(crate) struct Rust2024IncompatiblePatSugg {
pub(crate) suggestion: Vec<(Span, String)>, pub(crate) suggestion: Vec<(Span, String)>,
pub(crate) ref_pattern_count: usize, pub(crate) ref_pattern_count: usize,
pub(crate) binding_mode_count: usize, pub(crate) binding_mode_count: usize,
/// Internal state: the ref-mutability of the default binding mode at the subpattern being
/// lowered, with the span where it was introduced. `None` for a by-value default mode.
pub(crate) default_mode_span: Option<(Span, ty::Mutability)>,
/// Labels for where incompatibility-causing by-ref default binding modes were introduced. /// Labels for where incompatibility-causing by-ref default binding modes were introduced.
pub(crate) default_mode_labels: FxIndexMap<Span, ty::Mutability>, pub(crate) default_mode_labels: FxIndexMap<Span, ty::Mutability>,
} }

View file

@ -0,0 +1,87 @@
//! Automatic migration of Rust 2021 patterns to a form valid in both Editions 2021 and 2024.
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::MultiSpan;
use rustc_hir::HirId;
use rustc_lint as lint;
use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, TyCtxt};
use rustc_span::Span;
use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg};
use crate::fluent_generated as fluent;
/// For patterns flagged for migration during HIR typeck, this handles constructing and emitting
/// a diagnostic suggestion.
pub(super) struct PatMigration<'a> {
pub(super) suggestion: Vec<(Span, String)>,
pub(super) ref_pattern_count: usize,
pub(super) binding_mode_count: usize,
/// Internal state: the ref-mutability of the default binding mode at the subpattern being
/// lowered, with the span where it was introduced. `None` for a by-value default mode.
pub(super) default_mode_span: Option<(Span, ty::Mutability)>,
/// Labels for where incompatibility-causing by-ref default binding modes were introduced.
// FIXME(ref_pat_eat_one_layer_2024_structural): To track the default binding mode, we duplicate
// logic from HIR typeck (in order to avoid needing to store all changes to the dbm in
// TypeckResults). Since the default binding mode acts differently under this feature gate, the
// labels will be wrong.
pub(super) default_mode_labels: FxIndexMap<Span, Mutability>,
/// Information collected from typeck, including spans for subpatterns invalid in Rust 2024.
pub(super) info: &'a Rust2024IncompatiblePatInfo,
}
impl<'a> PatMigration<'a> {
pub(super) fn new(info: &'a Rust2024IncompatiblePatInfo) -> Self {
PatMigration {
suggestion: Vec::new(),
ref_pattern_count: 0,
binding_mode_count: 0,
default_mode_span: None,
default_mode_labels: Default::default(),
info,
}
}
/// On Rust 2024, this emits a hard error. On earlier Editions, this emits the
/// future-incompatibility lint `rust_2024_incompatible_pat`.
pub(super) fn emit<'tcx>(self, tcx: TyCtxt<'tcx>, pat_id: HirId) {
let mut spans =
MultiSpan::from_spans(self.info.primary_labels.iter().map(|(span, _)| *span).collect());
for (span, label) in self.info.primary_labels.iter() {
spans.push_span_label(*span, label.clone());
}
let sugg = Rust2024IncompatiblePatSugg {
suggest_eliding_modes: self.info.suggest_eliding_modes,
suggestion: self.suggestion,
ref_pattern_count: self.ref_pattern_count,
binding_mode_count: self.binding_mode_count,
default_mode_labels: self.default_mode_labels,
};
// If a relevant span is from at least edition 2024, this is a hard error.
let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());
if is_hard_error {
let mut err =
tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);
if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
// provide the same reference link as the lint
err.note(format!("for more information, see {}", info.reference));
}
err.arg("bad_modifiers", self.info.bad_modifiers);
err.arg("bad_ref_pats", self.info.bad_ref_pats);
err.arg("is_hard_error", true);
err.subdiagnostic(sugg);
err.emit();
} else {
tcx.emit_node_span_lint(
lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
pat_id,
spans,
Rust2024IncompatiblePat {
sugg,
bad_modifiers: self.info.bad_modifiers,
bad_ref_pats: self.info.bad_ref_pats,
is_hard_error,
},
);
}
}
}

View file

@ -2,18 +2,17 @@
mod check_match; mod check_match;
mod const_to_pat; mod const_to_pat;
mod migration;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::sync::Arc; use std::sync::Arc;
use rustc_abi::{FieldIdx, Integer}; use rustc_abi::{FieldIdx, Integer};
use rustc_errors::MultiSpan;
use rustc_errors::codes::*; use rustc_errors::codes::*;
use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::pat_util::EnumerateAndAdjustIterator;
use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd}; use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd};
use rustc_index::Idx; use rustc_index::Idx;
use rustc_lint as lint;
use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::mir::interpret::LitToConstInput;
use rustc_middle::thir::{ use rustc_middle::thir::{
Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
@ -26,8 +25,8 @@ use rustc_span::{ErrorGuaranteed, Span};
use tracing::{debug, instrument}; use tracing::{debug, instrument};
pub(crate) use self::check_match::check_match; pub(crate) use self::check_match::check_match;
use self::migration::PatMigration;
use crate::errors::*; use crate::errors::*;
use crate::fluent_generated as fluent;
struct PatCtxt<'a, 'tcx> { struct PatCtxt<'a, 'tcx> {
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
@ -35,7 +34,7 @@ struct PatCtxt<'a, 'tcx> {
typeck_results: &'a ty::TypeckResults<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>,
/// Used by the Rust 2024 migration lint. /// Used by the Rust 2024 migration lint.
rust_2024_migration_suggestion: Option<Rust2024IncompatiblePatSugg>, rust_2024_migration: Option<PatMigration<'a>>,
} }
pub(super) fn pat_from_hir<'a, 'tcx>( pub(super) fn pat_from_hir<'a, 'tcx>(
@ -44,59 +43,19 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
typeck_results: &'a ty::TypeckResults<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>,
pat: &'tcx hir::Pat<'tcx>, pat: &'tcx hir::Pat<'tcx>,
) -> Box<Pat<'tcx>> { ) -> Box<Pat<'tcx>> {
let migration_info = typeck_results.rust_2024_migration_desugared_pats().get(pat.hir_id);
let mut pcx = PatCtxt { let mut pcx = PatCtxt {
tcx, tcx,
typing_env, typing_env,
typeck_results, typeck_results,
rust_2024_migration_suggestion: migration_info.and_then(|info| { rust_2024_migration: typeck_results
Some(Rust2024IncompatiblePatSugg { .rust_2024_migration_desugared_pats()
suggest_eliding_modes: info.suggest_eliding_modes, .get(pat.hir_id)
suggestion: Vec::new(), .map(PatMigration::new),
ref_pattern_count: 0,
binding_mode_count: 0,
default_mode_span: None,
default_mode_labels: Default::default(),
})
}),
}; };
let result = pcx.lower_pattern(pat); let result = pcx.lower_pattern(pat);
debug!("pat_from_hir({:?}) = {:?}", pat, result); debug!("pat_from_hir({:?}) = {:?}", pat, result);
if let Some(info) = migration_info if let Some(m) = pcx.rust_2024_migration {
&& let Some(sugg) = pcx.rust_2024_migration_suggestion m.emit(tcx, pat.hir_id);
{
let mut spans =
MultiSpan::from_spans(info.primary_labels.iter().map(|(span, _)| *span).collect());
for (span, label) in &info.primary_labels {
spans.push_span_label(*span, label.clone());
}
// If a relevant span is from at least edition 2024, this is a hard error.
let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());
if is_hard_error {
let mut err =
tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);
if let Some(lint_info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
// provide the same reference link as the lint
err.note(format!("for more information, see {}", lint_info.reference));
}
err.arg("bad_modifiers", info.bad_modifiers);
err.arg("bad_ref_pats", info.bad_ref_pats);
err.arg("is_hard_error", true);
err.subdiagnostic(sugg);
err.emit();
} else {
tcx.emit_node_span_lint(
lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
pat.hir_id,
spans,
Rust2024IncompatiblePat {
sugg,
bad_modifiers: info.bad_modifiers,
bad_ref_pats: info.bad_ref_pats,
is_hard_error,
},
);
}
} }
result result
} }
@ -107,7 +66,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v); self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
let mut opt_old_mode_span = None; let mut opt_old_mode_span = None;
if let Some(s) = &mut self.rust_2024_migration_suggestion if let Some(s) = &mut self.rust_2024_migration
&& !adjustments.is_empty() && !adjustments.is_empty()
{ {
let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| { let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| {
@ -117,7 +76,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
mutbl mutbl
}); });
if !s.suggest_eliding_modes { if !s.info.suggest_eliding_modes {
let suggestion_str: String = let suggestion_str: String =
implicit_deref_mutbls.clone().map(|mutbl| mutbl.ref_prefix_str()).collect(); implicit_deref_mutbls.clone().map(|mutbl| mutbl.ref_prefix_str()).collect();
s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str)); s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
@ -169,7 +128,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
}) })
}); });
if let Some(s) = &mut self.rust_2024_migration_suggestion if let Some(s) = &mut self.rust_2024_migration
&& let Some(old_mode_span) = opt_old_mode_span && let Some(old_mode_span) = opt_old_mode_span
{ {
s.default_mode_span = old_mode_span; s.default_mode_span = old_mode_span;
@ -368,7 +327,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
} }
hir::PatKind::Ref(subpattern, _) => { hir::PatKind::Ref(subpattern, _) => {
// Track the default binding mode for the Rust 2024 migration suggestion. // Track the default binding mode for the Rust 2024 migration suggestion.
let old_mode_span = self.rust_2024_migration_suggestion.as_mut().and_then(|s| { let old_mode_span = self.rust_2024_migration.as_mut().and_then(|s| {
if let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span { if let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span {
// If this eats a by-ref default binding mode, label the binding mode. // If this eats a by-ref default binding mode, label the binding mode.
s.default_mode_labels.insert(default_mode_span, default_ref_mutbl); s.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
@ -376,7 +335,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
s.default_mode_span.take() s.default_mode_span.take()
}); });
let subpattern = self.lower_pattern(subpattern); let subpattern = self.lower_pattern(subpattern);
if let Some(s) = &mut self.rust_2024_migration_suggestion { if let Some(s) = &mut self.rust_2024_migration {
s.default_mode_span = old_mode_span; s.default_mode_span = old_mode_span;
} }
PatKind::Deref { subpattern } PatKind::Deref { subpattern }
@ -408,19 +367,19 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
.get(pat.hir_id) .get(pat.hir_id)
.expect("missing binding mode"); .expect("missing binding mode");
if let Some(s) = &mut self.rust_2024_migration_suggestion { if let Some(s) = &mut self.rust_2024_migration {
if explicit_ba != hir::BindingMode::NONE if explicit_ba != hir::BindingMode::NONE
&& let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span && let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span
{ {
// If this overrides a by-ref default binding mode, label the binding mode. // If this overrides a by-ref default binding mode, label the binding mode.
s.default_mode_labels.insert(default_mode_span, default_ref_mutbl); s.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
// If our suggestion is to elide redundnt modes, this will be one of them. // If our suggestion is to elide redundnt modes, this will be one of them.
if s.suggest_eliding_modes { if s.info.suggest_eliding_modes {
s.suggestion.push((pat.span.with_hi(ident.span.lo()), String::new())); s.suggestion.push((pat.span.with_hi(ident.span.lo()), String::new()));
s.binding_mode_count += 1; s.binding_mode_count += 1;
} }
} }
if !s.suggest_eliding_modes if !s.info.suggest_eliding_modes
&& explicit_ba.0 == ByRef::No && explicit_ba.0 == ByRef::No
&& let ByRef::Yes(mutbl) = mode.0 && let ByRef::Yes(mutbl) = mode.0
{ {