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) 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>,
|
||||||
}
|
}
|
||||||
|
|
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 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
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue