move pattern migration internals to the migration
module
This commit is contained in:
parent
e1c6eade16
commit
f1c287f45b
2 changed files with 114 additions and 66 deletions
|
@ -2,10 +2,11 @@
|
|||
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::MultiSpan;
|
||||
use rustc_hir::HirId;
|
||||
use rustc_hir::{BindingMode, ByRef, HirId, Mutability};
|
||||
use rustc_lint as lint;
|
||||
use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, Ty, TyCtxt};
|
||||
use rustc_span::{Ident, Span};
|
||||
|
||||
use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg};
|
||||
use crate::fluent_generated as fluent;
|
||||
|
@ -13,20 +14,20 @@ 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,
|
||||
suggestion: Vec<(Span, String)>,
|
||||
ref_pattern_count: usize,
|
||||
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)>,
|
||||
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>,
|
||||
default_mode_labels: FxIndexMap<Span, Mutability>,
|
||||
/// Information collected from typeck, including spans for subpatterns invalid in Rust 2024.
|
||||
pub(super) info: &'a Rust2024IncompatiblePatInfo,
|
||||
info: &'a Rust2024IncompatiblePatInfo,
|
||||
}
|
||||
|
||||
impl<'a> PatMigration<'a> {
|
||||
|
@ -84,4 +85,98 @@ impl<'a> PatMigration<'a> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Tracks when we're lowering a pattern that implicitly dereferences the scrutinee.
|
||||
/// This should only be called when the pattern type adjustments list `adjustments` is
|
||||
/// non-empty. Returns the prior default binding mode; this should be followed by a call to
|
||||
/// [`PatMigration::leave_ref`] to restore it when we leave the pattern.
|
||||
pub(super) fn visit_implicit_derefs<'tcx>(
|
||||
&mut self,
|
||||
pat_span: Span,
|
||||
adjustments: &[Ty<'tcx>],
|
||||
) -> Option<(Span, Mutability)> {
|
||||
let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| {
|
||||
let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
|
||||
span_bug!(pat_span, "pattern implicitly dereferences a non-ref type");
|
||||
};
|
||||
mutbl
|
||||
});
|
||||
|
||||
if !self.info.suggest_eliding_modes {
|
||||
// If we can't fix the pattern by eliding modifiers, we'll need to make the pattern
|
||||
// fully explicit. i.e. we'll need to suggest reference patterns for this.
|
||||
let suggestion_str: String =
|
||||
implicit_deref_mutbls.clone().map(|mutbl| mutbl.ref_prefix_str()).collect();
|
||||
self.suggestion.push((pat_span.shrink_to_lo(), suggestion_str));
|
||||
self.ref_pattern_count += adjustments.len();
|
||||
}
|
||||
|
||||
// Remember if this changed the default binding mode, in case we want to label it.
|
||||
let min_mutbl = implicit_deref_mutbls.min().unwrap();
|
||||
if self.default_mode_span.is_none_or(|(_, old_mutbl)| min_mutbl < old_mutbl) {
|
||||
// This changes the default binding mode to `ref` or `ref mut`. Return the old mode so
|
||||
// it can be reinstated when we leave the pattern.
|
||||
self.default_mode_span.replace((pat_span, min_mutbl))
|
||||
} else {
|
||||
// This does not change the default binding mode; it was already `ref` or `ref mut`.
|
||||
self.default_mode_span
|
||||
}
|
||||
}
|
||||
|
||||
/// Tracks the default binding mode when we're lowering a `&` or `&mut` pattern.
|
||||
/// Returns the prior default binding mode; this should be followed by a call to
|
||||
/// [`PatMigration::leave_ref`] to restore it when we leave the pattern.
|
||||
pub(super) fn visit_explicit_deref(&mut self) -> Option<(Span, Mutability)> {
|
||||
if let Some((default_mode_span, default_ref_mutbl)) = self.default_mode_span {
|
||||
// If this eats a by-ref default binding mode, label the binding mode.
|
||||
self.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
|
||||
}
|
||||
// Set the default binding mode to by-value and return the old default binding mode so it
|
||||
// can be reinstated when we leave the pattern.
|
||||
self.default_mode_span.take()
|
||||
}
|
||||
|
||||
/// Restores the default binding mode after lowering a pattern that could change it.
|
||||
/// This should follow a call to either [`PatMigration::visit_explicit_deref`] or
|
||||
/// [`PatMigration::visit_implicit_derefs`].
|
||||
pub(super) fn leave_ref(&mut self, old_mode_span: Option<(Span, Mutability)>) {
|
||||
self.default_mode_span = old_mode_span
|
||||
}
|
||||
|
||||
/// Determines if a binding is relevant to the diagnostic and adjusts the notes/suggestion if
|
||||
/// so. Bindings are relevant if they have a modifier under a by-ref default mode (invalid in
|
||||
/// Rust 2024) or if we need to suggest a binding modifier for them.
|
||||
pub(super) fn visit_binding(
|
||||
&mut self,
|
||||
pat_span: Span,
|
||||
mode: BindingMode,
|
||||
explicit_ba: BindingMode,
|
||||
ident: Ident,
|
||||
) {
|
||||
if explicit_ba != BindingMode::NONE
|
||||
&& let Some((default_mode_span, default_ref_mutbl)) = self.default_mode_span
|
||||
{
|
||||
// If this overrides a by-ref default binding mode, label the binding mode.
|
||||
self.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 self.info.suggest_eliding_modes {
|
||||
self.suggestion.push((pat_span.with_hi(ident.span.lo()), String::new()));
|
||||
self.binding_mode_count += 1;
|
||||
}
|
||||
}
|
||||
if !self.info.suggest_eliding_modes
|
||||
&& explicit_ba.0 == ByRef::No
|
||||
&& let ByRef::Yes(mutbl) = mode.0
|
||||
{
|
||||
// If we can't fix the pattern by eliding modifiers, we'll need to make the pattern
|
||||
// fully explicit. i.e. we'll need to suggest reference patterns for this.
|
||||
let sugg_str = match mutbl {
|
||||
Mutability::Not => "ref ",
|
||||
Mutability::Mut => "ref mut ",
|
||||
};
|
||||
self.suggestion
|
||||
.push((pat_span.with_lo(ident.span.lo()).shrink_to_lo(), sugg_str.to_owned()));
|
||||
self.binding_mode_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use rustc_abi::{FieldIdx, Integer};
|
|||
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_hir::{self as hir, RangeEnd};
|
||||
use rustc_index::Idx;
|
||||
use rustc_middle::mir::interpret::LitToConstInput;
|
||||
use rustc_middle::thir::{
|
||||
|
@ -65,31 +65,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
let adjustments: &[Ty<'tcx>] =
|
||||
self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
|
||||
|
||||
// Track the default binding mode for the Rust 2024 migration suggestion.
|
||||
let mut opt_old_mode_span = None;
|
||||
if let Some(s) = &mut self.rust_2024_migration
|
||||
&& !adjustments.is_empty()
|
||||
{
|
||||
let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| {
|
||||
let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
|
||||
span_bug!(pat.span, "pattern implicitly dereferences a non-ref type");
|
||||
};
|
||||
mutbl
|
||||
});
|
||||
|
||||
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));
|
||||
s.ref_pattern_count += adjustments.len();
|
||||
}
|
||||
|
||||
// Remember if this changed the default binding mode, in case we want to label it.
|
||||
let min_mutbl = implicit_deref_mutbls.min().unwrap();
|
||||
if s.default_mode_span.is_none_or(|(_, old_mutbl)| min_mutbl < old_mutbl) {
|
||||
opt_old_mode_span = Some(s.default_mode_span);
|
||||
s.default_mode_span = Some((pat.span, min_mutbl));
|
||||
}
|
||||
};
|
||||
opt_old_mode_span = s.visit_implicit_derefs(pat.span, adjustments);
|
||||
}
|
||||
|
||||
// When implicit dereferences have been inserted in this pattern, the unadjusted lowered
|
||||
// pattern has the type that results *after* dereferencing. For example, in this code:
|
||||
|
@ -129,9 +111,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
});
|
||||
|
||||
if let Some(s) = &mut self.rust_2024_migration
|
||||
&& let Some(old_mode_span) = opt_old_mode_span
|
||||
&& !adjustments.is_empty()
|
||||
{
|
||||
s.default_mode_span = old_mode_span;
|
||||
s.leave_ref(opt_old_mode_span);
|
||||
}
|
||||
|
||||
adjusted_pat
|
||||
|
@ -327,16 +309,11 @@ 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.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);
|
||||
}
|
||||
s.default_mode_span.take()
|
||||
});
|
||||
let opt_old_mode_span =
|
||||
self.rust_2024_migration.as_mut().and_then(|s| s.visit_explicit_deref());
|
||||
let subpattern = self.lower_pattern(subpattern);
|
||||
if let Some(s) = &mut self.rust_2024_migration {
|
||||
s.default_mode_span = old_mode_span;
|
||||
s.leave_ref(opt_old_mode_span);
|
||||
}
|
||||
PatKind::Deref { subpattern }
|
||||
}
|
||||
|
@ -368,31 +345,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
.expect("missing binding mode");
|
||||
|
||||
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.info.suggest_eliding_modes {
|
||||
s.suggestion.push((pat.span.with_hi(ident.span.lo()), String::new()));
|
||||
s.binding_mode_count += 1;
|
||||
}
|
||||
}
|
||||
if !s.info.suggest_eliding_modes
|
||||
&& explicit_ba.0 == ByRef::No
|
||||
&& let ByRef::Yes(mutbl) = mode.0
|
||||
{
|
||||
let sugg_str = match mutbl {
|
||||
Mutability::Not => "ref ",
|
||||
Mutability::Mut => "ref mut ",
|
||||
};
|
||||
s.suggestion.push((
|
||||
pat.span.with_lo(ident.span.lo()).shrink_to_lo(),
|
||||
sugg_str.to_owned(),
|
||||
));
|
||||
s.binding_mode_count += 1;
|
||||
}
|
||||
s.visit_binding(pat.span, mode, explicit_ba, ident);
|
||||
}
|
||||
|
||||
// A ref x pattern is the same node used for x, and as such it has
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue