From 3c7f1f16016d8a19a549f2c34a05cfdf8f79968b Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 23 Jun 2022 20:33:40 +0400 Subject: [PATCH 1/3] Suggest defining variable as mutable on `&mut _` type mismatch in pats --- compiler/rustc_typeck/src/check/pat.rs | 51 ++++++++++ .../ref-pat-suggestions.fixed | 13 +++ .../mismatched_types/ref-pat-suggestions.rs | 13 +++ .../ref-pat-suggestions.stderr | 92 ++++++++++++++++++- src/test/ui/pattern/for-loop-bad-item.stderr | 5 + 5 files changed, 173 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index f45b94bcdff45..58fd11200c901 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -663,6 +663,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ast::Mutability::Not => "", }; + let mut_var_suggestion = 'block: { + if !matches!(mutbl, ast::Mutability::Mut) { + break 'block None; + } + + let ident_kind = match binding_parent { + hir::Node::Param(_) => Some("parameter"), + hir::Node::Local(_) => Some("variable"), + hir::Node::Arm(_) => Some("binding"), + + // Provide diagnostics only if the parent pattern is struct-like, + // i.e. where `mut binding` makes sense + hir::Node::Pat(Pat { kind, .. }) => match kind { + PatKind::Struct(..) + | PatKind::TupleStruct(..) + | PatKind::Or(..) + | PatKind::Tuple(..) + | PatKind::Slice(..) => Some("binding"), + + PatKind::Wild + | PatKind::Binding(..) + | PatKind::Path(..) + | PatKind::Box(..) + | PatKind::Ref(..) + | PatKind::Lit(..) + | PatKind::Range(..) => None, + }, + + // Don't provide suggestions in other cases + _ => None, + }; + + ident_kind.map(|thing| ( + pat.span, + format!("to declare a mutable {thing} use `mut variable_name`"), + format!("mut {binding}"), + )) + + }; + match binding_parent { // Check that there is explicit type (ie this is not a closure param with inferred type) // so we don't suggest moving something to the type that does not exist @@ -675,6 +715,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ], Applicability::MachineApplicable ); + + if let Some((sp, msg, sugg)) = mut_var_suggestion { + err.span_note(sp, format!("{msg}: `{sugg}`")); + } } hir::Node::Param(_) | hir::Node::Arm(_) | hir::Node::Pat(_) => { // rely on match ergonomics or it might be nested `&&pat` @@ -684,6 +728,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "", Applicability::MaybeIncorrect, ); + + if let Some((sp, msg, sugg)) = mut_var_suggestion { + err.span_note(sp, format!("{msg}: `{sugg}`")); + } + } + _ if let Some((sp, msg, sugg)) = mut_var_suggestion => { + err.span_suggestion(sp, msg, sugg, Applicability::MachineApplicable); } _ => {} // don't provide suggestions in other cases #55175 } diff --git a/src/test/ui/mismatched_types/ref-pat-suggestions.fixed b/src/test/ui/mismatched_types/ref-pat-suggestions.fixed index ab8483eef49fa..d50acd1ac62d2 100644 --- a/src/test/ui/mismatched_types/ref-pat-suggestions.fixed +++ b/src/test/ui/mismatched_types/ref-pat-suggestions.fixed @@ -21,4 +21,17 @@ fn main() { let _ = |&mut _a: &mut u32| (); //~ ERROR mismatched types let _ = |&_a: &u32| (); //~ ERROR mismatched types let _ = |&mut _a: &mut u32| (); //~ ERROR mismatched types + + #[allow(unused_mut)] + { + struct S(u8); + + let mut _a = 0; //~ ERROR mismatched types + let S(_b) = S(0); //~ ERROR mismatched types + let (_c,) = (0,); //~ ERROR mismatched types + + match 0 { + _d => {} //~ ERROR mismatched types + } + } } diff --git a/src/test/ui/mismatched_types/ref-pat-suggestions.rs b/src/test/ui/mismatched_types/ref-pat-suggestions.rs index 7e55539aa3d1b..1a77f68769224 100644 --- a/src/test/ui/mismatched_types/ref-pat-suggestions.rs +++ b/src/test/ui/mismatched_types/ref-pat-suggestions.rs @@ -21,4 +21,17 @@ fn main() { let _ = |&mut &_a: &mut u32| (); //~ ERROR mismatched types let _ = |&&mut _a: &u32| (); //~ ERROR mismatched types let _ = |&mut &mut _a: &mut u32| (); //~ ERROR mismatched types + + #[allow(unused_mut)] + { + struct S(u8); + + let &mut _a = 0; //~ ERROR mismatched types + let S(&mut _b) = S(0); //~ ERROR mismatched types + let (&mut _c,) = (0,); //~ ERROR mismatched types + + match 0 { + &mut _d => {} //~ ERROR mismatched types + } + } } diff --git a/src/test/ui/mismatched_types/ref-pat-suggestions.stderr b/src/test/ui/mismatched_types/ref-pat-suggestions.stderr index 0e8f8853fb751..cd86bb532abda 100644 --- a/src/test/ui/mismatched_types/ref-pat-suggestions.stderr +++ b/src/test/ui/mismatched_types/ref-pat-suggestions.stderr @@ -24,6 +24,11 @@ LL | fn _f1(&mut _a: u32) {} | = note: expected type `u32` found mutable reference `&mut _` +note: to declare a mutable parameter use `mut variable_name`: `mut _a` + --> $DIR/ref-pat-suggestions.rs:4:8 + | +LL | fn _f1(&mut _a: u32) {} + | ^^^^^^^ help: to take parameter `_a` by reference, move `&mut` to the type | LL - fn _f1(&mut _a: u32) {} @@ -122,6 +127,11 @@ LL | let _: fn(u32) = |&mut _a| (); | = note: expected type `u32` found mutable reference `&mut _` +note: to declare a mutable parameter use `mut variable_name`: `mut _a` + --> $DIR/ref-pat-suggestions.rs:12:23 + | +LL | let _: fn(u32) = |&mut _a| (); + | ^^^^^^^ help: consider removing `&mut` from the pattern | LL - let _: fn(u32) = |&mut _a| (); @@ -222,6 +232,11 @@ LL | let _ = |&mut _a: u32| (); | = note: expected type `u32` found mutable reference `&mut _` +note: to declare a mutable parameter use `mut variable_name`: `mut _a` + --> $DIR/ref-pat-suggestions.rs:19:14 + | +LL | let _ = |&mut _a: u32| (); + | ^^^^^^^ help: to take parameter `_a` by reference, move `&mut` to the type | LL - let _ = |&mut _a: u32| (); @@ -292,6 +307,81 @@ LL - let _ = |&mut &mut _a: &mut u32| (); LL + let _ = |&mut _a: &mut u32| (); | -error: aborting due to 18 previous errors +error[E0308]: mismatched types + --> $DIR/ref-pat-suggestions.rs:29:13 + | +LL | let &mut _a = 0; + | ^^^^^^^ - this expression has type `{integer}` + | | + | expected integer, found `&mut _` + | help: to declare a mutable variable use `mut variable_name`: `mut _a` + | + = note: expected type `{integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/ref-pat-suggestions.rs:30:15 + | +LL | let S(&mut _b) = S(0); + | ^^^^^^^ ---- this expression has type `S` + | | + | expected `u8`, found `&mut _` + | + = note: expected type `u8` + found mutable reference `&mut _` +note: to declare a mutable binding use `mut variable_name`: `mut _b` + --> $DIR/ref-pat-suggestions.rs:30:15 + | +LL | let S(&mut _b) = S(0); + | ^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let S(&mut _b) = S(0); +LL + let S(_b) = S(0); + | + +error[E0308]: mismatched types + --> $DIR/ref-pat-suggestions.rs:31:14 + | +LL | let (&mut _c,) = (0,); + | ^^^^^^^ ---- this expression has type `({integer},)` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use `mut variable_name`: `mut _c` + --> $DIR/ref-pat-suggestions.rs:31:14 + | +LL | let (&mut _c,) = (0,); + | ^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let (&mut _c,) = (0,); +LL + let (_c,) = (0,); + | + +error[E0308]: mismatched types + --> $DIR/ref-pat-suggestions.rs:34:13 + | +LL | match 0 { + | - this expression has type `{integer}` +LL | &mut _d => {} + | ^^^^^^^ expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use `mut variable_name`: `mut _d` + --> $DIR/ref-pat-suggestions.rs:34:13 + | +LL | &mut _d => {} + | ^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - &mut _d => {} +LL + _d => {} + | + +error: aborting due to 22 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/pattern/for-loop-bad-item.stderr b/src/test/ui/pattern/for-loop-bad-item.stderr index ad737f7bd15e1..212d66f649763 100644 --- a/src/test/ui/pattern/for-loop-bad-item.stderr +++ b/src/test/ui/pattern/for-loop-bad-item.stderr @@ -8,6 +8,11 @@ LL | for ((_, _), (&mut c, _)) in &mut map { | = note: expected type `char` found mutable reference `&mut _` +note: to declare a mutable binding use `mut variable_name`: `mut c` + --> $DIR/for-loop-bad-item.rs:7:19 + | +LL | for ((_, _), (&mut c, _)) in &mut map { + | ^^^^^^ help: consider removing `&mut` from the pattern | LL - for ((_, _), (&mut c, _)) in &mut map { From e25129b055cae83e1a005d84c8119b74416f94ad Mon Sep 17 00:00:00 2001 From: Waffle Maybe Date: Fri, 24 Jun 2022 21:05:02 +0400 Subject: [PATCH 2/3] take advantage of a labelled block Co-authored-by: Michael Goulet --- compiler/rustc_typeck/src/check/pat.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 58fd11200c901..5d40f05834355 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -669,9 +669,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let ident_kind = match binding_parent { - hir::Node::Param(_) => Some("parameter"), - hir::Node::Local(_) => Some("variable"), - hir::Node::Arm(_) => Some("binding"), + hir::Node::Param(_) => "parameter", + hir::Node::Local(_) => "variable", + hir::Node::Arm(_) => "binding", // Provide diagnostics only if the parent pattern is struct-like, // i.e. where `mut binding` makes sense @@ -680,7 +680,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | PatKind::TupleStruct(..) | PatKind::Or(..) | PatKind::Tuple(..) - | PatKind::Slice(..) => Some("binding"), + | PatKind::Slice(..) => "binding", PatKind::Wild | PatKind::Binding(..) @@ -688,16 +688,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | PatKind::Box(..) | PatKind::Ref(..) | PatKind::Lit(..) - | PatKind::Range(..) => None, + | PatKind::Range(..) => break 'block None, }, // Don't provide suggestions in other cases - _ => None, + _ => break 'block None, }; - ident_kind.map(|thing| ( + Some(( pat.span, - format!("to declare a mutable {thing} use `mut variable_name`"), + format!("to declare a mutable {ident_kind} use `mut variable_name`"), format!("mut {binding}"), )) From 1dfb53b7dad1d5f68466a68b068b8030d216ce3f Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 24 Jun 2022 21:18:26 +0400 Subject: [PATCH 3/3] improve wording of a suggestion --- compiler/rustc_typeck/src/check/pat.rs | 2 +- .../ui/mismatched_types/ref-pat-suggestions.stderr | 14 +++++++------- src/test/ui/pattern/for-loop-bad-item.stderr | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 5d40f05834355..e1ec9f13cd166 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -697,7 +697,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(( pat.span, - format!("to declare a mutable {ident_kind} use `mut variable_name`"), + format!("to declare a mutable {ident_kind} use"), format!("mut {binding}"), )) diff --git a/src/test/ui/mismatched_types/ref-pat-suggestions.stderr b/src/test/ui/mismatched_types/ref-pat-suggestions.stderr index cd86bb532abda..d9501a9bbc61e 100644 --- a/src/test/ui/mismatched_types/ref-pat-suggestions.stderr +++ b/src/test/ui/mismatched_types/ref-pat-suggestions.stderr @@ -24,7 +24,7 @@ LL | fn _f1(&mut _a: u32) {} | = note: expected type `u32` found mutable reference `&mut _` -note: to declare a mutable parameter use `mut variable_name`: `mut _a` +note: to declare a mutable parameter use: `mut _a` --> $DIR/ref-pat-suggestions.rs:4:8 | LL | fn _f1(&mut _a: u32) {} @@ -127,7 +127,7 @@ LL | let _: fn(u32) = |&mut _a| (); | = note: expected type `u32` found mutable reference `&mut _` -note: to declare a mutable parameter use `mut variable_name`: `mut _a` +note: to declare a mutable parameter use: `mut _a` --> $DIR/ref-pat-suggestions.rs:12:23 | LL | let _: fn(u32) = |&mut _a| (); @@ -232,7 +232,7 @@ LL | let _ = |&mut _a: u32| (); | = note: expected type `u32` found mutable reference `&mut _` -note: to declare a mutable parameter use `mut variable_name`: `mut _a` +note: to declare a mutable parameter use: `mut _a` --> $DIR/ref-pat-suggestions.rs:19:14 | LL | let _ = |&mut _a: u32| (); @@ -314,7 +314,7 @@ LL | let &mut _a = 0; | ^^^^^^^ - this expression has type `{integer}` | | | expected integer, found `&mut _` - | help: to declare a mutable variable use `mut variable_name`: `mut _a` + | help: to declare a mutable variable use: `mut _a` | = note: expected type `{integer}` found mutable reference `&mut _` @@ -329,7 +329,7 @@ LL | let S(&mut _b) = S(0); | = note: expected type `u8` found mutable reference `&mut _` -note: to declare a mutable binding use `mut variable_name`: `mut _b` +note: to declare a mutable binding use: `mut _b` --> $DIR/ref-pat-suggestions.rs:30:15 | LL | let S(&mut _b) = S(0); @@ -350,7 +350,7 @@ LL | let (&mut _c,) = (0,); | = note: expected type `{integer}` found mutable reference `&mut _` -note: to declare a mutable binding use `mut variable_name`: `mut _c` +note: to declare a mutable binding use: `mut _c` --> $DIR/ref-pat-suggestions.rs:31:14 | LL | let (&mut _c,) = (0,); @@ -371,7 +371,7 @@ LL | &mut _d => {} | = note: expected type `{integer}` found mutable reference `&mut _` -note: to declare a mutable binding use `mut variable_name`: `mut _d` +note: to declare a mutable binding use: `mut _d` --> $DIR/ref-pat-suggestions.rs:34:13 | LL | &mut _d => {} diff --git a/src/test/ui/pattern/for-loop-bad-item.stderr b/src/test/ui/pattern/for-loop-bad-item.stderr index 212d66f649763..f064a25a9c9a4 100644 --- a/src/test/ui/pattern/for-loop-bad-item.stderr +++ b/src/test/ui/pattern/for-loop-bad-item.stderr @@ -8,7 +8,7 @@ LL | for ((_, _), (&mut c, _)) in &mut map { | = note: expected type `char` found mutable reference `&mut _` -note: to declare a mutable binding use `mut variable_name`: `mut c` +note: to declare a mutable binding use: `mut c` --> $DIR/for-loop-bad-item.rs:7:19 | LL | for ((_, _), (&mut c, _)) in &mut map {