Skip to content

Commit

Permalink
Auto merge of #120095 - Nadrieril:lint-resetting-mut, r=<try>
Browse files Browse the repository at this point in the history
Experiment: lint resetting `mut` bindings

We would like to fix #105647 in rust 2024. To evaluate the breakage, I wrote a lint that detects the cases where there would be a difference between now and rust 2024.

I'd like to request a crater run with `dereferencing_mut_binding` set to `deny` to see how many crates would be affected.
  • Loading branch information
bors committed Jan 18, 2024
2 parents 8424f8e + ce7328b commit 047472f
Show file tree
Hide file tree
Showing 15 changed files with 199 additions and 10 deletions.
4 changes: 4 additions & 0 deletions compiler/rustc_hir_typeck/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_hir_typeck/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
20 changes: 16 additions & 4 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -602,8 +602,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);
Expand Down Expand Up @@ -1841,7 +1853,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&unmentioned_fields.iter().map(|(_, i)| i).collect::<Vec<_>>(),
);

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",
Expand Down
31 changes: 31 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions tests/ui/or-patterns/or-patterns-default-binding-modes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// run-rustfix
#![allow(unused_variables)]
#![warn(dereferencing_mut_binding)]
fn main() {
struct U;

Expand All @@ -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`
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// run-rustfix
#![allow(unused_variables)]
#![warn(dereferencing_mut_binding)]
fn main() {
struct U;

Expand All @@ -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`
}
Original file line number Diff line number Diff line change
@@ -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;
| ----- ^^^^^^
Expand All @@ -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`.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![warn(dereferencing_mut_binding)]
fn main() {
struct U;

Expand All @@ -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`
}
Original file line number Diff line number Diff line change
@@ -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;
| ----- ^^
Expand All @@ -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`.
2 changes: 2 additions & 0 deletions tests/ui/rfcs/rfc-2005-default-binding-mode/for.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#![warn(dereferencing_mut_binding)]
struct Foo {}

pub fn main() {
let mut tups = vec![(Foo {}, Foo {})];
// 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`
}
}
21 changes: 19 additions & 2 deletions tests/ui/rfcs/rfc-2005-default-binding-mode/for.stderr
Original file line number Diff line number Diff line change
@@ -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 {
| ----- ^^^^^
Expand All @@ -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`.
1 change: 1 addition & 0 deletions tests/ui/rfcs/rfc-2005-default-binding-mode/general.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// run-pass
#![allow(unused_variables)]
#![allow(dereferencing_mut_binding)]
fn some_or_wildcard(r: &Option<i32>, b: &i32) {
let _: &i32 = match r {
Some(a) => a,
Expand Down
30 changes: 30 additions & 0 deletions tests/ui/rfcs/rfc-2005-default-binding-mode/resetting-mut.rs
Original file line number Diff line number Diff line change
@@ -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 => {}
};
}
43 changes: 43 additions & 0 deletions tests/ui/rfcs/rfc-2005-default-binding-mode/resetting-mut.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 047472f

Please sign in to comment.