From e1c6eade1619e2b84b39d61811407afc1f82fd12 Mon Sep 17 00:00:00 2001 From: dianne Date: Sun, 9 Feb 2025 04:11:23 -0800 Subject: [PATCH 1/2] move pattern migration setup/emitting to a separate module --- compiler/rustc_mir_build/src/errors.rs | 3 - .../src/thir/pattern/migration.rs | 87 +++++++++++++++++++ .../rustc_mir_build/src/thir/pattern/mod.rs | 75 ++++------------ 3 files changed, 104 insertions(+), 61 deletions(-) create mode 100644 compiler/rustc_mir_build/src/thir/pattern/migration.rs diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 07bdc59756aa7..9f8b52165ab90 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -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, } diff --git a/compiler/rustc_mir_build/src/thir/pattern/migration.rs b/compiler/rustc_mir_build/src/thir/pattern/migration.rs new file mode 100644 index 0000000000000..be1ebd57d1182 --- /dev/null +++ b/compiler/rustc_mir_build/src/thir/pattern/migration.rs @@ -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, + /// 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, + }, + ); + } + } +} diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 83fef7b0de6f5..5535ac9bd636f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -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, + rust_2024_migration: Option>, } 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> { - 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 { From f1c287f45bace992857b0a9c850da6f1a849a6f7 Mon Sep 17 00:00:00 2001 From: dianne Date: Sun, 9 Feb 2025 07:11:08 -0800 Subject: [PATCH 2/2] move pattern migration internals to the `migration` module --- .../src/thir/pattern/migration.rs | 113 ++++++++++++++++-- .../rustc_mir_build/src/thir/pattern/mod.rs | 67 ++--------- 2 files changed, 114 insertions(+), 66 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/migration.rs b/compiler/rustc_mir_build/src/thir/pattern/migration.rs index be1ebd57d1182..bd7787b643d57 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/migration.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/migration.rs @@ -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, + default_mode_labels: FxIndexMap, /// 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; + } + } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 5535ac9bd636f..5734eb1a0d818 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -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