diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 4fd9391acc368..ff785ade1aa88 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 4f92906888796..8140ed06a3148 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -630,3 +630,11 @@ pub struct SuggestConvertViaMethod<'tcx> { pub expected: Ty<'tcx>, pub found: Ty<'tcx>, } + +#[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 95813cb68a664..563680d7fd615 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -14,7 +14,7 @@ use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitableExt}; -use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; +use rustc_session::lint; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::Spanned; @@ -600,8 +600,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Determine the binding mode... let bm = match ba { - hir::BindingAnnotation::NONE => def_bm, - _ => BindingMode::convert(ba), + hir::BindingAnnotation(ast::ByRef::No, hir::Mutability::Not) => def_bm, + hir::BindingAnnotation(ast::ByRef::No, mutbl @ hir::Mutability::Mut) => { + if let BindingMode::BindByReference(_) = def_bm { + // `mut x` resets the binding mode. + self.tcx.emit_spanned_lint( + lint::builtin::DEREFERENCING_MUT_BINDING, + pat.hir_id, + pat.span, + errors::DereferencingMutBinding { span: pat.span }, + ); + } + BindingMode::BindByValue(mutbl) + } + hir::BindingAnnotation(ast::ByRef::Yes, mutbl) => BindingMode::BindByReference(mutbl), }; // ...and store it in a side table: self.inh.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm); @@ -1838,7 +1850,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &unmentioned_fields.iter().map(|(_, i)| i).collect::>(), ); - self.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, "some fields are not explicitly listed", |lint| { + self.tcx.struct_span_lint_hir(lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, "some fields are not explicitly listed", |lint| { lint.span_label(pat.span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns)); lint.help( "ensure that all fields are mentioned explicitly by adding the suggested fields", diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index e917e7cb02b39..bf171b1204f98 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -35,6 +35,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, @@ -1578,6 +1579,36 @@ 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`" +} + declare_lint! { /// The `unconditional_recursion` lint detects functions that cannot /// return without calling themselves. diff --git a/tests/ui/or-patterns/or-patterns-default-binding-modes.rs b/tests/ui/or-patterns/or-patterns-default-binding-modes.rs index df6aab0e6a885..e65546c6b812f 100644 --- a/tests/ui/or-patterns/or-patterns-default-binding-modes.rs +++ b/tests/ui/or-patterns/or-patterns-default-binding-modes.rs @@ -5,6 +5,7 @@ #![allow(irrefutable_let_patterns)] #![allow(dropping_copy_types)] #![allow(dropping_references)] +#![allow(dereferencing_mut_binding)] fn main() { // A regression test for a mistake we made at one point: diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.fixed b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.fixed index 5f04fc83d37ab..4814dd8585d53 100644 --- a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.fixed +++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.fixed @@ -1,5 +1,6 @@ // run-rustfix #![allow(unused_variables)] +#![warn(dereferencing_mut_binding)] fn main() { struct U; @@ -9,4 +10,5 @@ fn main() { let mut p = (U, U); let (a, ref mut b) = &mut p; //~^ ERROR cannot move out of a mutable reference + //~| WARN dereferencing `mut` } diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.rs b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.rs index 5dc1ae2feb5f0..a77898f8ae392 100644 --- a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.rs +++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.rs @@ -1,5 +1,6 @@ // run-rustfix #![allow(unused_variables)] +#![warn(dereferencing_mut_binding)] fn main() { struct U; @@ -9,4 +10,5 @@ fn main() { let mut p = (U, U); let (a, mut b) = &mut p; //~^ ERROR cannot move out of a mutable reference + //~| WARN dereferencing `mut` } diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.stderr b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.stderr index 85379d6605bca..ebbfd97439611 100644 --- a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.stderr +++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.stderr @@ -1,5 +1,22 @@ +warning: dereferencing `mut` binding + --> $DIR/move-ref-patterns-default-binding-modes-fixable.rs:11:13 + | +LL | let (a, mut b) = &mut p; + | ^^^^^ `mut` dereferences the type of this binding + | +help: this will change in edition 2024 + --> $DIR/move-ref-patterns-default-binding-modes-fixable.rs:11:13 + | +LL | let (a, mut b) = &mut p; + | ^^^^^ +note: the lint level is defined here + --> $DIR/move-ref-patterns-default-binding-modes-fixable.rs:3:9 + | +LL | #![warn(dereferencing_mut_binding)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0507]: cannot move out of a mutable reference - --> $DIR/move-ref-patterns-default-binding-modes-fixable.rs:10:22 + --> $DIR/move-ref-patterns-default-binding-modes-fixable.rs:11:22 | LL | let (a, mut b) = &mut p; | ----- ^^^^^^ @@ -12,6 +29,6 @@ help: consider borrowing the pattern binding LL | let (a, ref mut b) = &mut p; | +++ -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.rs b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.rs index 6c913c245130d..08c642f43c83f 100644 --- a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.rs +++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.rs @@ -1,3 +1,4 @@ +#![warn(dereferencing_mut_binding)] fn main() { struct U; @@ -7,4 +8,5 @@ fn main() { let p = (U, U); let (a, mut b) = &p; //~^ ERROR cannot move out of a shared reference + //~| WARN dereferencing `mut` } diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.stderr b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.stderr index 494e5e2b2e85f..ae7ac394cc0e9 100644 --- a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.stderr +++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.stderr @@ -1,5 +1,22 @@ +warning: dereferencing `mut` binding + --> $DIR/move-ref-patterns-default-binding-modes.rs:9:13 + | +LL | let (a, mut b) = &p; + | ^^^^^ `mut` dereferences the type of this binding + | +help: this will change in edition 2024 + --> $DIR/move-ref-patterns-default-binding-modes.rs:9:13 + | +LL | let (a, mut b) = &p; + | ^^^^^ +note: the lint level is defined here + --> $DIR/move-ref-patterns-default-binding-modes.rs:1:9 + | +LL | #![warn(dereferencing_mut_binding)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0507]: cannot move out of a shared reference - --> $DIR/move-ref-patterns-default-binding-modes.rs:8:22 + --> $DIR/move-ref-patterns-default-binding-modes.rs:9:22 | LL | let (a, mut b) = &p; | ----- ^^ @@ -12,6 +29,6 @@ help: consider borrowing the pattern binding LL | let (a, ref mut b) = &p; | +++ -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/for.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/for.rs index d6c5a13b1bdb0..61d508ed191ac 100644 --- a/tests/ui/rfcs/rfc-2005-default-binding-mode/for.rs +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/for.rs @@ -1,3 +1,4 @@ +#![warn(dereferencing_mut_binding)] struct Foo {} pub fn main() { @@ -5,5 +6,6 @@ pub fn main() { // The below desugars to &(ref n, mut m). for (n, mut m) in &tups { //~^ ERROR cannot move out of a shared reference + //~| WARN dereferencing `mut` } } diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/for.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/for.stderr index 8f720daf11ee6..57d14ea5b337e 100644 --- a/tests/ui/rfcs/rfc-2005-default-binding-mode/for.stderr +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/for.stderr @@ -1,5 +1,22 @@ +warning: dereferencing `mut` binding + --> $DIR/for.rs:7:13 + | +LL | for (n, mut m) in &tups { + | ^^^^^ `mut` dereferences the type of this binding + | +help: this will change in edition 2024 + --> $DIR/for.rs:7:13 + | +LL | for (n, mut m) in &tups { + | ^^^^^ +note: the lint level is defined here + --> $DIR/for.rs:1:9 + | +LL | #![warn(dereferencing_mut_binding)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0507]: cannot move out of a shared reference - --> $DIR/for.rs:6:23 + --> $DIR/for.rs:7:23 | LL | for (n, mut m) in &tups { | ----- ^^^^^ @@ -12,6 +29,6 @@ help: consider borrowing the pattern binding LL | for (n, ref mut m) in &tups { | +++ -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/general.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/general.rs index 0207f607be8ea..e0d7086556198 100644 --- a/tests/ui/rfcs/rfc-2005-default-binding-mode/general.rs +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/general.rs @@ -1,5 +1,6 @@ // run-pass #![allow(unused_variables)] +#![allow(dereferencing_mut_binding)] fn some_or_wildcard(r: &Option, b: &i32) { let _: &i32 = match r { Some(a) => a, diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/resetting-mut.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/resetting-mut.rs new file mode 100644 index 0000000000000..ead3969867273 --- /dev/null +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/resetting-mut.rs @@ -0,0 +1,30 @@ +// `mut` resets the binding mode. +#![deny(dereferencing_mut_binding)] + +fn main() { + let (x, mut y) = &(0, 0); + //~^ ERROR dereferencing `mut` + let _: &u32 = x; + let _: u32 = y; + + match &Some(5i32) { + Some(mut n) => { + //~^ ERROR dereferencing `mut` + n += 1; + let _ = n; + } + None => {} + }; + if let Some(mut n) = &Some(5i32) { + //~^ ERROR dereferencing `mut` + n += 1; + let _ = n; + }; + match &Some(5i32) { + &Some(mut n) => { + n += 1; + let _ = n; + } + None => {} + }; +} diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/resetting-mut.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/resetting-mut.stderr new file mode 100644 index 0000000000000..7b061ac93ddb5 --- /dev/null +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/resetting-mut.stderr @@ -0,0 +1,43 @@ +error: dereferencing `mut` binding + --> $DIR/resetting-mut.rs:5:13 + | +LL | let (x, mut y) = &(0, 0); + | ^^^^^ `mut` dereferences the type of this binding + | +help: this will change in edition 2024 + --> $DIR/resetting-mut.rs:5:13 + | +LL | let (x, mut y) = &(0, 0); + | ^^^^^ +note: the lint level is defined here + --> $DIR/resetting-mut.rs:2:9 + | +LL | #![deny(dereferencing_mut_binding)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: dereferencing `mut` binding + --> $DIR/resetting-mut.rs:11:14 + | +LL | Some(mut n) => { + | ^^^^^ `mut` dereferences the type of this binding + | +help: this will change in edition 2024 + --> $DIR/resetting-mut.rs:11:14 + | +LL | Some(mut n) => { + | ^^^^^ + +error: dereferencing `mut` binding + --> $DIR/resetting-mut.rs:18:17 + | +LL | if let Some(mut n) = &Some(5i32) { + | ^^^^^ `mut` dereferences the type of this binding + | +help: this will change in edition 2024 + --> $DIR/resetting-mut.rs:18:17 + | +LL | if let Some(mut n) = &Some(5i32) { + | ^^^^^ + +error: aborting due to 3 previous errors +