Skip to content

Match ergonomics 2024: migration lint #124639

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
@@ -4367,6 +4367,7 @@ dependencies = [
"rustc_hir",
"rustc_index",
"rustc_infer",
"rustc_lint",
"rustc_macros",
"rustc_middle",
"rustc_pattern_analysis",
4 changes: 0 additions & 4 deletions compiler/rustc_hir_typeck/messages.ftl
Original file line number Diff line number Diff line change
@@ -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
7 changes: 0 additions & 7 deletions compiler/rustc_hir_typeck/src/errors.rs
Original file line number Diff line number Diff line change
@@ -651,10 +651,3 @@ pub enum SuggestBoxingForReturnImplTrait {
ends: Vec<Span>,
},
}
#[derive(LintDiagnostic)]
#[diag(hir_typeck_dereferencing_mut_binding)]
pub struct DereferencingMutBinding {
#[label]
#[help]
pub span: Span,
}
49 changes: 29 additions & 20 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
@@ -73,21 +73,23 @@ struct TopInfo<'tcx> {
/// found type `std::result::Result<_, _>`
/// ```
span: Option<Span>,
/// 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<DeclOrigin<'a>>,
top_info: &'a TopInfo<'tcx>,
decl_origin: Option<DeclOrigin<'tcx>>,

/// 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<Diag<'tcx>> {
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<DeclOrigin<'tcx>>,
) {
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<LoweredTy<'tcx>>, &'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;
17 changes: 17 additions & 0 deletions compiler/rustc_hir_typeck/src/writeback.rs
Original file line number Diff line number Diff line change
@@ -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);
38 changes: 19 additions & 19 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
@@ -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),
19 changes: 19 additions & 0 deletions compiler/rustc_middle/src/ty/typeck_results.rs
Original file line number Diff line number Diff line change
@@ -79,6 +79,10 @@ pub struct TypeckResults<'tcx> {
/// Stores the actual binding mode for all instances of [`BindingMode`].
pat_binding_modes: ItemLocalMap<BindingMode>,

/// 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 }
}
1 change: 1 addition & 0 deletions compiler/rustc_mir_build/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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" }
2 changes: 2 additions & 0 deletions compiler/rustc_mir_build/messages.ftl
Original file line number Diff line number Diff line change
@@ -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
27 changes: 27 additions & 0 deletions compiler/rustc_mir_build/src/errors.rs
Original file line number Diff line number Diff line change
@@ -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<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
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);
}
}
90 changes: 71 additions & 19 deletions compiler/rustc_mir_build/src/thir/pattern/mod.rs
Original file line number Diff line number Diff line change
@@ -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<Rust2024IncompatiblePatSugg>,
}

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<Pat<'tcx>> {
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<Box<Pat<'tcx>>> {
fn lower_opt_pattern(&mut self, pat: Option<&'tcx hir::Pat<'tcx>>) -> Option<Box<Pat<'tcx>>> {
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);
12 changes: 12 additions & 0 deletions tests/ui/pattern/auxiliary/match_ergonomics_2024_macros.rs
Original file line number Diff line number Diff line change
@@ -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)
};
}
57 changes: 57 additions & 0 deletions tests/ui/pattern/match_ergonomics_2024.fixed
Original file line number Diff line number Diff line change
@@ -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;
}
_ => {}
}
}
57 changes: 57 additions & 0 deletions tests/ui/pattern/match_ergonomics_2024.rs
Original file line number Diff line number Diff line change
@@ -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;
}
_ => {}
}
}
97 changes: 97 additions & 0 deletions tests/ui/pattern/match_ergonomics_2024.stderr
Original file line number Diff line number Diff line change
@@ -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

16 changes: 0 additions & 16 deletions tests/ui/pattern/mut_preserve_binding_mode_2024_lint.rs

This file was deleted.

31 changes: 0 additions & 31 deletions tests/ui/pattern/mut_preserve_binding_mode_2024_lint.stderr

This file was deleted.