Skip to content

Commit

Permalink
Improve 'mut ' diagnostic.
Browse files Browse the repository at this point in the history
  • Loading branch information
Centril committed Aug 27, 2019
1 parent dbbe336 commit 42e895d
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 37 deletions.
54 changes: 34 additions & 20 deletions src/libsyntax/parse/parser/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,22 +405,13 @@ impl<'a> Parser<'a> {
let mut pat = self.parse_pat(Some("identifier"))?;

// Add `mut` to any binding in the parsed pattern.
struct AddMut;
impl MutVisitor for AddMut {
fn visit_pat(&mut self, pat: &mut P<Pat>) {
if let PatKind::Ident(BindingMode::ByValue(ref mut m), ..) = pat.node {
*m = Mutability::Mutable;
}
noop_visit_pat(pat, self);
}
}
AddMut.visit_pat(&mut pat);
let changed_any_binding = Self::make_all_value_bindings_mutable(&mut pat);

// Unwrap; If we don't have `mut $ident`, error.
let pat = pat.into_inner();
match &pat.node {
PatKind::Ident(..) => {}
_ => self.ban_mut_general_pat(mut_span, &pat),
_ => self.ban_mut_general_pat(mut_span, &pat, changed_any_binding),
}

Ok(pat.node)
Expand All @@ -442,17 +433,40 @@ impl<'a> Parser<'a> {
self.parse_pat_ident(BindingMode::ByRef(Mutability::Mutable))
}

/// Turn all by-value immutable bindings in a pattern into mutable bindings.
/// Returns `true` if any change was made.
fn make_all_value_bindings_mutable(pat: &mut P<Pat>) -> bool {
struct AddMut(bool);
impl MutVisitor for AddMut {
fn visit_pat(&mut self, pat: &mut P<Pat>) {
if let PatKind::Ident(BindingMode::ByValue(ref mut m @ Mutability::Immutable), ..)
= pat.node
{
*m = Mutability::Mutable;
self.0 = true;
}
noop_visit_pat(pat, self);
}
}

let mut add_mut = AddMut(false);
add_mut.visit_pat(pat);
add_mut.0
}

/// Error on `mut $pat` where `$pat` is not an ident.
fn ban_mut_general_pat(&self, lo: Span, pat: &Pat) {
fn ban_mut_general_pat(&self, lo: Span, pat: &Pat, changed_any_binding: bool) {
let span = lo.to(pat.span);
self.struct_span_err(span, "`mut` must be attached to each individual binding")
.span_suggestion(
span,
"add `mut` to each binding",
pprust::pat_to_string(&pat),
Applicability::MachineApplicable,
)
.emit();
let fix = pprust::pat_to_string(&pat);
let (problem, suggestion) = if changed_any_binding {
("`mut` must be attached to each individual binding", "add `mut` to each binding")
} else {
("`mut` must be followed by a named binding", "remove the `mut` prefix")
};
self.struct_span_err(span, problem)
.span_suggestion(span, suggestion, fix, Applicability::MachineApplicable)
.note("`mut` may be followed by `variable` and `variable @ pattern`")
.emit()
}

/// Eat any extraneous `mut`s and error + recover if we ate any.
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/parser/issue-32501.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ fn main() {
let mut b = 0;
let mut _b = 0;
let mut _ = 0;
//~^ ERROR `mut` must be attached to each individual binding
//~^ ERROR `mut` must be followed by a named binding
}
6 changes: 4 additions & 2 deletions src/test/ui/parser/issue-32501.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
error: `mut` must be attached to each individual binding
error: `mut` must be followed by a named binding
--> $DIR/issue-32501.rs:7:9
|
LL | let mut _ = 0;
| ^^^^^ help: add `mut` to each binding: `_`
| ^^^^^ help: remove the `mut` prefix: `_`
|
= note: `mut` may be followed by `variable` and `variable @ pattern`

error: aborting due to previous error

3 changes: 3 additions & 0 deletions src/test/ui/parser/mut-patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
#![allow(warnings)]

pub fn main() {
let mut _ = 0; //~ ERROR `mut` must be followed by a named binding
let mut (_, _) = (0, 0); //~ ERROR `mut` must be followed by a named binding

let mut mut x = 0;
//~^ ERROR `mut` on a binding may not be repeated
//~| remove the additional `mut`s
Expand Down
46 changes: 35 additions & 11 deletions src/test/ui/parser/mut-patterns.stderr
Original file line number Diff line number Diff line change
@@ -1,29 +1,49 @@
error: `mut` must be followed by a named binding
--> $DIR/mut-patterns.rs:9:9
|
LL | let mut _ = 0;
| ^^^^^ help: remove the `mut` prefix: `_`
|
= note: `mut` may be followed by `variable` and `variable @ pattern`

error: `mut` must be followed by a named binding
--> $DIR/mut-patterns.rs:10:9
|
LL | let mut (_, _) = (0, 0);
| ^^^^^^^^^^ help: remove the `mut` prefix: `(_, _)`
|
= note: `mut` may be followed by `variable` and `variable @ pattern`

error: `mut` on a binding may not be repeated
--> $DIR/mut-patterns.rs:9:13
--> $DIR/mut-patterns.rs:12:13
|
LL | let mut mut x = 0;
| ^^^ help: remove the additional `mut`s

error: `mut` must be attached to each individual binding
--> $DIR/mut-patterns.rs:14:9
--> $DIR/mut-patterns.rs:17:9
|
LL | let mut Foo { x: x } = Foo { x: 3 };
| ^^^^^^^^^^^^^^^^ help: add `mut` to each binding: `Foo { x: mut x }`
|
= note: `mut` may be followed by `variable` and `variable @ pattern`

error: `mut` must be attached to each individual binding
--> $DIR/mut-patterns.rs:18:9
--> $DIR/mut-patterns.rs:21:9
|
LL | let mut Foo { x } = Foo { x: 3 };
| ^^^^^^^^^^^^^ help: add `mut` to each binding: `Foo { mut x }`
|
= note: `mut` may be followed by `variable` and `variable @ pattern`

error: `mut` on a binding may not be repeated
--> $DIR/mut-patterns.rs:23:13
--> $DIR/mut-patterns.rs:26:13
|
LL | let mut mut yield(become, await) = r#yield(0, 0);
| ^^^ help: remove the additional `mut`s

error: expected identifier, found reserved keyword `yield`
--> $DIR/mut-patterns.rs:23:17
--> $DIR/mut-patterns.rs:26:17
|
LL | let mut mut yield(become, await) = r#yield(0, 0);
| ^^^^^ expected identifier, found reserved keyword
Expand All @@ -33,7 +53,7 @@ LL | let mut mut r#yield(become, await) = r#yield(0, 0);
| ^^^^^^^

error: expected identifier, found reserved keyword `become`
--> $DIR/mut-patterns.rs:23:23
--> $DIR/mut-patterns.rs:26:23
|
LL | let mut mut yield(become, await) = r#yield(0, 0);
| ^^^^^^ expected identifier, found reserved keyword
Expand All @@ -43,7 +63,7 @@ LL | let mut mut yield(r#become, await) = r#yield(0, 0);
| ^^^^^^^^

error: expected identifier, found reserved keyword `await`
--> $DIR/mut-patterns.rs:23:31
--> $DIR/mut-patterns.rs:26:31
|
LL | let mut mut yield(become, await) = r#yield(0, 0);
| ^^^^^ expected identifier, found reserved keyword
Expand All @@ -53,25 +73,29 @@ LL | let mut mut yield(become, r#await) = r#yield(0, 0);
| ^^^^^^^

error: `mut` must be attached to each individual binding
--> $DIR/mut-patterns.rs:23:9
--> $DIR/mut-patterns.rs:26:9
|
LL | let mut mut yield(become, await) = r#yield(0, 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `mut` to each binding: `r#yield(mut r#become, mut r#await)`
|
= note: `mut` may be followed by `variable` and `variable @ pattern`

error: `mut` must be attached to each individual binding
--> $DIR/mut-patterns.rs:32:9
--> $DIR/mut-patterns.rs:35:9
|
LL | let mut W(mut a, W(b, W(ref c, W(d, B { box f }))))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `mut` to each binding: `W(mut a, W(mut b, W(ref c, W(mut d, B { box mut f }))))`
|
= note: `mut` may be followed by `variable` and `variable @ pattern`

error: expected identifier, found `x`
--> $DIR/mut-patterns.rs:39:21
--> $DIR/mut-patterns.rs:42:21
|
LL | let mut $p = 0;
| ^^ expected identifier
...
LL | foo!(x);
| -------- in this macro invocation

error: aborting due to 10 previous errors
error: aborting due to 12 previous errors

2 changes: 1 addition & 1 deletion src/test/ui/self/self_type_keyword.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub fn main() {
ref Self => (),
//~^ ERROR expected identifier, found keyword `Self`
mut Self => (),
//~^ ERROR `mut` must be attached to each individual binding
//~^ ERROR `mut` must be followed by a named binding
//~| ERROR cannot find unit struct/variant or constant `Self`
ref mut Self => (),
//~^ ERROR expected identifier, found keyword `Self`
Expand Down
6 changes: 4 additions & 2 deletions src/test/ui/self/self_type_keyword.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ error: expected identifier, found keyword `Self`
LL | ref Self => (),
| ^^^^ expected identifier, found keyword

error: `mut` must be attached to each individual binding
error: `mut` must be followed by a named binding
--> $DIR/self_type_keyword.rs:16:9
|
LL | mut Self => (),
| ^^^^^^^^ help: add `mut` to each binding: `Self`
| ^^^^^^^^ help: remove the `mut` prefix: `Self`
|
= note: `mut` may be followed by `variable` and `variable @ pattern`

error: expected identifier, found keyword `Self`
--> $DIR/self_type_keyword.rs:19:17
Expand Down

0 comments on commit 42e895d

Please sign in to comment.