move pattern migration setup/emitting to a separate module
This commit is contained in:
parent
80c091958f
commit
e1c6eade16
3 changed files with 104 additions and 61 deletions
|
@ -1113,9 +1113,6 @@ pub(crate) struct Rust2024IncompatiblePatSugg {
|
|||
pub(crate) suggestion: Vec<(Span, String)>,
|
||||
pub(crate) ref_pattern_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.
|
||||
pub(crate) default_mode_labels: FxIndexMap<Span, ty::Mutability>,
|
||||
}
|
||||
|
|
87
compiler/rustc_mir_build/src/thir/pattern/migration.rs
Normal file
87
compiler/rustc_mir_build/src/thir/pattern/migration.rs
Normal 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,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,18 +2,17 @@
|
|||
|
||||
mod check_match;
|
||||
mod const_to_pat;
|
||||
mod migration;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustc_abi::{FieldIdx, Integer};
|
||||
use rustc_errors::MultiSpan;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
|
||||
use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd};
|
||||
use rustc_index::Idx;
|
||||
use rustc_lint as lint;
|
||||
use rustc_middle::mir::interpret::LitToConstInput;
|
||||
use rustc_middle::thir::{
|
||||
Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
|
||||
|
@ -26,8 +25,8 @@ use rustc_span::{ErrorGuaranteed, Span};
|
|||
use tracing::{debug, instrument};
|
||||
|
||||
pub(crate) use self::check_match::check_match;
|
||||
use self::migration::PatMigration;
|
||||
use crate::errors::*;
|
||||
use crate::fluent_generated as fluent;
|
||||
|
||||
struct PatCtxt<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
@ -35,7 +34,7 @@ struct PatCtxt<'a, 'tcx> {
|
|||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
|
||||
/// 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>(
|
||||
|
@ -44,59 +43,19 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
|
|||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
pat: &'tcx hir::Pat<'tcx>,
|
||||
) -> Box<Pat<'tcx>> {
|
||||
let migration_info = typeck_results.rust_2024_migration_desugared_pats().get(pat.hir_id);
|
||||
let mut pcx = PatCtxt {
|
||||
tcx,
|
||||
typing_env,
|
||||
typeck_results,
|
||||
rust_2024_migration_suggestion: migration_info.and_then(|info| {
|
||||
Some(Rust2024IncompatiblePatSugg {
|
||||
suggest_eliding_modes: info.suggest_eliding_modes,
|
||||
suggestion: Vec::new(),
|
||||
ref_pattern_count: 0,
|
||||
binding_mode_count: 0,
|
||||
default_mode_span: None,
|
||||
default_mode_labels: Default::default(),
|
||||
})
|
||||
}),
|
||||
rust_2024_migration: typeck_results
|
||||
.rust_2024_migration_desugared_pats()
|
||||
.get(pat.hir_id)
|
||||
.map(PatMigration::new),
|
||||
};
|
||||
let result = pcx.lower_pattern(pat);
|
||||
debug!("pat_from_hir({:?}) = {:?}", pat, result);
|
||||
if let Some(info) = migration_info
|
||||
&& let Some(sugg) = pcx.rust_2024_migration_suggestion
|
||||
{
|
||||
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,
|
||||
},
|
||||
);
|
||||
}
|
||||
if let Some(m) = pcx.rust_2024_migration {
|
||||
m.emit(tcx, pat.hir_id);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
@ -107,7 +66,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
|
||||
|
||||
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()
|
||||
{
|
||||
let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| {
|
||||
|
@ -117,7 +76,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
mutbl
|
||||
});
|
||||
|
||||
if !s.suggest_eliding_modes {
|
||||
if !s.info.suggest_eliding_modes {
|
||||
let suggestion_str: String =
|
||||
implicit_deref_mutbls.clone().map(|mutbl| mutbl.ref_prefix_str()).collect();
|
||||
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
|
||||
{
|
||||
s.default_mode_span = old_mode_span;
|
||||
|
@ -368,7 +327,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
}
|
||||
hir::PatKind::Ref(subpattern, _) => {
|
||||
// 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 this eats a by-ref default binding mode, label the binding mode.
|
||||
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()
|
||||
});
|
||||
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;
|
||||
}
|
||||
PatKind::Deref { subpattern }
|
||||
|
@ -408,19 +367,19 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
.get(pat.hir_id)
|
||||
.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
|
||||
&& 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.
|
||||
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 s.suggest_eliding_modes {
|
||||
if s.info.suggest_eliding_modes {
|
||||
s.suggestion.push((pat.span.with_hi(ident.span.lo()), String::new()));
|
||||
s.binding_mode_count += 1;
|
||||
}
|
||||
}
|
||||
if !s.suggest_eliding_modes
|
||||
if !s.info.suggest_eliding_modes
|
||||
&& explicit_ba.0 == ByRef::No
|
||||
&& let ByRef::Yes(mutbl) = mode.0
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue