diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 18d9d739dd6..07b4948872d 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -46,6 +46,10 @@ 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 d399730bf3d..3dc9c7b86f7 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -632,3 +632,11 @@ 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 06f6a7f6bf2..252125aba7c 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -10,6 +10,7 @@ use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{self as hir, BindingAnnotation, ByRef, HirId, Mutability, Pat, PatKind}; use rustc_infer::infer; use rustc_infer::infer::type_variable::TypeVariableOrigin; +use rustc_lint as lint; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::{self, Adt, Ty, TypeVisitableExt}; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; @@ -639,6 +640,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && matches!(def_br, ByRef::Yes(_)) => { // `mut x` resets the binding mode in edition <= 2021. + self.tcx.emit_node_span_lint( + lint::builtin::DEREFERENCING_MUT_BINDING, + pat.hir_id, + pat.span, + errors::DereferencingMutBinding { span: pat.span }, + ); BindingAnnotation(ByRef::No, Mutability::Mut) } BindingAnnotation(ByRef::No, mutbl) => BindingAnnotation(def_br, mutbl), diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 2713690f812..bc36a587a9e 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -38,6 +38,7 @@ 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, @@ -1627,6 +1628,41 @@ declare_lint! { "detect mut variables which don't need to be mutable" } +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. + /// + /// ### Example + /// + /// ```rust + /// # #![warn(dereferencing_mut_binding)] + /// let x = Some(123u32); + /// let _y = match &x { + /// Some(mut x) => { + /// x += 1; + /// x + /// } + /// None => 0, + /// }; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Without the `mut`, `x` would have type `&u32`. Pre-2024, adding `mut` makes `x` have type + /// `u32`, which was deeped 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, + Allow, + "detects `mut x` bindings that change the type of `x`", + @feature_gate = sym::mut_dont_reset_binding_mode_2024; + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), + reference: "123076", + }; +} + declare_lint! { /// The `unconditional_recursion` lint detects functions that cannot /// return without calling themselves. diff --git a/tests/ui/pattern/mut_dont_reset_binding_mode_2024_lint.rs b/tests/ui/pattern/mut_dont_reset_binding_mode_2024_lint.rs new file mode 100644 index 00000000000..2992e61fbbc --- /dev/null +++ b/tests/ui/pattern/mut_dont_reset_binding_mode_2024_lint.rs @@ -0,0 +1,18 @@ +//@ edition: 2021 +#![feature(mut_dont_reset_binding_mode_2024)] +#![allow(unused)] +#![forbid(dereferencing_mut_binding)] + +struct Foo(u8); + +fn main() { + let Foo(mut a) = &Foo(0); + //~^ ERROR: dereferencing `mut` binding + //~| WARN: this changes meaning in Rust 2024 + a = 42; + + let Foo(mut a) = &mut Foo(0); + //~^ ERROR: dereferencing `mut` binding + //~| WARN: this changes meaning in Rust 2024 + a = 42; +} diff --git a/tests/ui/pattern/mut_dont_reset_binding_mode_2024_lint.stderr b/tests/ui/pattern/mut_dont_reset_binding_mode_2024_lint.stderr new file mode 100644 index 00000000000..72b72f0df5d --- /dev/null +++ b/tests/ui/pattern/mut_dont_reset_binding_mode_2024_lint.stderr @@ -0,0 +1,35 @@ +error: dereferencing `mut` binding + --> $DIR/mut_dont_reset_binding_mode_2024_lint.rs:9:13 + | +LL | let Foo(mut a) = &Foo(0); + | ^^^^^ `mut` dereferences the type of this binding + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see 123076 +help: this will change in edition 2024 + --> $DIR/mut_dont_reset_binding_mode_2024_lint.rs:9:13 + | +LL | let Foo(mut a) = &Foo(0); + | ^^^^^ +note: the lint level is defined here + --> $DIR/mut_dont_reset_binding_mode_2024_lint.rs:4:11 + | +LL | #![forbid(dereferencing_mut_binding)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: dereferencing `mut` binding + --> $DIR/mut_dont_reset_binding_mode_2024_lint.rs:14:13 + | +LL | let Foo(mut a) = &mut Foo(0); + | ^^^^^ `mut` dereferences the type of this binding + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see 123076 +help: this will change in edition 2024 + --> $DIR/mut_dont_reset_binding_mode_2024_lint.rs:14:13 + | +LL | let Foo(mut a) = &mut Foo(0); + | ^^^^^ + +error: aborting due to 2 previous errors +