Skip to content

Commit 047472f

Browse files
committed
Auto merge of rust-lang#120095 - Nadrieril:lint-resetting-mut, r=<try>
Experiment: lint resetting `mut` bindings We would like to fix rust-lang#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.
2 parents 8424f8e + ce7328b commit 047472f

15 files changed

+199
-10
lines changed

compiler/rustc_hir_typeck/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private
4646
4747
hir_typeck_deref_is_empty = this expression `Deref`s to `{$deref_ty}` which implements `is_empty`
4848
49+
hir_typeck_dereferencing_mut_binding = dereferencing `mut` binding
50+
.label = `mut` dereferences the type of this binding
51+
.help = this will change in edition 2024
52+
4953
hir_typeck_expected_default_return_type = expected `()` because of default return type
5054
5155
hir_typeck_expected_return_type = expected `{$expected}` because of return type

compiler/rustc_hir_typeck/src/errors.rs

+8
Original file line numberDiff line numberDiff line change
@@ -630,3 +630,11 @@ pub struct SuggestConvertViaMethod<'tcx> {
630630
pub expected: Ty<'tcx>,
631631
pub found: Ty<'tcx>,
632632
}
633+
634+
#[derive(LintDiagnostic)]
635+
#[diag(hir_typeck_dereferencing_mut_binding)]
636+
pub struct DereferencingMutBinding {
637+
#[label]
638+
#[help]
639+
pub span: Span,
640+
}

compiler/rustc_hir_typeck/src/pat.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_infer::infer;
1414
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
1515
use rustc_middle::mir::interpret::ErrorHandled;
1616
use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitableExt};
17-
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
17+
use rustc_session::lint;
1818
use rustc_span::edit_distance::find_best_match_for_name;
1919
use rustc_span::hygiene::DesugaringKind;
2020
use rustc_span::source_map::Spanned;
@@ -602,8 +602,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
602602

603603
// Determine the binding mode...
604604
let bm = match ba {
605-
hir::BindingAnnotation::NONE => def_bm,
606-
_ => BindingMode::convert(ba),
605+
hir::BindingAnnotation(ast::ByRef::No, hir::Mutability::Not) => def_bm,
606+
hir::BindingAnnotation(ast::ByRef::No, mutbl @ hir::Mutability::Mut) => {
607+
if let BindingMode::BindByReference(_) = def_bm {
608+
// `mut x` resets the binding mode.
609+
self.tcx.emit_spanned_lint(
610+
lint::builtin::DEREFERENCING_MUT_BINDING,
611+
pat.hir_id,
612+
pat.span,
613+
errors::DereferencingMutBinding { span: pat.span },
614+
);
615+
}
616+
BindingMode::BindByValue(mutbl)
617+
}
618+
hir::BindingAnnotation(ast::ByRef::Yes, mutbl) => BindingMode::BindByReference(mutbl),
607619
};
608620
// ...and store it in a side table:
609621
self.inh.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm);
@@ -1841,7 +1853,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18411853
&unmentioned_fields.iter().map(|(_, i)| i).collect::<Vec<_>>(),
18421854
);
18431855

