Skip to content

Commit 0f7308a

Browse files
NadrierilJules-Bertholet
authored andcommitted
Implement dereferencing_mut_binding lint
1 parent 548e14b commit 0f7308a

15 files changed

+222
-10
lines changed

compiler/rustc_hir_typeck/messages.ftl

Lines changed: 4 additions & 0 deletions
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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,3 +621,11 @@ pub struct NoteCallerChoosesTyForTyParam<'tcx> {
621621
pub ty_param_name: Symbol,
622622
pub found_ty: Ty<'tcx>,
623623
}
624+
625+
#[derive(LintDiagnostic)]
626+
#[diag(hir_typeck_dereferencing_mut_binding)]
627+
pub struct DereferencingMutBinding {
628+
#[label]
629+
#[help]
630+
pub span: Span,
631+
}

compiler/rustc_hir_typeck/src/pat.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_infer::infer;
1313
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
1414
use rustc_middle::mir::interpret::ErrorHandled;
1515
use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitableExt};
16-
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
16+
use rustc_session::lint;
1717
use rustc_span::edit_distance::find_best_match_for_name;
1818
use rustc_span::hygiene::DesugaringKind;
1919
use rustc_span::source_map::Spanned;
@@ -610,8 +610,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
610610

611611
// Determine the binding mode...
612612
let bm = match ba {
613-
hir::BindingAnnotation::NONE => def_bm,
614-
_ => BindingMode::convert(ba),
613+
hir::BindingAnnotation(ast::ByRef::No, hir::Mutability::Not) => def_bm,
614+
hir::BindingAnnotation(ast::ByRef::No, mutbl @ hir::Mutability::Mut) => {
615+
if let BindingMode::BindByReference(_) = def_bm {
616+
// `mut x` resets the binding mode.
617+
self.tcx.emit_node_span_lint(
618+
lint::builtin::DEREFERENCING_MUT_BINDING,
619+
pat.hir_id,
620+
pat.span,
621+
errors::DereferencingMutBinding { span: pat.span },
622+
);
623+
}
624+
BindingMode::BindByValue(mutbl)
625+
}
626+
hir::BindingAnnotation(ast::ByRef::Yes, mutbl) => BindingMode::BindByReference(mutbl),
615627
};
616628
// ...and store it in a side table:
617629
self.inh.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm);
@@ -1847,7 +1859,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18471859
&unmentioned_fields.iter().map(|(_, i)| i).collect::<Vec<_>>(),
18481860
);
18491861

