diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index ae00bb4e218ab..1895e0abad7e8 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -835,20 +835,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.add_rust_2024_migration_desugared_pat( pat_info.top_info.hir_id, pat, - ident.span, + 't', def_br_mutbl, ); BindingMode(ByRef::No, Mutability::Mut) } } BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl), - BindingMode(ByRef::Yes(_), _) => { + BindingMode(ByRef::Yes(user_br_mutbl), _) => { if let ByRef::Yes(def_br_mutbl) = def_br { // `ref`/`ref mut` overrides the binding mode on edition <= 2021 self.add_rust_2024_migration_desugared_pat( pat_info.top_info.hir_id, pat, - ident.span, + if user_br_mutbl.is_mut() { 't' } else { 'f' }, def_br_mutbl, ); } @@ -2387,7 +2387,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.add_rust_2024_migration_desugared_pat( pat_info.top_info.hir_id, pat, - inner.span, + if pat_mutbl.is_mut() { 't' } else { '&' }, inh_mut, ) } @@ -2779,55 +2779,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, pat_id: HirId, subpat: &'tcx Pat<'tcx>, - cutoff_span: Span, + final_char: char, def_br_mutbl: Mutability, ) { // Try to trim the span we're labeling to just the `&` or binding mode that's an issue. // If the subpattern's span is is from an expansion, the emitted label will not be trimmed. - let source_map = self.tcx.sess.source_map(); - let cutoff_span = source_map - .span_extend_prev_while(cutoff_span, |c| c.is_whitespace() || c == '(') - .unwrap_or(cutoff_span); - // Ensure we use the syntax context and thus edition of `subpat.span`; this will be a hard - // error if the subpattern is of edition >= 2024. - let trimmed_span = subpat.span.until(cutoff_span).with_ctxt(subpat.span.ctxt()); + // Importantly, the edition of the trimmed span should be the same as `subpat.span`; this + // will be a hard error if the subpattern is of edition >= 2024. + let from_expansion = subpat.span.from_expansion(); + let trimmed_span = if from_expansion { + subpat.span + } else { + self.tcx.sess.source_map().span_through_char(subpat.span, final_char) + }; let mut typeck_results = self.typeck_results.borrow_mut(); let mut table = typeck_results.rust_2024_migration_desugared_pats_mut(); // FIXME(ref_pat_eat_one_layer_2024): The migration diagnostic doesn't know how to track the // default binding mode in the presence of Rule 3 or Rule 5. As a consequence, the labels it // gives for default binding modes are wrong, as well as suggestions based on the default - // binding mode. This keeps it from making those suggestions, as doing so could panic. - let info = table.entry(pat_id).or_insert_with(|| ty::Rust2024IncompatiblePatInfo { - primary_labels: Vec::new(), - bad_modifiers: false, - bad_ref_pats: false, - suggest_eliding_modes: !self.tcx.features().ref_pat_eat_one_layer_2024() - && !self.tcx.features().ref_pat_eat_one_layer_2024_structural(), - }); + // binding mode. + let info = table.entry(pat_id).or_default(); - let pat_kind = if let PatKind::Binding(user_bind_annot, _, _, _) = subpat.kind { + let pat_kind = if matches!(subpat.kind, PatKind::Binding(..)) { info.bad_modifiers = true; - // If the user-provided binding modifier doesn't match the default binding mode, we'll - // need to suggest reference patterns, which can affect other bindings. - // For simplicity, we opt to suggest making the pattern fully explicit. - info.suggest_eliding_modes &= - user_bind_annot == BindingMode(ByRef::Yes(def_br_mutbl), Mutability::Not); "binding modifier" } else { info.bad_ref_pats = true; - // For simplicity, we don't try to suggest eliding reference patterns. Thus, we'll - // suggest adding them instead, which can affect the types assigned to bindings. - // As such, we opt to suggest making the pattern fully explicit. - info.suggest_eliding_modes = false; "reference pattern" }; // Only provide a detailed label if the problematic subpattern isn't from an expansion. // In the case that it's from a macro, we'll add a more detailed note in the emitter. - let from_expansion = subpat.span.from_expansion(); let primary_label = if from_expansion { - // We can't suggest eliding modifiers within expansions. - info.suggest_eliding_modes = false; // NB: This wording assumes the only expansions that can produce problematic reference // patterns and bindings are macros. If a desugaring or AST pass is added that can do // so, we may want to inspect the span's source callee or macro backtrace. diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index b8c73d2584379..f923205e631aa 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -812,7 +812,7 @@ impl<'tcx> std::fmt::Display for UserTypeKind<'tcx> { /// Information on a pattern incompatible with Rust 2024, for use by the error/migration diagnostic /// emitted during THIR construction. -#[derive(TyEncodable, TyDecodable, Debug, HashStable)] +#[derive(TyEncodable, TyDecodable, Debug, Default, HashStable)] pub struct Rust2024IncompatiblePatInfo { /// Labeled spans for `&`s, `&mut`s, and binding modifiers incompatible with Rust 2024. pub primary_labels: Vec<(Span, String)>, @@ -820,6 +820,4 @@ pub struct Rust2024IncompatiblePatInfo { pub bad_modifiers: bool, /// Whether any `&` or `&mut` patterns occur under a non-`move` default binding mode. pub bad_ref_pats: bool, - /// If `true`, we can give a simpler suggestion solely by eliding explicit binding modifiers. - pub suggest_eliding_modes: bool, } diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index f1753be845d4f..89a7a7bb0e11a 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1097,34 +1097,41 @@ pub(crate) enum MiscPatternSuggestion { #[derive(LintDiagnostic)] #[diag(mir_build_rust_2024_incompatible_pat)] -pub(crate) struct Rust2024IncompatiblePat { +pub(crate) struct Rust2024IncompatiblePat<'m> { #[subdiagnostic] - pub(crate) sugg: Rust2024IncompatiblePatSugg, + pub(crate) sugg: Rust2024IncompatiblePatSugg<'m>, pub(crate) bad_modifiers: bool, pub(crate) bad_ref_pats: bool, pub(crate) is_hard_error: bool, } -pub(crate) struct Rust2024IncompatiblePatSugg { - /// If true, our suggestion is to elide explicit binding modifiers. - /// If false, our suggestion is to make the pattern fully explicit. - pub(crate) suggest_eliding_modes: bool, +pub(crate) struct Rust2024IncompatiblePatSugg<'m> { pub(crate) suggestion: Vec<(Span, String)>, + /// If `Some(..)`, we provide a suggestion about either adding or removing syntax. + /// If `None`, we suggest both additions and removals; use a generic wording for simplicity. + pub(crate) kind: Option, pub(crate) ref_pattern_count: usize, pub(crate) binding_mode_count: usize, /// Labels for where incompatibility-causing by-ref default binding modes were introduced. - pub(crate) default_mode_labels: FxIndexMap, + pub(crate) default_mode_labels: &'m FxIndexMap, } -impl Subdiagnostic for Rust2024IncompatiblePatSugg { +pub(crate) enum Rust2024IncompatiblePatSuggKind { + Subtractive, + Additive, +} + +impl<'m> Subdiagnostic for Rust2024IncompatiblePatSugg<'m> { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, _f: &F, ) { + use Rust2024IncompatiblePatSuggKind::*; + // Format and emit explanatory notes about default binding modes. Reversing the spans' order // means if we have nested spans, the innermost ones will be visited first. - for (span, def_br_mutbl) in self.default_mode_labels.into_iter().rev() { + for (&span, &def_br_mutbl) in self.default_mode_labels.iter().rev() { // Don't point to a macro call site. if !span.from_expansion() { let note_msg = "matching on a reference type with a non-reference pattern changes the default binding mode"; @@ -1143,17 +1150,33 @@ impl Subdiagnostic for Rust2024IncompatiblePatSugg { } else { Applicability::MaybeIncorrect }; - let msg = if self.suggest_eliding_modes { - let plural_modes = pluralize!(self.binding_mode_count); - format!("remove the unnecessary binding modifier{plural_modes}") - } else { - let plural_derefs = pluralize!(self.ref_pattern_count); - let and_modes = if self.binding_mode_count > 0 { - format!(" and variable binding mode{}", pluralize!(self.binding_mode_count)) + let msg = if let Some(kind) = self.kind { + let derefs = if self.ref_pattern_count > 0 { + format!("reference pattern{}", pluralize!(self.ref_pattern_count)) } else { String::new() }; - format!("make the implied reference pattern{plural_derefs}{and_modes} explicit") + let modes = if self.binding_mode_count > 0 { + match kind { + Subtractive => { + format!("binding modifier{}", pluralize!(self.binding_mode_count)) + } + Additive => { + format!("variable binding mode{}", pluralize!(self.binding_mode_count)) + } + } + } else { + String::new() + }; + let and = if !derefs.is_empty() && !modes.is_empty() { " and " } else { "" }; + match kind { + Subtractive => format!("remove the unnecessary {derefs}{and}{modes}"), + Additive => { + format!("make the implied {derefs}{and}{modes} explicit") + } + } + } else { + "rewrite the pattern".to_owned() }; // FIXME(dianne): for peace of mind, don't risk emitting a 0-part suggestion (that panics!) if !self.suggestion.is_empty() { diff --git a/compiler/rustc_mir_build/src/thir/pattern/migration.rs b/compiler/rustc_mir_build/src/thir/pattern/migration.rs index bd7787b643d57..12b6afabadfae 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/migration.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/migration.rs @@ -2,24 +2,30 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::MultiSpan; -use rustc_hir::{BindingMode, ByRef, HirId, Mutability}; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; +use rustc_index::IndexVec; use rustc_lint as lint; use rustc_middle::span_bug; use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, Ty, TyCtxt}; +use rustc_span::source_map::SourceMap; use rustc_span::{Ident, Span}; -use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg}; +use crate::errors::{ + Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg, Rust2024IncompatiblePatSuggKind, +}; 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> { - 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. - default_mode_span: Option<(Span, ty::Mutability)>, +pub(super) struct PatMigration<'a, 'tcx> { + /// All the variable bindings encountered in lowering the pattern, along with whether to + /// suggest adding/removing them. + bindings: IndexVec, + /// All the dereferences encountered in lowering the pattern, along with how their corresponding + /// patterns affect the default binding mode, and whether to suggest adding/removing them. + derefs: IndexVec>, + /// Internal state: the innermost deref above the pattern currently being lowered. + innermost_deref: Option, /// 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 @@ -30,13 +36,95 @@ pub(super) struct PatMigration<'a> { info: &'a Rust2024IncompatiblePatInfo, } -impl<'a> PatMigration<'a> { +rustc_index::newtype_index! { + struct PatBindingIdx {} +} + +rustc_index::newtype_index! { + struct PatDerefIdx {} +} + +struct PatBinding { + /// The span of the binding modifier (empty if no explicit modifier was provided). + span: Span, + /// The actual binding mode of this binding. + mode: BindingMode, + /// Whether to include a binding modifier (e.g. `ref` or `mut`) in the suggested pattern. + suggest: bool, + /// The next binding in the innermost enclosing deref's list of bindings. + next_sibling: Option, +} + +struct PatDeref<'a, 'tcx> { + /// The span of the pattern where this deref occurs (implicitly or explicitly). + span: Span, + /// The mutability of the ref pattern (or for implicit derefs, of the reference type). + // FIXME(ref_pattern_eat_one_layer_2024): Under RFC 3627's Rule 5, a `&` pattern can match a + // `&mut` type or `ref mut` binding mode. Thus, an omitted `&` could result in a `ref mut` + // default binding mode. We may want to track both the pattern and ref type's mutabilities. + mutbl: Mutability, + /// Whether this span is for a potentially-removable explicitly-provided deref, or an implicit + /// dereference which we can potentially suggest making explicit. + kind: PatDerefKind<'a, 'tcx>, + /// Whether to include this as a `&` or `&mut` in the suggested pattern. + suggest: bool, + /// The default binding mode for variables under this deref in the user's pattern. + real_default_mode: ByRef, + /// The default binding mode for variable under this deref in our suggestion. + sugg_default_mode: ByRef, + /// The span that introduced the current default binding mode, or `None` for the top-level pat. + default_mode_origin: Option, + /// Whether this is an instance of `&ref x` which we may be able to simplify to `x`. + /// Stores the HIR id of the binding pattern `ref x`, to identify it later. + simplify_deref_ref: Option, + /// The next deref above this. Since we can't suggest using `&` or `&mut` on a by-ref default + /// binding mode, a suggested deref's ancestors must also all be suggested. + // FIXME(ref_pat_eat_one_layer_2024): By suggesting `&` and `&mut` patterns that can eat the + // default binding mode, we'll be able to make more local suggestions. That may make this forest + // structure unnecessary. + parent: Option, + /// The head of the linked list of child derefs directly under this. When we suggest a `&` + /// pattern, any implicit `&mut` children will go from producing a `ref` default binding mode + /// to `ref mut`, so we check recursively in that case to see if any bindings would change. + // FIXME(ref_pat_eat_one_layer_2024_structural): Aside from this maybe being unnecessary if we + // can make more local suggestions (see the above fixme), RFC 3627's Rule 3 should also obsolete + // this (see the comments on `propagate_default_mode_change`). + first_child: Option, + /// The next child in their parents' linked list of children. + next_sibling: Option, + /// The head of the linked list of bindings directly under this deref. If we suggest this + /// deref, we'll also need to suggest binding modifiers for any by-ref bindings. + first_binding: Option, +} + +enum PatDerefKind<'a, 'tcx> { + /// For dereferences from lowering `&` and `&mut` patterns + Explicit { inner_span: Span }, + /// For dereferences inserted by match ergonomics + Implicit { ref_tys: &'a [Ty<'tcx>] }, +} + +/// Assuming the input is a slice of reference types implicitly dereferenced by match ergonomics +/// (stored in [`ty::TypeckResults::pat_adjustments`]), iterate over their reference mutabilities. +/// A span is provided for debugging purposes. +fn iter_ref_mutbls<'a, 'tcx>( + span: Span, + ref_tys: &'a [Ty<'tcx>], +) -> impl Iterator + use<'a, 'tcx> { + ref_tys.iter().map(move |ref_ty| { + let &ty::Ref(_, _, mutbl) = ref_ty.kind() else { + span_bug!(span, "pattern implicitly dereferences a non-ref type"); + }; + mutbl + }) +} + +impl<'a, 'tcx> PatMigration<'a, 'tcx> { pub(super) fn new(info: &'a Rust2024IncompatiblePatInfo) -> Self { PatMigration { - suggestion: Vec::new(), - ref_pattern_count: 0, - binding_mode_count: 0, - default_mode_span: None, + bindings: IndexVec::new(), + derefs: IndexVec::new(), + innermost_deref: None, default_mode_labels: Default::default(), info, } @@ -44,19 +132,13 @@ impl<'a> PatMigration<'a> { /// 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) { + pub(super) fn emit(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, - }; + let sugg = self.build_suggestion(tcx.sess.source_map()); // 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 { @@ -86,61 +168,224 @@ 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 + /// When lowering a reference pattern or a binding with a modifier, this checks if the default + /// binding mode is by-ref, and if so, adds a labeled note to the diagnostic with the origin of + /// the current default binding mode. + fn add_default_mode_label_if_needed(&mut self) { + if let ByRef::Yes(ref_mutbl) = self.real_default_mode() { + // The by-ref default binding mode must have come from an implicit deref. If there was a + // problem in tracking that for the diagnostic, try to avoid ICE on release builds. + debug_assert!( + self.innermost_deref + .is_some_and(|ix| self.derefs[ix].default_mode_origin.is_some()) + ); + if let Some(ix) = self.innermost_deref + && let Some(span) = self.derefs[ix].default_mode_origin + { + self.default_mode_labels.insert(span, ref_mutbl); + } + } + } + + fn build_suggestion<'m>(&'m self, source_map: &SourceMap) -> Rust2024IncompatiblePatSugg<'m> { + let mut removed_modifiers = 0; + let mut added_modifiers = 0; + let modes = self.bindings.iter().filter_map(|binding| { + if binding.mode == BindingMode::NONE { + // This binding mode is written as the empty string; no need to suggest. + None + } else { + if !binding.suggest && !binding.span.is_empty() { + // This binding is in the source but not the suggestion; suggest removing it. + removed_modifiers += 1; + Some((binding.span, String::new())) + } else if binding.suggest && binding.span.is_empty() { + // This binding is in the suggestion but not the source; suggest adding it. + added_modifiers += 1; + Some((binding.span, binding.mode.prefix_str().to_owned())) + } else { + // This binding is as it should be. + None + } + } + }); + + let mut removed_ref_pats = 0; + let mut added_ref_pats = 0; + let derefs = self.derefs.iter().filter_map(|deref| match deref.kind { + PatDerefKind::Explicit { inner_span } if !deref.suggest => { + // This is a ref pattern in the source but not the suggestion; suggest removing it. + removed_ref_pats += 1; + // Avoid eating the '(' in `&(...)` + let span = source_map.span_until_char(deref.span.with_hi(inner_span.lo()), '('); + // But *do* eat the ' ' in `&mut [...]` + Some((source_map.span_extend_while_whitespace(span), String::new())) + } + PatDerefKind::Implicit { ref_tys } if deref.suggest => { + // This is a ref pattern in the suggestion but not the source; suggest adding it. + let ref_pat_str = + iter_ref_mutbls(deref.span, ref_tys).map(Mutability::ref_prefix_str).collect(); + added_ref_pats += ref_tys.len(); + Some((deref.span.shrink_to_lo(), ref_pat_str)) + } + _ => None, }); - 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)) + let suggestion = modes.chain(derefs).collect(); + let (kind, binding_mode_count, ref_pattern_count) = + if added_modifiers == 0 && added_ref_pats == 0 { + let kind = Rust2024IncompatiblePatSuggKind::Subtractive; + (Some(kind), removed_modifiers, removed_ref_pats) + } else if removed_modifiers == 0 && removed_ref_pats == 0 { + (Some(Rust2024IncompatiblePatSuggKind::Additive), added_modifiers, added_ref_pats) + } else { + (None, 0, 0) + }; + Rust2024IncompatiblePatSugg { + suggestion, + kind, + binding_mode_count, + ref_pattern_count, + default_mode_labels: &self.default_mode_labels, + } + } + + /// The default binding mode at the current point in the pattern the user wrote. + fn real_default_mode(&self) -> ByRef { + if let Some(current_ix) = self.innermost_deref { + self.derefs[current_ix].real_default_mode } else { - // This does not change the default binding mode; it was already `ref` or `ref mut`. - self.default_mode_span + ByRef::No } } + /// The default binding mode at the current point in the pattern we're suggesting. + fn sugg_default_mode(&self) -> ByRef { + if let Some(deref_ix) = self.innermost_deref { + self.derefs[deref_ix].sugg_default_mode + } else { + ByRef::No + } + } + + /// Tracks when we're lowering a pattern that implicitly dereferences the scrutinee. + /// This should only be called when the pattern type adjustments list `ref_tys` is non-empty. + /// This should be followed by a call to [`PatMigration::leave_ref`] when we leave the pattern. + pub(super) fn visit_implicit_derefs(&mut self, pat: &hir::Pat<'_>, ref_tys: &'a [Ty<'tcx>]) { + // The effective mutability of this (as far as the default binding mode goes) is `ref` if + // any of `ref_tys` are shared, and `ref mut` if they're all mutable. + let mutbl = iter_ref_mutbls(pat.span, ref_tys) + .min() + .expect("`ref_tys` should have at least one element"); + self.push_deref(pat.span, mutbl, PatDerefKind::Implicit { ref_tys }); + } + /// 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() + /// This should be followed by a call to [`PatMigration::leave_ref`] when we leave the pattern. + // FIXME(ref_pat_eat_one_layer_2024): This assumes reference patterns correspond to real + // dereferences. If reference patterns can match the default binding mode alone, we may need to + // check `TypeckResults::skipped_ref_pats` to tell if this pattern corresponds to an implicit + // dereference we've already visited. + pub(super) fn visit_explicit_deref( + &mut self, + pat_span: Span, + mutbl: Mutability, + subpat: &hir::Pat<'_>, + ) { + // If this eats a by-ref default binding mode, label the binding mode. + self.add_default_mode_label_if_needed(); + // This sets the default binding mode to by-value in the user's pattern, but we'll try to + // suggest removing it. + let my_ix = + self.push_deref(pat_span, mutbl, PatDerefKind::Explicit { inner_span: subpat.span }); + + // If this is inside a macro expansion, we won't be able to remove it. + if pat_span.from_expansion() { + self.add_derefs_to_suggestion(self.innermost_deref); + return; + } + + // If the subpattern is a binding, removing this reference pattern would change its type. + // FIXME(ref_pat_eat_one_layer_2024): This assumes ref pats can't eat the binding mode + // alone. Depending on the pattern typing rules in use, we can be more precise here. + if let hir::PatKind::Binding(explicit_ba, _, _, _) = subpat.kind { + if explicit_ba == BindingMode(ByRef::Yes(mutbl), Mutability::Not) { + // If the binding has a `ref` modifier, we can elide both this `&` and the `ref`; + // i.e. we can simplify `&ref x` to `x`, as long as all parent derefs are explicit. + // NB: We don't rewrite `&ref x @ ...` to `x @ &...`, so we may end up needing to + // reinstate this `&` later if the binding's subpattern requires it. + // FIXME(ref_pat_eat_one_layer_2024): With RFC 3627's Rule 5, `&` patterns can match + // `&mut` types; we'll have to check the mutability of the type rather than the + // pattern to see whether we can elide it. + self.derefs[my_ix].simplify_deref_ref = Some(subpat.hir_id); + self.add_derefs_to_suggestion(self.derefs[my_ix].parent); + } else { + // Otherwise, we need to suggest including this `&` as well. + self.add_derefs_to_suggestion(self.innermost_deref); + } + } + } + + /// Adds a deref to our deref-forest, so that we can track the default binding mode and + /// propagate binding mode changes when we suggest adding patterns. + /// See [`PatMigration::propagate_default_mode_change`]. + fn push_deref( + &mut self, + span: Span, + mutbl: Mutability, + kind: PatDerefKind<'a, 'tcx>, + ) -> PatDerefIdx { + let parent = self.innermost_deref; + // Get the new default binding mode in the pattern the user wrote. + let real_default_mode = match kind { + PatDerefKind::Implicit { .. } => match self.real_default_mode() { + ByRef::Yes(old_mutbl) => ByRef::Yes(Ord::min(mutbl, old_mutbl)), + ByRef::No => ByRef::Yes(mutbl), + }, + PatDerefKind::Explicit { .. } => ByRef::No, + }; + // If this keeps the default binding mode the same, it shares a mode origin with its + // parent. If it changes the default binding mode, its mode origin is itself. + let default_mode_origin = if real_default_mode == self.real_default_mode() { + parent.and_then(|p| self.derefs[p].default_mode_origin) + } else { + Some(span) + }; + // Get the default binding mode in the suggestion, assuming we don't include a reference + // pattern for this deref. We may add one later if necessary. + let sugg_default_mode = ByRef::Yes(match self.sugg_default_mode() { + ByRef::Yes(parent_mutbl) => Ord::min(mutbl, parent_mutbl), + ByRef::No => mutbl, + }); + let my_ix = self.derefs.push(PatDeref { + span, + mutbl, + kind, + suggest: false, + sugg_default_mode, + real_default_mode, + default_mode_origin, + simplify_deref_ref: None, + parent, + next_sibling: parent.and_then(|p| self.derefs[p].first_child), + first_child: None, + first_binding: None, + }); + if let Some(p) = parent { + self.derefs[p].first_child = Some(my_ix); + } + self.innermost_deref = Some(my_ix); + my_ix } /// 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 + pub(super) fn leave_ref(&mut self) { + debug_assert!(self.innermost_deref.is_some(), "entering/leaving refs should be paired"); + if let Some(child_ix) = self.innermost_deref { + self.innermost_deref = self.derefs[child_ix].parent; + } } /// Determines if a binding is relevant to the diagnostic and adjusts the notes/suggestion if @@ -148,35 +393,109 @@ impl<'a> PatMigration<'a> { /// Rust 2024) or if we need to suggest a binding modifier for them. pub(super) fn visit_binding( &mut self, - pat_span: Span, + pat: &hir::Pat<'_>, 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 explicit_ba != BindingMode::NONE { // 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; + self.add_default_mode_label_if_needed(); + } + + // As a special case, we may simplify `&ref x` to `x`; check our parent to see if we can. + // The default binding mode will always be by-move in this case. + let simplify_deref_ref = self.innermost_deref.is_some_and(|p| { + self.derefs[p].simplify_deref_ref.is_some_and(|binding_id| pat.hir_id == binding_id) + }); + + // Otherwise, if `mode` doesn't match the default, we'll need to specify its binding + // modifiers explicitly, which in turn necessitates a by-move default binding mode. + // Additionally, if this is inside a macro expansion, we won't be able to change it. If a + // binding modifier is missing inside the expansion, there's not much we can do, but we can + // avoid suggestions to elide binding modifiers that are explicit within expansions. + let suggest = !simplify_deref_ref + && mode != BindingMode(self.sugg_default_mode(), Mutability::Not) + || pat.span.from_expansion() && explicit_ba != BindingMode::NONE; + + // Track the binding + let span = if explicit_ba == BindingMode::NONE { + pat.span.shrink_to_lo() + } else { + pat.span.with_hi(ident.span.lo()) + }; + // If we're not already suggesting an explicit binding modifier for this binding, we may + // need to later, if adding reference patterns above it changes the default binding mode. + // In that case, track it as a child of the innermost dereference above it. + let parent_deref = if suggest { None } else { self.innermost_deref }; + let next_sibling = parent_deref.and_then(|p| self.derefs[p].first_binding); + let bind_ix = self.bindings.push(PatBinding { span, mode, suggest, next_sibling }); + if let Some(p) = parent_deref { + self.derefs[p].first_binding = Some(bind_ix); + } + + // If there was a mismatch, add `&`s to make sure we're in a by-move default binding mode. + if suggest { + self.add_derefs_to_suggestion(self.innermost_deref); + } + } + + /// Include a deref and all its ancestors in the suggestion. If this would change the mode of + /// a binding, we include a binding modifier for it in the suggestion, which may in turn + /// require including more explicit dereferences, etc. + fn add_derefs_to_suggestion(&mut self, mut opt_ix: Option) { + while let Some(ix) = opt_ix { + let deref = &mut self.derefs[ix]; + if deref.suggest { + // If this is already marked as suggested, its ancestors will be too. + break; } + deref.suggest = true; + deref.sugg_default_mode = ByRef::No; + deref.simplify_deref_ref = None; + opt_ix = deref.parent; + let propagate_downstream_ref_mut = deref.mutbl.is_not(); + self.propagate_default_mode_change(ix, propagate_downstream_ref_mut); } - 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; + } + + /// If including a `&` or `&mut` pattern in our suggestion would change the binding mode of any + /// variables, add any necessary binding modifiers and reference patterns to keep them the same. + fn propagate_default_mode_change(&mut self, start_ix: PatDerefIdx, propagate_ref_mut: bool) { + // After suggesting a deref, any immediate-child bindings will by default be by-value, so + // we'll need to suggest modifiers if they should be by-ref. Likewise, if suggesting a `&` + // changes the ref-mutability of a downstream binding under an implicit `&mut`, we'll need + // to add a binding modifier and `&mut` patterns. + let mut opt_bind_ix = self.derefs[start_ix].first_binding; + while let Some(bind_ix) = opt_bind_ix { + let binding = &mut self.bindings[bind_ix]; + opt_bind_ix = binding.next_sibling; + // FIXME(ref_pat_eat_one_layer_2024_structural): With RFC 3627's Rule 3, an implicit + // `&mut` under a `&` pattern won't set the default binding mode to `ref mut`, so we + // won't need to do any mutability checks or ref-mutability propagation. We'd only call + // this on `&`/`&mut` patterns we suggest, not their descendants, so we can assume the + // default binding mode is by-move and that the deref is already suggested. + if binding.mode.0 != self.derefs[start_ix].sugg_default_mode { + binding.suggest = true; + self.add_derefs_to_suggestion(Some(start_ix)); + } + } + + // If we change an implicit dereference of a shared reference to a `&` pattern, any implicit + // derefs of `&mut` references in children (until we hit another implicit `&`) will now + // produce a `ref mut` default binding mode instead of `ref`. We'll need to recur in case + // any downstream bindings' modes are changed. + // FIXME(ref_pat_eat_one_layer_2024_structural): See the above fixme. This can all go. + if propagate_ref_mut { + let mut opt_child_ix = self.derefs[start_ix].first_child; + while let Some(child_ix) = opt_child_ix { + let child = &mut self.derefs[child_ix]; + opt_child_ix = child.next_sibling; + if child.mutbl.is_mut() { + child.sugg_default_mode = ByRef::Yes(Mutability::Mut); + self.propagate_default_mode_change(child_ix, true); + } + } } } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 8dc3f998e0916..c3e0d76dc9479 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -34,7 +34,7 @@ struct PatCtxt<'a, 'tcx> { typeck_results: &'a ty::TypeckResults<'tcx>, /// Used by the Rust 2024 migration lint. - rust_2024_migration: Option>, + rust_2024_migration: Option>, } pub(super) fn pat_from_hir<'a, 'tcx>( @@ -66,11 +66,10 @@ impl<'a, 'tcx> PatCtxt<'a, '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() { - opt_old_mode_span = s.visit_implicit_derefs(pat.span, adjustments); + s.visit_implicit_derefs(pat, adjustments); } // When implicit dereferences have been inserted in this pattern, the unadjusted lowered @@ -113,7 +112,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { if let Some(s) = &mut self.rust_2024_migration && !adjustments.is_empty() { - s.leave_ref(opt_old_mode_span); + s.leave_ref(); } adjusted_pat @@ -307,13 +306,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), mutability } } - hir::PatKind::Ref(subpattern, _) => { + hir::PatKind::Ref(subpattern, mutbl) => { // Track the default binding mode for the Rust 2024 migration suggestion. - let opt_old_mode_span = - self.rust_2024_migration.as_mut().and_then(|s| s.visit_explicit_deref()); + if let Some(s) = &mut self.rust_2024_migration { + s.visit_explicit_deref(pat.span, mutbl, subpattern); + } let subpattern = self.lower_pattern(subpattern); if let Some(s) = &mut self.rust_2024_migration { - s.leave_ref(opt_old_mode_span); + s.leave_ref(); } PatKind::Deref { subpattern } } @@ -344,8 +344,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .get(pat.hir_id) .expect("missing binding mode"); - if let Some(s) = &mut self.rust_2024_migration { - s.visit_binding(pat.span, mode, explicit_ba, ident); + if let Some(m) = &mut self.rust_2024_migration { + m.visit_binding(pat, mode, explicit_ba, ident); } // A ref x pattern is the same node used for x, and as such it has diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/auxiliary/migration_lint_macros.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/auxiliary/migration_lint_macros.rs index b18f87fd56995..fcde98812270d 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/auxiliary/migration_lint_macros.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/auxiliary/migration_lint_macros.rs @@ -16,3 +16,10 @@ macro_rules! bind_ref { ref $foo }; } + +#[macro_export] +macro_rules! match_ref { + ($p:pat) => { + &$p + }; +} diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr index 56125be2d6fc8..cdc707013f607 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr @@ -46,10 +46,11 @@ note: matching on a reference type with a non-reference pattern changes the defa | LL | let [ref x] = &[0]; | ^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit +help: remove the unnecessary binding modifier + | +LL - let [ref x] = &[0]; +LL + let [x] = &[0]; | -LL | let &[ref x] = &[0]; - | + error: binding modifiers may only be written when the default binding mode is `move` --> $DIR/ref-binding-on-inh-ref-errors.rs:79:10 @@ -80,10 +81,11 @@ note: matching on a reference type with a non-reference pattern changes the defa | LL | let [ref mut x] = &mut [0]; | ^^^^^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit +help: remove the unnecessary binding modifier + | +LL - let [ref mut x] = &mut [0]; +LL + let [x] = &mut [0]; | -LL | let &mut [ref mut x] = &mut [0]; - | ++++ error: aborting due to 6 previous errors diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr index 31930e8c03371..9070f085066f8 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr @@ -10,10 +10,11 @@ note: matching on a reference type with a non-reference pattern changes the defa | LL | let [&ref x] = &[&0]; | ^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit +help: rewrite the pattern + | +LL - let [&ref x] = &[&0]; +LL + let &[x] = &[&0]; | -LL | let &[&ref x] = &[&0]; - | + error: binding modifiers may only be written when the default binding mode is `move` --> $DIR/ref-binding-on-inh-ref-errors.rs:20:11 @@ -27,10 +28,11 @@ note: matching on a reference type with a non-reference pattern changes the defa | LL | let [&ref x] = &mut [&0]; | ^^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit +help: rewrite the pattern + | +LL - let [&ref x] = &mut [&0]; +LL + let &mut [x] = &mut [&0]; | -LL | let &mut [&ref x] = &mut [&0]; - | ++++ error: binding modifiers may only be written when the default binding mode is `move` --> $DIR/ref-binding-on-inh-ref-errors.rs:25:15 @@ -61,10 +63,11 @@ note: matching on a reference type with a non-reference pattern changes the defa | LL | let [&mut ref mut x] = &mut [&mut 0]; | ^^^^^^^^^^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit +help: rewrite the pattern + | +LL - let [&mut ref mut x] = &mut [&mut 0]; +LL + let &mut [x] = &mut [&mut 0]; | -LL | let &mut [&mut ref mut x] = &mut [&mut 0]; - | ++++ error: binding modifiers may only be written when the default binding mode is `move` --> $DIR/ref-binding-on-inh-ref-errors.rs:39:11 @@ -78,10 +81,11 @@ note: matching on a reference type with a non-reference pattern changes the defa | LL | let [&ref x] = &[&mut 0]; | ^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit +help: rewrite the pattern + | +LL - let [&ref x] = &[&mut 0]; +LL + let &[x] = &[&mut 0]; | -LL | let &[&ref x] = &[&mut 0]; - | + error: binding modifiers may only be written when the default binding mode is `move` --> $DIR/ref-binding-on-inh-ref-errors.rs:45:11 @@ -95,10 +99,11 @@ note: matching on a reference type with a non-reference pattern changes the defa | LL | let [&ref x] = &mut [&mut 0]; | ^^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit +help: rewrite the pattern + | +LL - let [&ref x] = &mut [&mut 0]; +LL + let &mut [x] = &mut [&mut 0]; | -LL | let &mut [&ref x] = &mut [&mut 0]; - | ++++ error: binding modifiers may only be written when the default binding mode is `move` --> $DIR/ref-binding-on-inh-ref-errors.rs:54:15 @@ -152,10 +157,11 @@ note: matching on a reference type with a non-reference pattern changes the defa | LL | let [ref x] = &[0]; | ^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit +help: remove the unnecessary binding modifier + | +LL - let [ref x] = &[0]; +LL + let [x] = &[0]; | -LL | let &[ref x] = &[0]; - | + error: binding modifiers may only be written when the default binding mode is `move` --> $DIR/ref-binding-on-inh-ref-errors.rs:79:10 @@ -186,10 +192,11 @@ note: matching on a reference type with a non-reference pattern changes the defa | LL | let [ref mut x] = &mut [0]; | ^^^^^^^^^^^ this matches on type `&mut _` -help: make the implied reference pattern explicit +help: remove the unnecessary binding modifier + | +LL - let [ref mut x] = &mut [0]; +LL + let [x] = &mut [0]; | -LL | let &mut [ref mut x] = &mut [0]; - | ++++ error: aborting due to 12 previous errors diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed index e35896f32ad7d..34eb6500ec279 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed @@ -96,7 +96,7 @@ fn main() { assert_type_eq(x, 0u8); } - if let &mut Some(&mut Some(&mut Some(ref mut x))) = &mut Some(&mut Some(&mut Some(0u8))) { + if let Some(Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) { //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &mut 0u8); @@ -121,7 +121,7 @@ fn main() { assert_type_eq(b, &&0u32); assert_type_eq(c, &&0u32); - if let &Struct { a: &Some(a), b: &Some(&b), c: &Some(ref c) } = + if let &Struct { a: &Some(a), b: &Some(&b), c: Some(c) } = //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 &(Struct { a: &Some(&0), b: &Some(&0), c: &Some(&0) }) @@ -142,17 +142,15 @@ fn main() { _ => {} } - let &mut [&mut &[ref a]] = &mut [&mut &[0]]; + let [[a]] = &mut [&mut &[0]]; //~^ ERROR: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); - let &[&(_)] = &[&0]; + let [(_)] = &[&0]; //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 - // NB: Most of the following tests are for possible future improvements to migration suggestions - // Test removing multiple binding modifiers. let Struct { a, b, c } = &Struct { a: 0, b: 0, c: 0 }; //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 @@ -169,7 +167,7 @@ fn main() { assert_type_eq(c, &mut 0u32); // Test removing multiple reference patterns of various mutabilities, plus a binding modifier. - let &mut &Struct { a: &[ref a], b: &mut [&[ref b]], ref c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; + let Struct { a: [a], b: [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); @@ -177,13 +175,13 @@ fn main() { assert_type_eq(c, &0u32); // Test that we don't change bindings' types when removing reference patterns. - let &Foo(&ref a) = &Foo(&0); + let &Foo(a) = &Foo(&0); //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); // Test that we don't change bindings' modes when adding reference paterns (caught early). - let &(&a, ref b, &[ref c], &mut [&mut (ref d, &[ref e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); + let &(&a, ref b, [c], &mut [&mut (ref d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, 0u32); @@ -201,7 +199,7 @@ fn main() { assert_type_eq(c, 0u32); // Test featuring both additions and removals. - let &(&a, &mut (ref b, &[ref c])) = &(&0, &mut (0, &[0])); + let &(&a, &mut (ref b, [c])) = &(&0, &mut (0, &[0])); //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, 0u32); @@ -223,7 +221,7 @@ fn main() { assert_type_eq(b, 0u32); // Test that we respect bindings' subpatterns' types when rewriting `&ref x` to `x`. - let [&Foo(&ref a @ ref b), &Foo(&ref c @ d)] = [&Foo(&0); 2]; + let [&Foo(a @ b), &Foo(&ref c @ d)] = [&Foo(&0); 2]; //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); @@ -232,7 +230,7 @@ fn main() { assert_type_eq(d, 0u32); // Test that we respect bindings' subpatterns' modes when rewriting `&ref x` to `x`. - let [&Foo(&ref a @ [ref b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; + let [&Foo(a @ [b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &[0u32]); @@ -244,4 +242,20 @@ fn main() { let &[migration_lint_macros::bind_ref!(a)] = &[0]; //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` assert_type_eq(a, &0u32); + + let &[migration_lint_macros::match_ref!(a)] = &[&0]; + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` + assert_type_eq(a, 0u32); + + // Test that we use the correct span when labeling a `&` whose subpattern is from an expansion. + // Also test that we don't simplify `&ref x` to `x` when the `ref` is from an expansion. + let &[&migration_lint_macros::bind_ref!(a)] = &[&0]; + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + + // Test that we don't simplify `&ref x` to `x` when the `&` is from an expansion. + let &[migration_lint_macros::match_ref!(ref a)] = &[&0]; + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` + assert_type_eq(a, &0u32); } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs index 10a23e6f2fa18..ee3c6f8a49955 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs @@ -151,8 +151,6 @@ fn main() { //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 - // NB: Most of the following tests are for possible future improvements to migration suggestions - // Test removing multiple binding modifiers. let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 }; //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 @@ -244,4 +242,20 @@ fn main() { let [migration_lint_macros::bind_ref!(a)] = &[0]; //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` assert_type_eq(a, &0u32); + + let [migration_lint_macros::match_ref!(a)] = &[&0]; + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` + assert_type_eq(a, 0u32); + + // Test that we use the correct span when labeling a `&` whose subpattern is from an expansion. + // Also test that we don't simplify `&ref x` to `x` when the `ref` is from an expansion. + let [&migration_lint_macros::bind_ref!(a)] = &[&0]; + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + + // Test that we don't simplify `&ref x` to `x` when the `&` is from an expansion. + let [migration_lint_macros::match_ref!(ref a)] = &[&0]; + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` + assert_type_eq(a, &0u32); } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr index 3dd91c86a3b8f..e157b2d22b7d3 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr @@ -215,10 +215,11 @@ note: matching on a reference type with a non-reference pattern changes the defa | LL | if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) { | ^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&mut _` -help: make the implied reference patterns and variable binding mode explicit +help: remove the unnecessary reference pattern + | +LL - if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) { +LL + if let Some(Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) { | -LL | if let &mut Some(&mut Some(&mut Some(ref mut x))) = &mut Some(&mut Some(&mut Some(0u8))) { - | ++++ ++++ +++++++ error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:111:21 @@ -273,10 +274,10 @@ note: matching on a reference type with a non-reference pattern changes the defa | LL | if let Struct { a: &Some(a), b: Some(&b), c: Some(c) } = | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference patterns and variable binding mode explicit +help: make the implied reference patterns explicit | -LL | if let &Struct { a: &Some(a), b: &Some(&b), c: &Some(ref c) } = - | + + + +++ +LL | if let &Struct { a: &Some(a), b: &Some(&b), c: Some(c) } = + | + + error: binding modifiers may only be written when the default binding mode is `move` --> $DIR/migration_lint.rs:137:15 @@ -318,10 +319,11 @@ note: matching on a reference type with a non-reference pattern changes the defa | LL | let [&mut [ref a]] = &mut [&mut &[0]]; | ^^^^^^^^^^^^^^ this matches on type `&mut _` -help: make the implied reference patterns explicit +help: remove the unnecessary reference pattern and binding modifier + | +LL - let [&mut [ref a]] = &mut [&mut &[0]]; +LL + let [[a]] = &mut [&mut &[0]]; | -LL | let &mut [&mut &[ref a]] = &mut [&mut &[0]]; - | ++++ + error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:150:10 @@ -336,13 +338,14 @@ note: matching on a reference type with a non-reference pattern changes the defa | LL | let [&(_)] = &[&0]; | ^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit +help: remove the unnecessary reference pattern + | +LL - let [&(_)] = &[&0]; +LL + let [(_)] = &[&0]; | -LL | let &[&(_)] = &[&0]; - | + error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 - --> $DIR/migration_lint.rs:157:18 + --> $DIR/migration_lint.rs:155:18 | LL | let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 }; | ^^^ ^^^ binding modifier not allowed under `ref` default binding mode @@ -352,7 +355,7 @@ LL | let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 }; = warning: this changes meaning in Rust 2024 = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/migration_lint.rs:157:9 + --> $DIR/migration_lint.rs:155:9 | LL | let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` @@ -363,7 +366,7 @@ LL + let Struct { a, b, c } = &Struct { a: 0, b: 0, c: 0 }; | error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 - --> $DIR/migration_lint.rs:164:18 + --> $DIR/migration_lint.rs:162:18 | LL | let Struct { ref a, ref mut b, c } = &mut Struct { a: 0, b: 0, c: 0 }; | ^^^ ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode @@ -373,7 +376,7 @@ LL | let Struct { ref a, ref mut b, c } = &mut Struct { a: 0, b: 0, c: 0 }; = warning: this changes meaning in Rust 2024 = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/migration_lint.rs:164:9 + --> $DIR/migration_lint.rs:162:9 | LL | let Struct { ref a, ref mut b, c } = &mut Struct { a: 0, b: 0, c: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&mut _` @@ -383,7 +386,7 @@ LL | let &mut Struct { ref a, ref mut b, ref mut c } = &mut Struct { a: 0, b | ++++ +++++++ error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 - --> $DIR/migration_lint.rs:172:21 + --> $DIR/migration_lint.rs:170:21 | LL | let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; | ^ ^^^^ reference pattern not allowed under `ref` default binding mode @@ -393,17 +396,18 @@ LL | let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0], = warning: this changes meaning in Rust 2024 = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/migration_lint.rs:172:9 + --> $DIR/migration_lint.rs:170:9 | LL | let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference patterns and variable binding modes explicit +help: remove the unnecessary reference patterns and binding modifier + | +LL - let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; +LL + let Struct { a: [a], b: [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; | -LL | let &mut &Struct { a: &[ref a], b: &mut [&[ref b]], ref c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; - | ++++++ + +++ +++ error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 - --> $DIR/migration_lint.rs:180:13 + --> $DIR/migration_lint.rs:178:13 | LL | let Foo(&ref a) = &Foo(&0); | ^ reference pattern not allowed under `ref` default binding mode @@ -411,17 +415,18 @@ LL | let Foo(&ref a) = &Foo(&0); = warning: this changes meaning in Rust 2024 = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/migration_lint.rs:180:9 + --> $DIR/migration_lint.rs:178:9 | LL | let Foo(&ref a) = &Foo(&0); | ^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference pattern explicit +help: rewrite the pattern + | +LL - let Foo(&ref a) = &Foo(&0); +LL + let &Foo(a) = &Foo(&0); | -LL | let &Foo(&ref a) = &Foo(&0); - | + error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 - --> $DIR/migration_lint.rs:186:10 + --> $DIR/migration_lint.rs:184:10 | LL | let (&a, b, [c], [(d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); | ^ reference pattern not allowed under `ref` default binding mode @@ -429,17 +434,17 @@ LL | let (&a, b, [c], [(d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); = warning: this changes meaning in Rust 2024 = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/migration_lint.rs:186:9 + --> $DIR/migration_lint.rs:184:9 | LL | let (&a, b, [c], [(d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); | ^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` help: make the implied reference patterns and variable binding modes explicit | -LL | let &(&a, ref b, &[ref c], &mut [&mut (ref d, &[ref e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); - | + +++ + +++ ++++ ++++ +++ + +++ +LL | let &(&a, ref b, [c], &mut [&mut (ref d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); + | + +++ ++++ ++++ +++ error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 - --> $DIR/migration_lint.rs:196:19 + --> $DIR/migration_lint.rs:194:19 | LL | let (a, [b], [mut c]) = &(0, &mut [0], &[0]); | ^^^ binding modifier not allowed under `ref` default binding mode @@ -447,7 +452,7 @@ LL | let (a, [b], [mut c]) = &(0, &mut [0], &[0]); = warning: this changes meaning in Rust 2024 = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/migration_lint.rs:196:9 + --> $DIR/migration_lint.rs:194:9 | LL | let (a, [b], [mut c]) = &(0, &mut [0], &[0]); | ^^^^^^^^^^^^^^^^^ this matches on type `&_` @@ -457,7 +462,7 @@ LL | let &(ref a, &mut [ref b], &[mut c]) = &(0, &mut [0], &[0]); | + +++ ++++ +++ + error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 - --> $DIR/migration_lint.rs:204:10 + --> $DIR/migration_lint.rs:202:10 | LL | let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0])); | ^ ^ reference pattern not allowed under `ref` default binding mode @@ -467,17 +472,18 @@ LL | let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0])); = warning: this changes meaning in Rust 2024 = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/migration_lint.rs:204:9 + --> $DIR/migration_lint.rs:202:9 | LL | let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0])); | ^^^^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference patterns and variable binding mode explicit +help: rewrite the pattern + | +LL - let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0])); +LL + let &(&a, &mut (ref b, [c])) = &(&0, &mut (0, &[0])); | -LL | let &(&a, &mut (ref b, &[ref c])) = &(&0, &mut (0, &[0])); - | + ++++ +++ error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 - --> $DIR/migration_lint.rs:212:10 + --> $DIR/migration_lint.rs:210:10 | LL | let [mut a @ b] = &[0]; | ^^^ binding modifier not allowed under `ref` default binding mode @@ -485,7 +491,7 @@ LL | let [mut a @ b] = &[0]; = warning: this changes meaning in Rust 2024 = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/migration_lint.rs:212:9 + --> $DIR/migration_lint.rs:210:9 | LL | let [mut a @ b] = &[0]; | ^^^^^^^^^^^ this matches on type `&_` @@ -495,7 +501,7 @@ LL | let &[mut a @ ref b] = &[0]; | + +++ error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 - --> $DIR/migration_lint.rs:219:14 + --> $DIR/migration_lint.rs:217:14 | LL | let [a @ mut b] = &[0]; | ^^^ binding modifier not allowed under `ref` default binding mode @@ -503,7 +509,7 @@ LL | let [a @ mut b] = &[0]; = warning: this changes meaning in Rust 2024 = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/migration_lint.rs:219:9 + --> $DIR/migration_lint.rs:217:9 | LL | let [a @ mut b] = &[0]; | ^^^^^^^^^^^ this matches on type `&_` @@ -513,7 +519,7 @@ LL | let &[ref a @ mut b] = &[0]; | + +++ error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 - --> $DIR/migration_lint.rs:226:14 + --> $DIR/migration_lint.rs:224:14 | LL | let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2]; | ^ ^ reference pattern not allowed under `ref` default binding mode @@ -523,22 +529,23 @@ LL | let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2]; = warning: this changes meaning in Rust 2024 = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/migration_lint.rs:226:31 + --> $DIR/migration_lint.rs:224:31 | LL | let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2]; | ^^^^^^^^^^^^^^^ this matches on type `&_` note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/migration_lint.rs:226:10 + --> $DIR/migration_lint.rs:224:10 | LL | let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2]; | ^^^^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference patterns explicit +help: rewrite the pattern + | +LL - let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2]; +LL + let [&Foo(a @ b), &Foo(&ref c @ d)] = [&Foo(&0); 2]; | -LL | let [&Foo(&ref a @ ref b), &Foo(&ref c @ d)] = [&Foo(&0); 2]; - | + + error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 - --> $DIR/migration_lint.rs:235:14 + --> $DIR/migration_lint.rs:233:14 | LL | let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; | ^ ^ reference pattern not allowed under `ref` default binding mode @@ -548,29 +555,30 @@ LL | let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; = warning: this changes meaning in Rust 2024 = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/migration_lint.rs:235:33 + --> $DIR/migration_lint.rs:233:33 | LL | let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; | ^^^^^^^^^^^^^^^^^ this matches on type `&_` note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/migration_lint.rs:235:10 + --> $DIR/migration_lint.rs:233:10 | LL | let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; | ^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` -help: make the implied reference patterns explicit +help: rewrite the pattern + | +LL - let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; +LL + let [&Foo(a @ [b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; | -LL | let [&Foo(&ref a @ [ref b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; - | + + error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/migration_lint.rs:244:10 + --> $DIR/migration_lint.rs:242:10 | LL | let [migration_lint_macros::bind_ref!(a)] = &[0]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ occurs within macro expansion | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/migration_lint.rs:244:9 + --> $DIR/migration_lint.rs:242:9 | LL | let [migration_lint_macros::bind_ref!(a)] = &[0]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` @@ -580,5 +588,59 @@ help: make the implied reference pattern explicit LL | let &[migration_lint_macros::bind_ref!(a)] = &[0]; | + -error: aborting due to 30 previous errors +error: reference patterns may only be written when the default binding mode is `move` + --> $DIR/migration_lint.rs:246:10 + | +LL | let [migration_lint_macros::match_ref!(a)] = &[&0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ occurs within macro expansion + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:246:9 + | +LL | let [migration_lint_macros::match_ref!(a)] = &[&0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` + = note: this error originates in the macro `migration_lint_macros::match_ref` (in Nightly builds, run with -Z macro-backtrace for more info) +help: make the implied reference pattern explicit + | +LL | let &[migration_lint_macros::match_ref!(a)] = &[&0]; + | + + +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + --> $DIR/migration_lint.rs:252:10 + | +LL | let [&migration_lint_macros::bind_ref!(a)] = &[&0]; + | ^ reference pattern not allowed under `ref` default binding mode + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:252:9 + | +LL | let [&migration_lint_macros::bind_ref!(a)] = &[&0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` +help: make the implied reference pattern explicit + | +LL | let &[&migration_lint_macros::bind_ref!(a)] = &[&0]; + | + + +error: reference patterns may only be written when the default binding mode is `move` + --> $DIR/migration_lint.rs:258:10 + | +LL | let [migration_lint_macros::match_ref!(ref a)] = &[&0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ occurs within macro expansion + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:258:9 + | +LL | let [migration_lint_macros::match_ref!(ref a)] = &[&0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` + = note: this error originates in the macro `migration_lint_macros::match_ref` (in Nightly builds, run with -Z macro-backtrace for more info) +help: make the implied reference pattern explicit + | +LL | let &[migration_lint_macros::match_ref!(ref a)] = &[&0]; + | + + +error: aborting due to 33 previous errors