1844-
self.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, "some fields are not explicitly listed", |lint| {
1856+
self.tcx.struct_span_lint_hir(lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, "some fields are not explicitly listed", |lint| {
18451857
lint.span_label(pat.span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns));
18461858
lint.help(
18471859
"ensure that all fields are mentioned explicitly by adding the suggested fields",

compiler/rustc_lint_defs/src/builtin.rs

+31
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ declare_lint_pass! {
3535
DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
3636
DEPRECATED_IN_FUTURE,
3737
DEPRECATED_WHERE_CLAUSE_LOCATION,
38+
DEREFERENCING_MUT_BINDING,
3839
DUPLICATE_MACRO_ATTRIBUTES,
3940
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
4041
ELIDED_LIFETIMES_IN_PATHS,
@@ -1578,6 +1579,36 @@ declare_lint! {
15781579
"detect mut variables which don't need to be mutable"
15791580
}
15801581

1582+
declare_lint! {
1583+
/// The `dereferencing_mut_binding` lint detects a `mut x` pattern that resets the binding mode,
1584+
/// as this behavior will change in rust 2024.
1585+
///
1586+
/// ### Example
1587+
///
1588+
/// ```rust
1589+
/// # #![warn(dereferencing_mut_binding)]
1590+
/// let x = Some(123u32);
1591+
/// let _y = match &x {
1592+
/// Some(mut x) => {
1593+
/// x += 1;
1594+
/// x
1595+
/// }
1596+
/// None => 0,
1597+
/// };
1598+
/// ```
1599+
///
1600+
/// {{produces}}
1601+
///
1602+
/// ### Explanation
1603+
///
1604+
/// Without the `mut`, `x` would have type `&u32`. Pre-2024, adding `mut` makes `x` have type
1605+
/// `u32`, which was deeped surprising. After edition 2024, adding `mut` will not change the
1606+
/// type of `x`. This lint warns users of editions before 2024 to update their code.
1607+
pub DEREFERENCING_MUT_BINDING,
1608+
Allow,
1609+
"detects `mut x` bindings that change the type of `x`"
1610+
}
1611+
15811612
declare_lint! {
15821613
/// The `unconditional_recursion` lint detects functions that cannot
15831614
/// return without calling themselves.

tests/ui/or-patterns/or-patterns-default-binding-modes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#![allow(irrefutable_let_patterns)]
66
#![allow(dropping_copy_types)]
77
#![allow(dropping_references)]
8+
#![allow(dereferencing_mut_binding)]
89

910
fn main() {
1011
// A regression test for a mistake we made at one point:

tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.fixed

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// run-rustfix
22
#![allow(unused_variables)]
3+
#![warn(dereferencing_mut_binding)]
34
fn main() {
45
struct U;
56

@@ -9,4 +10,5 @@ fn main() {
910
let mut p = (U, U);
1011
let (a, ref mut b) = &mut p;
1112
//~^ ERROR cannot move out of a mutable reference
13+
//~| WARN dereferencing `mut`
1214
}

tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// run-rustfix
22
#![allow(unused_variables)]
3+
#![warn(dereferencing_mut_binding)]
34
fn main() {
45
struct U;
56

@@ -9,4 +10,5 @@ fn main() {
910
let mut p = (U, U);
1011
let (a, mut b) = &mut p;
1112
//~^ ERROR cannot move out of a mutable reference
13+
//~| WARN dereferencing `mut`
1214
}

tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.stderr

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
1+
warning: dereferencing `mut` binding
2+
--> $DIR/move-ref-patterns-default-binding-modes-fixable.rs:11:13
3+
|
4+
LL | let (a, mut b) = &mut p;
5+
| ^^^^^ `mut` dereferences the type of this binding
6+
|
7+
help: this will change in edition 2024
8+
--> $DIR/move-ref-patterns-default-binding-modes-fixable.rs:11:13
9+
|
10+
LL | let (a, mut b) = &mut p;
11+
| ^^^^^
12+
note: the lint level is defined here
13+
--> $DIR/move-ref-patterns-default-binding-modes-fixable.rs:3:9
14+
|
15+
LL | #![warn(dereferencing_mut_binding)]
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
17+
118
error[E0507]: cannot move out of a mutable reference
2-
--> $DIR/move-ref-patterns-default-binding-modes-fixable.rs:10:22
19+
--> $DIR/move-ref-patterns-default-binding-modes-fixable.rs:11:22
320
|
421
LL | let (a, mut b) = &mut p;
522
| ----- ^^^^^^
@@ -12,6 +29,6 @@ help: consider borrowing the pattern binding
1229
LL | let (a, ref mut b) = &mut p;
1330
| +++
1431

15-
error: aborting due to 1 previous error
32+
error: aborting due to 1 previous error; 1 warning emitted
1633

1734
For more information about this error, try `rustc --explain E0507`.

tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![warn(dereferencing_mut_binding)]
12
fn main() {
23
struct U;
34

@@ -7,4 +8,5 @@ fn main() {
78
let p = (U, U);
89
let (a, mut b) = &p;
910
//~^ ERROR cannot move out of a shared reference
11+
//~| WARN dereferencing `mut`
1012
}

tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.stderr

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
1+
warning: dereferencing `mut` binding
2+
--> $DIR/move-ref-patterns-default-binding-modes.rs:9:13
3+
|
4+
LL | let (a, mut b) = &p;
5+
| ^^^^^ `mut` dereferences the type of this binding
6+
|
7+
help: this will change in edition 2024
8+
--> $DIR/move-ref-patterns-default-binding-modes.rs:9:13
9+
|
10+
LL | let (a, mut b) = &p;
11+
| ^^^^^
12+
note: the lint level is defined here
13+
--> $DIR/move-ref-patterns-default-binding-modes.rs:1:9
14+
|
15+
LL | #![warn(dereferencing_mut_binding)]
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
17+
118
error[E0507]: cannot move out of a shared reference
2-
--> $DIR/move-ref-patterns-default-binding-modes.rs:8:22
19+
--> $DIR/move-ref-patterns-default-binding-modes.rs:9:22
320
|
421
LL | let (a, mut b) = &p;
522
| ----- ^^
@@ -12,6 +29,6 @@ help: consider borrowing the pattern binding
1229
LL | let (a, ref mut b) = &p;
1330
| +++
1431

15-
error: aborting due to 1 previous error
32+
error: aborting due to 1 previous error; 1 warning emitted
1633

1734
For more information about this error, try `rustc --explain E0507`.
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
#![warn(dereferencing_mut_binding)]
12
struct Foo {}
23

34
pub fn main() {
45
let mut tups = vec![(Foo {}, Foo {})];
56
// The below desugars to &(ref n, mut m).
67
for (n, mut m) in &tups {
78
//~^ ERROR cannot move out of a shared reference
9+
//~| WARN dereferencing `mut`
810
}
911
}

tests/ui/rfcs/rfc-2005-default-binding-mode/for.stderr

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
1+
warning: dereferencing `mut` binding
2+
--> $DIR/for.rs:7:13
3+
|
4+
LL | for (n, mut m) in &tups {
5+
| ^^^^^ `mut` dereferences the type of this binding
6+
|
7+
help: this will change in edition 2024
8+
--> $DIR/for.rs:7:13
9+
|
10+
LL | for (n, mut m) in &tups {
11+
| ^^^^^
12+
note: the lint level is defined here
13+
--> $DIR/for.rs:1:9
14+
|
15+
LL | #![warn(dereferencing_mut_binding)]
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
17+
118
error[E0507]: cannot move out of a shared reference
2-
--> $DIR/for.rs:6:23
19+
--> $DIR/for.rs:7:23
320
|
421
LL | for (n, mut m) in &tups {
522
| ----- ^^^^^
@@ -12,6 +29,6 @@ help: consider borrowing the pattern binding
1229
LL | for (n, ref mut m) in &tups {
1330
| +++
1431

15-
error: aborting due to 1 previous error
32+
error: aborting due to 1 previous error; 1 warning emitted
1633

1734
For more information about this error, try `rustc --explain E0507`.

tests/ui/rfcs/rfc-2005-default-binding-mode/general.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// run-pass
22
#![allow(unused_variables)]
3+
#![allow(dereferencing_mut_binding)]
34
fn some_or_wildcard(r: &Option<i32>, b: &i32) {
45
let _: &i32 = match r {
56
Some(a) => a,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// `mut` resets the binding mode.
2+
#![deny(dereferencing_mut_binding)]
3+
4+
fn main() {
5+
let (x, mut y) = &(0, 0);
6+
//~^ ERROR dereferencing `mut`
7+
let _: &u32 = x;
8+
let _: u32 = y;
9+
10+
match &Some(5i32) {
11+
Some(mut n) => {
12+
//~^ ERROR dereferencing `mut`
13+
n += 1;
14+
let _ = n;
15+
}
16+
None => {}
17+
};
18+
if let Some(mut n) = &Some(5i32) {
19+
//~^ ERROR dereferencing `mut`
20+
n += 1;
21+
let _ = n;
22+
};
23+
match &Some(5i32) {
24+
&Some(mut n) => {
25+
n += 1;
26+
let _ = n;
27+
}
28+
None => {}
29+
};
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
error: dereferencing `mut` binding
2+
--> $DIR/resetting-mut.rs:5:13
3+
|
4+
LL | let (x, mut y) = &(0, 0);
5+
| ^^^^^ `mut` dereferences the type of this binding
6+
|
7+
help: this will change in edition 2024
8+
--> $DIR/resetting-mut.rs:5:13
9+
|
10+
LL | let (x, mut y) = &(0, 0);
11+
| ^^^^^
12+
note: the lint level is defined here
13+
--> $DIR/resetting-mut.rs:2:9
14+
|
15+
LL | #![deny(dereferencing_mut_binding)]
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
17+
18+
error: dereferencing `mut` binding
19+
--> $DIR/resetting-mut.rs:11:14
20+
|
21+
LL | Some(mut n) => {
22+
| ^^^^^ `mut` dereferences the type of this binding
23+
|
24+
help: this will change in edition 2024
25+
--> $DIR/resetting-mut.rs:11:14
26+
|
27+
LL | Some(mut n) => {
28+
| ^^^^^
29+
30+
error: dereferencing `mut` binding
31+
--> $DIR/resetting-mut.rs:18:17
32+
|
33+
LL | if let Some(mut n) = &Some(5i32) {
34+
| ^^^^^ `mut` dereferences the type of this binding
35+
|
36+
help: this will change in edition 2024
37+
--> $DIR/resetting-mut.rs:18:17
38+
|
39+
LL | if let Some(mut n) = &Some(5i32) {
40+
| ^^^^^
41+
42+
error: aborting due to 3 previous errors
43+

0 commit comments

Comments
 (0)