1850-
self.tcx.node_span_lint(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, "some fields are not explicitly listed", |lint| {
1862+
self.tcx.node_span_lint(lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, "some fields are not explicitly listed", |lint| {
18511863
lint.span_label(pat.span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns));
18521864
lint.help(
18531865
"ensure that all fields are mentioned explicitly by adding the suggested fields",

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ declare_lint_pass! {
3838
DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
3939
DEPRECATED_IN_FUTURE,
4040
DEPRECATED_WHERE_CLAUSE_LOCATION,
41+
DEREFERENCING_MUT_BINDING,
4142
DUPLICATE_MACRO_ATTRIBUTES,
4243
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
4344
ELIDED_LIFETIMES_IN_PATHS,
@@ -1626,6 +1627,40 @@ declare_lint! {
16261627
"detect mut variables which don't need to be mutable"
16271628
}
16281629

1630+
declare_lint! {
1631+
/// The `dereferencing_mut_binding` lint detects a `mut x` pattern that resets the binding mode,
1632+
/// as this behavior will change in rust 2024.
1633+
///
1634+
/// ### Example
1635+
///
1636+
/// ```rust
1637+
/// # #![warn(dereferencing_mut_binding)]
1638+
/// let x = Some(123u32);
1639+
/// let _y = match &x {
1640+
/// Some(mut x) => {
1641+
/// x += 1;
1642+
/// x
1643+
/// }
1644+
/// None => 0,
1645+
/// };
1646+
/// ```
1647+
///
1648+
/// {{produces}}
1649+
///
1650+
/// ### Explanation
1651+
///
1652+
/// Without the `mut`, `x` would have type `&u32`. Pre-2024, adding `mut` makes `x` have type
1653+
/// `u32`, which was deeped surprising. After edition 2024, adding `mut` will not change the
1654+
/// type of `x`. This lint warns users of editions before 2024 to update their code.
1655+
pub DEREFERENCING_MUT_BINDING,
1656+
Allow,
1657+
"detects `mut x` bindings that change the type of `x`",
1658+
@future_incompatible = FutureIncompatibleInfo {
1659+
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
1660+
reference: "FIXME none yet",
1661+
};
1662+
}
1663+
16291664
declare_lint! {
16301665
/// The `unconditional_recursion` lint detects functions that cannot
16311666
/// return without calling themselves.

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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 3 additions & 0 deletions
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,6 @@ 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`
14+
//~| WARN this changes meaning in Rust 2024
1215
}

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

Lines changed: 3 additions & 0 deletions
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,6 @@ 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`
14+
//~| WARN this changes meaning in Rust 2024
1215
}

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
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+
= warning: this changes meaning in Rust 2024
8+
= note: for more information, see TODO none yet
9+
help: this will change in edition 2024
10+
--> $DIR/move-ref-patterns-default-binding-modes-fixable.rs:11:13
11+
|
12+
LL | let (a, mut b) = &mut p;
13+
| ^^^^^
14+
note: the lint level is defined here
15+
--> $DIR/move-ref-patterns-default-binding-modes-fixable.rs:3:9
16+
|
17+
LL | #![warn(dereferencing_mut_binding)]
18+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
19+
120
error[E0507]: cannot move out of a mutable reference
2-
--> $DIR/move-ref-patterns-default-binding-modes-fixable.rs:10:22
21+
--> $DIR/move-ref-patterns-default-binding-modes-fixable.rs:11:22
322
|
423
LL | let (a, mut b) = &mut p;
524
| ----- ^^^^^^
@@ -12,6 +31,6 @@ help: consider borrowing the pattern binding
1231
LL | let (a, ref mut b) = &mut p;
1332
| +++
1433

15-
error: aborting due to 1 previous error
34+
error: aborting due to 1 previous error; 1 warning emitted
1635

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

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

Lines changed: 3 additions & 0 deletions
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,6 @@ 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`
12+
//~| WARN this changes meaning in Rust 2024
1013
}

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
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+
= warning: this changes meaning in Rust 2024
8+
= note: for more information, see TODO none yet
9+
help: this will change in edition 2024
10+
--> $DIR/move-ref-patterns-default-binding-modes.rs:9:13
11+
|
12+
LL | let (a, mut b) = &p;
13+
| ^^^^^
14+
note: the lint level is defined here
15+
--> $DIR/move-ref-patterns-default-binding-modes.rs:1:9
16+
|
17+
LL | #![warn(dereferencing_mut_binding)]
18+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
19+
120
error[E0507]: cannot move out of a shared reference
2-
--> $DIR/move-ref-patterns-default-binding-modes.rs:8:22
21+
--> $DIR/move-ref-patterns-default-binding-modes.rs:9:22
322
|
423
LL | let (a, mut b) = &p;
524
| ----- ^^
@@ -12,6 +31,6 @@ help: consider borrowing the pattern binding
1231
LL | let (a, ref mut b) = &p;
1332
| +++
1433

15-
error: aborting due to 1 previous error
34+
error: aborting due to 1 previous error; 1 warning emitted
1635

1736
For more information about this error, try `rustc --explain E0507`.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
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`
10+
//~| WARN this changes meaning in Rust 2024
811
}
912
}

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
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+
= warning: this changes meaning in Rust 2024
8+
= note: for more information, see TODO none yet
9+
help: this will change in edition 2024
10+
--> $DIR/for.rs:7:13
11+
|
12+
LL | for (n, mut m) in &tups {
13+
| ^^^^^
14+
note: the lint level is defined here
15+
--> $DIR/for.rs:1:9
16+
|
17+
LL | #![warn(dereferencing_mut_binding)]
18+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
19+
120
error[E0507]: cannot move out of a shared reference
2-
--> $DIR/for.rs:6:23
21+
--> $DIR/for.rs:7:23
322
|
423
LL | for (n, mut m) in &tups {
524
| ----- ^^^^^
@@ -12,6 +31,6 @@ help: consider borrowing the pattern binding
1231
LL | for (n, ref mut m) in &tups {
1332
| +++
1433

15-
error: aborting due to 1 previous error
34+
error: aborting due to 1 previous error; 1 warning emitted
1635

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

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

Lines changed: 1 addition & 0 deletions
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,
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
//~| WARN this changes meaning in Rust 2024
8+
let _: &u32 = x;
9+
let _: u32 = y;
10+
11+
match &Some(5i32) {
12+
Some(mut n) => {
13+
//~^ ERROR dereferencing `mut`
14+
//~| WARN this changes meaning in Rust 2024
15+
n += 1;
16+
let _ = n;
17+
}
18+
None => {}
19+
};
20+
if let Some(mut n) = &Some(5i32) {
21+
//~^ ERROR dereferencing `mut`
22+
//~| WARN this changes meaning in Rust 2024
23+
n += 1;
24+
let _ = n;
25+
};
26+
match &Some(5i32) {
27+
&Some(mut n) => {
28+
n += 1;
29+
let _ = n;
30+
}
31+
None => {}
32+
};
33+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
= warning: this changes meaning in Rust 2024
8+
= note: for more information, see TODO none yet
9+
help: this will change in edition 2024
10+
--> $DIR/resetting-mut.rs:5:13
11+
|
12+
LL | let (x, mut y) = &(0, 0);
13+
| ^^^^^
14+
note: the lint level is defined here
15+
--> $DIR/resetting-mut.rs:2:9
16+
|
17+
LL | #![deny(dereferencing_mut_binding)]
18+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
19+
20+
error: dereferencing `mut` binding
21+
--> $DIR/resetting-mut.rs:12:14
22+
|
23+
LL | Some(mut n) => {
24+
| ^^^^^ `mut` dereferences the type of this binding
25+
|
26+
= warning: this changes meaning in Rust 2024
27+
= note: for more information, see TODO none yet
28+
help: this will change in edition 2024
29+
--> $DIR/resetting-mut.rs:12:14
30+
|
31+
LL | Some(mut n) => {
32+
| ^^^^^
33+
34+
error: dereferencing `mut` binding
35+
--> $DIR/resetting-mut.rs:20:17
36+
|
37+
LL | if let Some(mut n) = &Some(5i32) {
38+
| ^^^^^ `mut` dereferences the type of this binding
39+
|
40+
= warning: this changes meaning in Rust 2024
41+
= note: for more information, see TODO none yet
42+
help: this will change in edition 2024
43+
--> $DIR/resetting-mut.rs:20:17
44+
|
45+
LL | if let Some(mut n) = &Some(5i32) {
46+
| ^^^^^
47+
48+
error: aborting due to 3 previous errors
49+

0 commit comments

Comments
 (0)