diff --git a/Cargo.lock b/Cargo.lock index a562bdbafa83a..a17fc000bfb9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4367,6 +4367,7 @@ dependencies = [ "rustc_hir", "rustc_index", "rustc_infer", + "rustc_lint", "rustc_macros", "rustc_middle", "rustc_pattern_analysis", diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 0560d0d902a3f..72b95a9603dd0 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -46,10 +46,6 @@ hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private hir_typeck_deref_is_empty = this expression `Deref`s to `{$deref_ty}` which implements `is_empty` -hir_typeck_dereferencing_mut_binding = dereferencing `mut` binding - .label = `mut` dereferences the type of this binding - .help = this will change in edition 2024 - hir_typeck_expected_default_return_type = expected `()` because of default return type hir_typeck_expected_return_type = expected `{$expected}` because of return type diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index ba8f246fd8d24..f250b909596ea 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -651,10 +651,3 @@ pub enum SuggestBoxingForReturnImplTrait { ends: Vec, }, } -#[derive(LintDiagnostic)] -#[diag(hir_typeck_dereferencing_mut_binding)] -pub struct DereferencingMutBinding { - #[label] - #[help] - pub span: Span, -} diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 259336f438d64..b77f91dd544c1 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -73,21 +73,23 @@ struct TopInfo<'tcx> { /// found type `std::result::Result<_, _>` /// ``` span: Option, + /// The [`HirId`] of the top-level pattern. + hir_id: HirId, } #[derive(Copy, Clone)] struct PatInfo<'tcx, 'a> { binding_mode: ByRef, max_ref_mutbl: MutblCap, - top_info: TopInfo<'tcx>, - decl_origin: Option>, + top_info: &'a TopInfo<'tcx>, + decl_origin: Option>, /// The depth of current pattern current_depth: u32, } impl<'tcx> FnCtxt<'_, 'tcx> { - fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> { + fn pattern_cause(&self, ti: &TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> { let code = ObligationCauseCode::Pattern { span: ti.span, root_ty: ti.expected, @@ -101,7 +103,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { cause_span: Span, expected: Ty<'tcx>, actual: Ty<'tcx>, - ti: TopInfo<'tcx>, + ti: &TopInfo<'tcx>, ) -> Option> { let mut diag = self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)?; @@ -118,7 +120,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { cause_span: Span, expected: Ty<'tcx>, actual: Ty<'tcx>, - ti: TopInfo<'tcx>, + ti: &TopInfo<'tcx>, ) { if let Some(err) = self.demand_eqtype_pat_diag(cause_span, expected, actual, ti) { err.emit(); @@ -199,11 +201,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { origin_expr: Option<&'tcx hir::Expr<'tcx>>, decl_origin: Option>, ) { - let info = TopInfo { expected, origin_expr, span }; + let info = TopInfo { expected, origin_expr, span, hir_id: pat.hir_id }; let pat_info = PatInfo { binding_mode: ByRef::No, max_ref_mutbl: MutblCap::Mut, - top_info: info, + top_info: &info, decl_origin, current_depth: 0, }; @@ -463,7 +465,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, lt: &hir::Expr<'tcx>, expected: Ty<'tcx>, - ti: TopInfo<'tcx>, + ti: &TopInfo<'tcx>, ) -> Ty<'tcx> { // We've already computed the type above (when checking for a non-ref pat), // so avoid computing it again. @@ -533,7 +535,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs: Option<&'tcx hir::Expr<'tcx>>, rhs: Option<&'tcx hir::Expr<'tcx>>, expected: Ty<'tcx>, - ti: TopInfo<'tcx>, + ti: &TopInfo<'tcx>, ) -> Ty<'tcx> { let calc_side = |opt_expr: Option<&'tcx hir::Expr<'tcx>>| match opt_expr { None => None, @@ -671,18 +673,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Determine the binding mode... let bm = match user_bind_annot { + // `mut` resets binding mode on edition <= 2021 BindingMode(ByRef::No, Mutability::Mut) if !(pat.span.at_least_rust_2024() && self.tcx.features().mut_preserve_binding_mode_2024) && matches!(def_br, ByRef::Yes(_)) => { - // `mut x` resets the binding mode in edition <= 2021. - self.tcx.emit_node_span_lint( - rustc_lint::builtin::DEREFERENCING_MUT_BINDING, - pat.hir_id, - pat.span, - errors::DereferencingMutBinding { span: pat.span }, - ); + self.typeck_results + .borrow_mut() + .rust_2024_migration_desugared_pats_mut() + .insert(pat_info.top_info.hir_id); BindingMode(ByRef::No, Mutability::Mut) } BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl), @@ -754,7 +754,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, var_id: HirId, ty: Ty<'tcx>, - ti: TopInfo<'tcx>, + ti: &TopInfo<'tcx>, ) { let var_ty = self.local_ty(span, var_id); if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) { @@ -996,7 +996,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { qpath: &hir::QPath<'_>, path_resolution: (Res, Option>, &'tcx [hir::PathSegment<'tcx>]), expected: Ty<'tcx>, - ti: TopInfo<'tcx>, + ti: &TopInfo<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; @@ -2178,8 +2178,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } else { // Reset binding mode on old editions - pat_info.binding_mode = ByRef::No; - pat_info.max_ref_mutbl = MutblCap::Mut + + if pat_info.binding_mode != ByRef::No { + pat_info.binding_mode = ByRef::No; + + self.typeck_results + .borrow_mut() + .rust_2024_migration_desugared_pats_mut() + .insert(pat_info.top_info.hir_id); + } + + pat_info.max_ref_mutbl = MutblCap::Mut; } let tcx = self.tcx; diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 0a40ffb0d5aab..45f8fa77e56f8 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -346,6 +346,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { _ => {} }; + self.visit_rust_2024_migration_desugared_pats(p.hir_id); self.visit_skipped_ref_pats(p.hir_id); self.visit_pat_adjustments(p.span, p.hir_id); @@ -655,6 +656,22 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + #[instrument(skip(self), level = "debug")] + fn visit_rust_2024_migration_desugared_pats(&mut self, hir_id: hir::HirId) { + if self + .fcx + .typeck_results + .borrow_mut() + .rust_2024_migration_desugared_pats_mut() + .remove(hir_id) + { + debug!( + "node is a pat whose match ergonomics are desugared by the Rust 2024 migration lint" + ); + self.typeck_results.rust_2024_migration_desugared_pats_mut().insert(hir_id); + } + } + #[instrument(skip(self, span), level = "debug")] fn visit_pat_adjustments(&mut self, span: Span, hir_id: HirId) { let adjustment = self.fcx.typeck_results.borrow_mut().pat_adjustments_mut().remove(hir_id); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index eea3ca44c48b2..5369454577249 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -38,7 +38,6 @@ declare_lint_pass! { DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, DEPRECATED_IN_FUTURE, DEPRECATED_WHERE_CLAUSE_LOCATION, - DEREFERENCING_MUT_BINDING, DUPLICATE_MACRO_ATTRIBUTES, ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, ELIDED_LIFETIMES_IN_PATHS, @@ -90,6 +89,7 @@ declare_lint_pass! { RUST_2021_INCOMPATIBLE_OR_PATTERNS, RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, RUST_2021_PRELUDE_COLLISIONS, + RUST_2024_INCOMPATIBLE_PAT, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, SINGLE_USE_LIFETIMES, SOFT_UNSTABLE, @@ -1630,34 +1630,34 @@ declare_lint! { } declare_lint! { - /// The `dereferencing_mut_binding` lint detects a `mut x` pattern that resets the binding mode, - /// as this behavior will change in rust 2024. + /// The `rust_2024_incompatible_pat` lint + /// detects patterns whose meaning will change in the Rust 2024 edition. /// /// ### Example /// - /// ```rust - /// # #![warn(dereferencing_mut_binding)] - /// let x = Some(123u32); - /// let _y = match &x { - /// Some(mut x) => { - /// x += 1; - /// x - /// } - /// None => 0, - /// }; + /// ```rust,edition2021 + /// #![feature(ref_pat_eat_one_layer_2024)] + /// #![warn(rust_2024_incompatible_pat)] + /// + /// if let Some(&a) = &Some(&0u8) { + /// let _: u8 = a; + /// } + /// if let Some(mut _a) = &mut Some(0u8) { + /// _a = 7u8; + /// } /// ``` /// /// {{produces}} /// /// ### Explanation /// - /// Without the `mut`, `x` would have type `&u32`. Pre-2024, adding `mut` makes `x` have type - /// `u32`, which was deemed surprising. After edition 2024, adding `mut` will not change the - /// type of `x`. This lint warns users of editions before 2024 to update their code. - pub DEREFERENCING_MUT_BINDING, + /// In Rust 2024 and above, the `mut` keyword does not reset the pattern binding mode, + /// and nor do `&` or `&mut` patterns. The lint will suggest code that + /// has the same meaning in all editions. + pub RUST_2024_INCOMPATIBLE_PAT, Allow, - "detects `mut x` bindings that change the type of `x`", - @feature_gate = sym::mut_preserve_binding_mode_2024; + "detects patterns whose meaning will change in Rust 2024", + @feature_gate = sym::ref_pat_eat_one_layer_2024; // FIXME uncomment below upon stabilization /*@future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 41f417dfd4b1a..24e3e623ff274 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -79,6 +79,10 @@ pub struct TypeckResults<'tcx> { /// Stores the actual binding mode for all instances of [`BindingMode`]. pat_binding_modes: ItemLocalMap, + /// Top-level patterns whose match ergonomics need to be desugared + /// by the Rust 2021 -> 2024 migration lint. + rust_2024_migration_desugared_pats: ItemLocalSet, + /// Stores the types which were implicitly dereferenced in pattern binding modes /// for later usage in THIR lowering. For example, /// @@ -229,6 +233,7 @@ impl<'tcx> TypeckResults<'tcx> { adjustments: Default::default(), pat_binding_modes: Default::default(), pat_adjustments: Default::default(), + rust_2024_migration_desugared_pats: Default::default(), skipped_ref_pats: Default::default(), closure_kind_origins: Default::default(), liberated_fn_sigs: Default::default(), @@ -432,6 +437,20 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments } } + pub fn rust_2024_migration_desugared_pats(&self) -> LocalSetInContext<'_> { + LocalSetInContext { + hir_owner: self.hir_owner, + data: &self.rust_2024_migration_desugared_pats, + } + } + + pub fn rust_2024_migration_desugared_pats_mut(&mut self) -> LocalSetInContextMut<'_> { + LocalSetInContextMut { + hir_owner: self.hir_owner, + data: &mut self.rust_2024_migration_desugared_pats, + } + } + pub fn skipped_ref_pats(&self) -> LocalSetInContext<'_> { LocalSetInContext { hir_owner: self.hir_owner, data: &self.skipped_ref_pats } } diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index 6618e4f5a0024..77f27236437dd 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -16,6 +16,7 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_infer = { path = "../rustc_infer" } +rustc_lint = { path = "../rustc_lint" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_pattern_analysis = { path = "../rustc_pattern_analysis" } diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 34440c60cf378..0bb44dbb8706b 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -267,6 +267,8 @@ mir_build_pointer_pattern = function pointers and raw pointers not derived from mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future +mir_build_rust_2024_incompatible_pat = the semantics of this pattern will change in edition 2024 + mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly .attributes = no other attributes may be applied .not_box = `#[rustc_box]` may only be applied to a `Box::new()` call diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index f67113afd6d9d..e2a28467b8457 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -950,3 +950,30 @@ pub enum RustcBoxAttrReason { #[note(mir_build_missing_box)] MissingBox, } + +#[derive(LintDiagnostic)] +#[diag(mir_build_rust_2024_incompatible_pat)] +pub struct Rust2024IncompatiblePat { + #[subdiagnostic] + pub sugg: Rust2024IncompatiblePatSugg, +} + +pub struct Rust2024IncompatiblePatSugg { + pub suggestion: Vec<(Span, String)>, +} + +impl Subdiagnostic for Rust2024IncompatiblePatSugg { + fn add_to_diag_with>( + self, + diag: &mut Diag<'_, G>, + _f: &F, + ) { + let applicability = + if self.suggestion.iter().all(|(span, _)| span.can_be_used_for_suggestions()) { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + diag.multipart_suggestion("desugar the match ergonomics", self.suggestion, applicability); + } +} diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 5c016682d8d2d..48fc0293f4058 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -11,8 +11,9 @@ use crate::thir::util::UserAnnotatedTyHelpers; use rustc_errors::codes::*; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::{self as hir, RangeEnd}; +use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd}; use rustc_index::Idx; +use rustc_lint as lint; use rustc_middle::mir::interpret::{ErrorHandled, GlobalId, LitToConstError, LitToConstInput}; use rustc_middle::mir::{self, Const}; use rustc_middle::thir::{ @@ -30,6 +31,9 @@ struct PatCtxt<'a, 'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, + + /// Used by the Rust 2024 migration lint. + rust_2024_migration_suggestion: Option, } pub(super) fn pat_from_hir<'a, 'tcx>( @@ -38,9 +42,25 @@ pub(super) fn pat_from_hir<'a, 'tcx>( typeck_results: &'a ty::TypeckResults<'tcx>, pat: &'tcx hir::Pat<'tcx>, ) -> Box> { - let mut pcx = PatCtxt { tcx, param_env, typeck_results }; + let mut pcx = PatCtxt { + tcx, + param_env, + typeck_results, + rust_2024_migration_suggestion: typeck_results + .rust_2024_migration_desugared_pats() + .contains(pat.hir_id) + .then_some(Rust2024IncompatiblePatSugg { suggestion: Vec::new() }), + }; let result = pcx.lower_pattern(pat); debug!("pat_from_hir({:?}) = {:?}", pat, result); + if let Some(sugg) = pcx.rust_2024_migration_suggestion { + tcx.emit_node_span_lint( + lint::builtin::RUST_2024_INCOMPATIBLE_PAT, + pat.hir_id, + pat.span, + Rust2024IncompatiblePat { sugg }, + ); + } result } @@ -73,17 +93,38 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } _ => self.lower_pattern_unadjusted(pat), }; - self.typeck_results.pat_adjustments().get(pat.hir_id).unwrap_or(&vec![]).iter().rev().fold( - unadjusted_pat, - |pat: Box<_>, ref_ty| { - debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty); - Box::new(Pat { - span: pat.span, - ty: *ref_ty, - kind: PatKind::Deref { subpattern: pat }, + + let adjustments: &[Ty<'tcx>] = + self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v); + let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, ref_ty| { + debug!("{:?}: wrapping pattern with type {:?}", thir_pat, ref_ty); + Box::new(Pat { + span: thir_pat.span, + ty: *ref_ty, + kind: PatKind::Deref { subpattern: thir_pat }, + }) + }); + + if let Some(s) = &mut self.rust_2024_migration_suggestion + && !adjustments.is_empty() + { + let suggestion_str: String = adjustments + .iter() + .map(|ref_ty| { + let &ty::Ref(_, _, mutbl) = ref_ty.kind() else { + span_bug!(pat.span, "pattern implicitly dereferences a non-ref type"); + }; + + match mutbl { + ty::Mutability::Not => "&", + ty::Mutability::Mut => "&mut ", + } }) - }, - ) + .collect(); + s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str)); + }; + + adjusted_pat } fn lower_pattern_range_endpoint( @@ -272,7 +313,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { PatKind::Deref { subpattern: self.lower_pattern(subpattern) } } - hir::PatKind::Slice(prefix, ref slice, suffix) => { + hir::PatKind::Slice(prefix, slice, suffix) => { self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix) } @@ -284,7 +325,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { PatKind::Leaf { subpatterns } } - hir::PatKind::Binding(_, id, ident, ref sub) => { + hir::PatKind::Binding(explicit_ba, id, ident, sub) => { if let Some(ident_span) = ident.span.find_ancestor_inside(span) { span = span.with_hi(ident_span.hi()); } @@ -295,6 +336,20 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .get(pat.hir_id) .expect("missing binding mode"); + if let Some(s) = &mut self.rust_2024_migration_suggestion + && 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(), + )) + } + // A ref x pattern is the same node used for x, and as such it has // x's type, which is &T, where we want T (the type being matched). let var_ty = ty; @@ -366,10 +421,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { pats.iter().map(|p| self.lower_pattern(p)).collect() } - fn lower_opt_pattern( - &mut self, - pat: &'tcx Option<&'tcx hir::Pat<'tcx>>, - ) -> Option>> { + fn lower_opt_pattern(&mut self, pat: Option<&'tcx hir::Pat<'tcx>>) -> Option>> { pat.map(|p| self.lower_pattern(p)) } @@ -378,7 +430,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { span: Span, ty: Ty<'tcx>, prefix: &'tcx [hir::Pat<'tcx>], - slice: &'tcx Option<&'tcx hir::Pat<'tcx>>, + slice: Option<&'tcx hir::Pat<'tcx>>, suffix: &'tcx [hir::Pat<'tcx>], ) -> PatKind<'tcx> { let prefix = self.lower_patterns(prefix); diff --git a/tests/ui/pattern/auxiliary/match_ergonomics_2024_macros.rs b/tests/ui/pattern/auxiliary/match_ergonomics_2024_macros.rs new file mode 100644 index 0000000000000..0b70e4404abce --- /dev/null +++ b/tests/ui/pattern/auxiliary/match_ergonomics_2024_macros.rs @@ -0,0 +1,12 @@ +//@ edition: 2024 +//@ compile-flags: -Z unstable-options + +// This contains a binding in edition 2024, so if matched with a reference binding mode it will end +// up with a `mut ref mut` binding mode. We use this to test the migration lint on patterns with +// mixed editions. +#[macro_export] +macro_rules! mixed_edition_pat { + ($foo:ident) => { + Some(mut $foo) + }; +} diff --git a/tests/ui/pattern/match_ergonomics_2024.fixed b/tests/ui/pattern/match_ergonomics_2024.fixed new file mode 100644 index 0000000000000..d8dbcb217c040 --- /dev/null +++ b/tests/ui/pattern/match_ergonomics_2024.fixed @@ -0,0 +1,57 @@ +//@ edition: 2021 +//@ run-rustfix +//@ rustfix-only-machine-applicable +//@ aux-build:match_ergonomics_2024_macros.rs +#![feature(mut_preserve_binding_mode_2024, ref_pat_eat_one_layer_2024)] +#![allow(incomplete_features, unused)] +#![deny(rust_2024_incompatible_pat)] + +extern crate match_ergonomics_2024_macros; + +struct Foo(u8); + +fn main() { + let &Foo(mut a) = &Foo(0); + //~^ ERROR: the semantics of this pattern will change in edition 2024 + a = 42; + + let &mut Foo(mut a) = &mut Foo(0); + //~^ ERROR: the semantics of this pattern will change in edition 2024 + a = 42; + + if let &&&&&Some(&_) = &&&&&Some(&0u8) {} + //~^ ERROR: the semantics of this pattern will change in edition 2024 + + if let &&&&&Some(&mut _) = &&&&&Some(&mut 0u8) {} + //~^ ERROR: the semantics of this pattern will change in edition 2024 + + if let &&&&&mut Some(&_) = &&&&&mut Some(&0u8) {} + //~^ ERROR: the semantics of this pattern will change in edition 2024 + + if let &mut Some(&mut Some(&mut Some(_))) = &mut Some(&mut Some(&mut Some(0u8))) {} + //~^ ERROR: the semantics of this pattern will change in edition 2024 + + if let &mut Some(&mut Some(&mut Some(ref mut _a))) = &mut Some(&mut Some(&mut Some(0u8))) {} + //~^ ERROR: the semantics of this pattern will change in edition 2024 + + struct Struct { + a: u32, + b: u32, + c: u32, + } + let s = Struct { a: 0, b: 0, c: 0 }; + let &Struct { ref a, mut b, ref c } = &s; + //~^ ERROR: the semantics of this pattern will change in edition 2024 + + #[warn(rust_2024_incompatible_pat)] + match &(Some(0), Some(0)) { + // The two patterns are the same syntactically, but because they're defined in different + // editions they don't mean the same thing. + (Some(mut _x), match_ergonomics_2024_macros::mixed_edition_pat!(_y)) => { + //~^ WARN: the semantics of this pattern will change in edition 2024 + _x = 4; + _y = &7; + } + _ => {} + } +} diff --git a/tests/ui/pattern/match_ergonomics_2024.rs b/tests/ui/pattern/match_ergonomics_2024.rs new file mode 100644 index 0000000000000..38dc0c8bebb2f --- /dev/null +++ b/tests/ui/pattern/match_ergonomics_2024.rs @@ -0,0 +1,57 @@ +//@ edition: 2021 +//@ run-rustfix +//@ rustfix-only-machine-applicable +//@ aux-build:match_ergonomics_2024_macros.rs +#![feature(mut_preserve_binding_mode_2024, ref_pat_eat_one_layer_2024)] +#![allow(incomplete_features, unused)] +#![deny(rust_2024_incompatible_pat)] + +extern crate match_ergonomics_2024_macros; + +struct Foo(u8); + +fn main() { + let Foo(mut a) = &Foo(0); + //~^ ERROR: the semantics of this pattern will change in edition 2024 + a = 42; + + let Foo(mut a) = &mut Foo(0); + //~^ ERROR: the semantics of this pattern will change in edition 2024 + a = 42; + + if let Some(&_) = &&&&&Some(&0u8) {} + //~^ ERROR: the semantics of this pattern will change in edition 2024 + + if let Some(&mut _) = &&&&&Some(&mut 0u8) {} + //~^ ERROR: the semantics of this pattern will change in edition 2024 + + if let Some(&_) = &&&&&mut Some(&0u8) {} + //~^ ERROR: the semantics of this pattern will change in edition 2024 + + if let Some(&mut Some(Some(_))) = &mut Some(&mut Some(&mut Some(0u8))) {} + //~^ ERROR: the semantics of this pattern will change in edition 2024 + + if let Some(&mut Some(Some(_a))) = &mut Some(&mut Some(&mut Some(0u8))) {} + //~^ ERROR: the semantics of this pattern will change in edition 2024 + + struct Struct { + a: u32, + b: u32, + c: u32, + } + let s = Struct { a: 0, b: 0, c: 0 }; + let Struct { a, mut b, c } = &s; + //~^ ERROR: the semantics of this pattern will change in edition 2024 + + #[warn(rust_2024_incompatible_pat)] + match &(Some(0), Some(0)) { + // The two patterns are the same syntactically, but because they're defined in different + // editions they don't mean the same thing. + (Some(mut _x), match_ergonomics_2024_macros::mixed_edition_pat!(_y)) => { + //~^ WARN: the semantics of this pattern will change in edition 2024 + _x = 4; + _y = &7; + } + _ => {} + } +} diff --git a/tests/ui/pattern/match_ergonomics_2024.stderr b/tests/ui/pattern/match_ergonomics_2024.stderr new file mode 100644 index 0000000000000..11844434ad214 --- /dev/null +++ b/tests/ui/pattern/match_ergonomics_2024.stderr @@ -0,0 +1,97 @@ +error: the semantics of this pattern will change in edition 2024 + --> $DIR/match_ergonomics_2024.rs:14:9 + | +LL | let Foo(mut a) = &Foo(0); + | -^^^^^^^^^ + | | + | help: desugar the match ergonomics: `&` + | +note: the lint level is defined here + --> $DIR/match_ergonomics_2024.rs:7:9 + | +LL | #![deny(rust_2024_incompatible_pat)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the semantics of this pattern will change in edition 2024 + --> $DIR/match_ergonomics_2024.rs:18:9 + | +LL | let Foo(mut a) = &mut Foo(0); + | -^^^^^^^^^ + | | + | help: desugar the match ergonomics: `&mut` + +error: the semantics of this pattern will change in edition 2024 + --> $DIR/match_ergonomics_2024.rs:22:12 + | +LL | if let Some(&_) = &&&&&Some(&0u8) {} + | -^^^^^^^ + | | + | help: desugar the match ergonomics: `&&&&&` + +error: the semantics of this pattern will change in edition 2024 + --> $DIR/match_ergonomics_2024.rs:25:12 + | +LL | if let Some(&mut _) = &&&&&Some(&mut 0u8) {} + | -^^^^^^^^^^^ + | | + | help: desugar the match ergonomics: `&&&&&` + +error: the semantics of this pattern will change in edition 2024 + --> $DIR/match_ergonomics_2024.rs:28:12 + | +LL | if let Some(&_) = &&&&&mut Some(&0u8) {} + | -^^^^^^^ + | | + | help: desugar the match ergonomics: `&&&&&mut` + +error: the semantics of this pattern will change in edition 2024 + --> $DIR/match_ergonomics_2024.rs:31:12 + | +LL | if let Some(&mut Some(Some(_))) = &mut Some(&mut Some(&mut Some(0u8))) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: desugar the match ergonomics + | +LL | if let &mut Some(&mut Some(&mut Some(_))) = &mut Some(&mut Some(&mut Some(0u8))) {} + | ++++ ++++ + +error: the semantics of this pattern will change in edition 2024 + --> $DIR/match_ergonomics_2024.rs:34:12 + | +LL | if let Some(&mut Some(Some(_a))) = &mut Some(&mut Some(&mut Some(0u8))) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: desugar the match ergonomics + | +LL | if let &mut Some(&mut Some(&mut Some(ref mut _a))) = &mut Some(&mut Some(&mut Some(0u8))) {} + | ++++ ++++ +++++++ + +error: the semantics of this pattern will change in edition 2024 + --> $DIR/match_ergonomics_2024.rs:43:9 + | +LL | let Struct { a, mut b, c } = &s; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: desugar the match ergonomics + | +LL | let &Struct { ref a, mut b, ref c } = &s; + | + +++ +++ + +warning: the semantics of this pattern will change in edition 2024 + --> $DIR/match_ergonomics_2024.rs:50:9 + | +LL | (Some(mut _x), match_ergonomics_2024_macros::mixed_edition_pat!(_y)) => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/match_ergonomics_2024.rs:46:12 + | +LL | #[warn(rust_2024_incompatible_pat)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: desugar the match ergonomics + | +LL | &(Some(mut _x), match_ergonomics_2024_macros::mixed_edition_pat!(ref _y)) => { + | + +++ + +error: aborting due to 8 previous errors; 1 warning emitted + diff --git a/tests/ui/pattern/mut_preserve_binding_mode_2024_lint.rs b/tests/ui/pattern/mut_preserve_binding_mode_2024_lint.rs deleted file mode 100644 index 249f251d2cd2e..0000000000000 --- a/tests/ui/pattern/mut_preserve_binding_mode_2024_lint.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ edition: 2021 -#![feature(mut_preserve_binding_mode_2024)] -#![allow(incomplete_features, unused)] -#![forbid(dereferencing_mut_binding)] - -struct Foo(u8); - -fn main() { - let Foo(mut a) = &Foo(0); - //~^ ERROR: dereferencing `mut` binding - a = 42; - - let Foo(mut a) = &mut Foo(0); - //~^ ERROR: dereferencing `mut` binding - a = 42; -} diff --git a/tests/ui/pattern/mut_preserve_binding_mode_2024_lint.stderr b/tests/ui/pattern/mut_preserve_binding_mode_2024_lint.stderr deleted file mode 100644 index e8d11acd83e50..0000000000000 --- a/tests/ui/pattern/mut_preserve_binding_mode_2024_lint.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error: dereferencing `mut` binding - --> $DIR/mut_preserve_binding_mode_2024_lint.rs:9:13 - | -LL | let Foo(mut a) = &Foo(0); - | ^^^^^ `mut` dereferences the type of this binding - | -help: this will change in edition 2024 - --> $DIR/mut_preserve_binding_mode_2024_lint.rs:9:13 - | -LL | let Foo(mut a) = &Foo(0); - | ^^^^^ -note: the lint level is defined here - --> $DIR/mut_preserve_binding_mode_2024_lint.rs:4:11 - | -LL | #![forbid(dereferencing_mut_binding)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: dereferencing `mut` binding - --> $DIR/mut_preserve_binding_mode_2024_lint.rs:13:13 - | -LL | let Foo(mut a) = &mut Foo(0); - | ^^^^^ `mut` dereferences the type of this binding - | -help: this will change in edition 2024 - --> $DIR/mut_preserve_binding_mode_2024_lint.rs:13:13 - | -LL | let Foo(mut a) = &mut Foo(0); - | ^^^^^ - -error: aborting due to 2 previous errors -