From 5cc1559c600f34f534fa3e0328ca1c2659562229 Mon Sep 17 00:00:00 2001
From: Mazdak Farrokhzad <twingoow@gmail.com>
Date: Tue, 27 Aug 2019 10:14:07 +0200
Subject: [PATCH 1/5] token: refactor with is_non_raw_ident_where.

---
 src/libsyntax/parse/token.rs | 32 +++++++++++++-------------------
 1 file changed, 13 insertions(+), 19 deletions(-)

diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index 1865f925165bd..dfea34c331ad4 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -409,7 +409,7 @@ impl Token {
     crate fn expect_lit(&self) -> Lit {
         match self.kind {
             Literal(lit) => lit,
-            _=> panic!("`expect_lit` called on non-literal"),
+            _ => panic!("`expect_lit` called on non-literal"),
         }
     }
 
@@ -457,6 +457,7 @@ impl Token {
     pub fn is_ident(&self) -> bool {
         self.ident().is_some()
     }
+
     /// Returns `true` if the token is a lifetime.
     crate fn is_lifetime(&self) -> bool {
         self.lifetime().is_some()
@@ -508,45 +509,38 @@ impl Token {
 
     /// Returns `true` if the token is a given keyword, `kw`.
     pub fn is_keyword(&self, kw: Symbol) -> bool {
-        self.ident().map(|(id, is_raw)| id.name == kw && !is_raw).unwrap_or(false)
+        self.is_non_raw_ident_where(|id| id.name == kw)
     }
 
     crate fn is_path_segment_keyword(&self) -> bool {
-        match self.ident() {
-            Some((id, false)) => id.is_path_segment_keyword(),
-            _ => false,
-        }
+        self.is_non_raw_ident_where(ast::Ident::is_path_segment_keyword)
     }
 
     // Returns true for reserved identifiers used internally for elided lifetimes,
     // unnamed method parameters, crate root module, error recovery etc.
     crate fn is_special_ident(&self) -> bool {
-        match self.ident() {
-            Some((id, false)) => id.is_special(),
-            _ => false,
-        }
+        self.is_non_raw_ident_where(ast::Ident::is_special)
     }
 
     /// Returns `true` if the token is a keyword used in the language.
     crate fn is_used_keyword(&self) -> bool {
-        match self.ident() {
-            Some((id, false)) => id.is_used_keyword(),
-            _ => false,
-        }
+        self.is_non_raw_ident_where(ast::Ident::is_used_keyword)
     }
 
     /// Returns `true` if the token is a keyword reserved for possible future use.
     crate fn is_unused_keyword(&self) -> bool {
-        match self.ident() {
-            Some((id, false)) => id.is_unused_keyword(),
-            _ => false,
-        }
+        self.is_non_raw_ident_where(ast::Ident::is_unused_keyword)
     }
 
     /// Returns `true` if the token is either a special identifier or a keyword.
     pub fn is_reserved_ident(&self) -> bool {
+        self.is_non_raw_ident_where(ast::Ident::is_reserved)
+    }
+
+    /// Returns `true` if the token is a non-raw identifier for which `pred` holds.
+    fn is_non_raw_ident_where(&self, pred: impl FnOnce(ast::Ident) -> bool) -> bool {
         match self.ident() {
-            Some((id, false)) => id.is_reserved(),
+            Some((id, false)) => pred(id),
             _ => false,
         }
     }

From e49b9581baba9d89519d17ac0d8400b6ae77e754 Mon Sep 17 00:00:00 2001
From: Mazdak Farrokhzad <twingoow@gmail.com>
Date: Tue, 27 Aug 2019 10:21:41 +0200
Subject: [PATCH 2/5] Simplify with Symbol/Token::is_book_lit.

---
 src/libsyntax/parse/literal.rs     |  4 ++--
 src/libsyntax/parse/parser/path.rs |  2 +-
 src/libsyntax/parse/token.rs       | 11 +++++++----
 src/libsyntax_pos/symbol.rs        |  5 +++++
 4 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/src/libsyntax/parse/literal.rs b/src/libsyntax/parse/literal.rs
index 6409acba573ad..36233de3cfb57 100644
--- a/src/libsyntax/parse/literal.rs
+++ b/src/libsyntax/parse/literal.rs
@@ -104,7 +104,7 @@ impl LitKind {
 
         Ok(match kind {
             token::Bool => {
-                assert!(symbol == kw::True || symbol == kw::False);
+                assert!(symbol.is_bool_lit());
                 LitKind::Bool(symbol == kw::True)
             }
             token::Byte => return unescape_byte(&symbol.as_str())
@@ -261,7 +261,7 @@ impl Lit {
     /// Converts arbitrary token into an AST literal.
     crate fn from_token(token: &Token) -> Result<Lit, LitError> {
         let lit = match token.kind {
-            token::Ident(name, false) if name == kw::True || name == kw::False =>
+            token::Ident(name, false) if name.is_bool_lit() =>
                 token::Lit::new(token::Bool, name, None),
             token::Literal(lit) =>
                 lit,
diff --git a/src/libsyntax/parse/parser/path.rs b/src/libsyntax/parse/parser/path.rs
index 3eb4d45045a9e..d4b13cc2e0121 100644
--- a/src/libsyntax/parse/parser/path.rs
+++ b/src/libsyntax/parse/parser/path.rs
@@ -423,7 +423,7 @@ impl<'a> Parser<'a> {
                     // FIXME(const_generics): to distinguish between idents for types and consts,
                     // we should introduce a GenericArg::Ident in the AST and distinguish when
                     // lowering to the HIR. For now, idents for const args are not permitted.
-                    if self.token.is_keyword(kw::True) || self.token.is_keyword(kw::False) {
+                    if self.token.is_bool_lit() {
                         self.parse_literal_maybe_minus()?
                     } else {
                         return Err(
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index dfea34c331ad4..fe3b51aa246b8 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -417,10 +417,8 @@ impl Token {
     /// for example a '-42', or one of the boolean idents).
     crate fn can_begin_literal_or_bool(&self) -> bool {
         match self.kind {
-            Literal(..)  => true,
-            BinOp(Minus) => true,
-            Ident(name, false) if name == kw::True => true,
-            Ident(name, false) if name == kw::False => true,
+            Literal(..) | BinOp(Minus) => true,
+            Ident(name, false) if name.is_bool_lit() => true,
             Interpolated(ref nt) => match **nt {
                 NtLiteral(..) => true,
                 _             => false,
@@ -537,6 +535,11 @@ impl Token {
         self.is_non_raw_ident_where(ast::Ident::is_reserved)
     }
 
+    /// Returns `true` if the token is the identifier `true` or `false`.
+    crate fn is_bool_lit(&self) -> bool {
+        self.is_non_raw_ident_where(|id| id.name.is_bool_lit())
+    }
+
     /// Returns `true` if the token is a non-raw identifier for which `pred` holds.
     fn is_non_raw_ident_where(&self, pred: impl FnOnce(ast::Ident) -> bool) -> bool {
         match self.ident() {
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index 0b8f16bbc3b99..856857f74e352 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -1063,6 +1063,11 @@ impl Symbol {
         self == kw::DollarCrate
     }
 
+    /// Returns `true` if the symbol is `true` or `false`.
+    pub fn is_bool_lit(self) -> bool {
+        self == kw::True || self == kw::False
+    }
+
     /// This symbol can be a raw identifier.
     pub fn can_be_raw(self) -> bool {
         self != kw::Invalid && self != kw::Underscore && !self.is_path_segment_keyword()

From f908aa9e8000dd7fd2c3de54fe1d914fddf4fe92 Mon Sep 17 00:00:00 2001
From: Mazdak Farrokhzad <twingoow@gmail.com>
Date: Tue, 27 Aug 2019 13:04:48 +0200
Subject: [PATCH 3/5] recover on 'mut ' and improve recovery for keywords.

---
 src/libsyntax/parse/parser/pat.rs             | 136 ++++++++++++++----
 .../keyword-extern-as-identifier-pat.rs       |   2 +-
 .../keyword-extern-as-identifier-pat.stderr   |   8 +-
 src/test/ui/parser/issue-32501.rs             |   3 +-
 src/test/ui/parser/issue-32501.stderr         |   6 +-
 src/test/ui/parser/keyword-abstract.rs        |   2 +-
 src/test/ui/parser/keyword-abstract.stderr    |   8 +-
 .../ui/parser/keyword-as-as-identifier.rs     |   2 +-
 .../ui/parser/keyword-as-as-identifier.stderr |   8 +-
 .../ui/parser/keyword-break-as-identifier.rs  |   2 +-
 .../parser/keyword-break-as-identifier.stderr |   8 +-
 .../ui/parser/keyword-const-as-identifier.rs  |   2 +-
 .../parser/keyword-const-as-identifier.stderr |   8 +-
 .../parser/keyword-continue-as-identifier.rs  |   2 +-
 .../keyword-continue-as-identifier.stderr     |   8 +-
 .../ui/parser/keyword-else-as-identifier.rs   |   2 +-
 .../parser/keyword-else-as-identifier.stderr  |   8 +-
 .../ui/parser/keyword-enum-as-identifier.rs   |   2 +-
 .../parser/keyword-enum-as-identifier.stderr  |   8 +-
 src/test/ui/parser/keyword-final.rs           |   2 +-
 src/test/ui/parser/keyword-final.stderr       |   8 +-
 .../ui/parser/keyword-fn-as-identifier.rs     |   2 +-
 .../ui/parser/keyword-fn-as-identifier.stderr |   8 +-
 .../ui/parser/keyword-for-as-identifier.rs    |   2 +-
 .../parser/keyword-for-as-identifier.stderr   |   8 +-
 .../ui/parser/keyword-if-as-identifier.rs     |   2 +-
 .../ui/parser/keyword-if-as-identifier.stderr |   8 +-
 .../ui/parser/keyword-impl-as-identifier.rs   |   2 +-
 .../parser/keyword-impl-as-identifier.stderr  |   8 +-
 .../ui/parser/keyword-let-as-identifier.rs    |   2 +-
 .../parser/keyword-let-as-identifier.stderr   |   8 +-
 .../ui/parser/keyword-loop-as-identifier.rs   |   2 +-
 .../parser/keyword-loop-as-identifier.stderr  |   8 +-
 .../ui/parser/keyword-match-as-identifier.rs  |   2 +-
 .../parser/keyword-match-as-identifier.stderr |   8 +-
 .../ui/parser/keyword-mod-as-identifier.rs    |   2 +-
 .../parser/keyword-mod-as-identifier.stderr   |   8 +-
 .../ui/parser/keyword-move-as-identifier.rs   |   2 +-
 .../parser/keyword-move-as-identifier.stderr  |   8 +-
 src/test/ui/parser/keyword-override.rs        |   2 +-
 src/test/ui/parser/keyword-override.stderr    |   8 +-
 .../ui/parser/keyword-pub-as-identifier.rs    |   2 +-
 .../parser/keyword-pub-as-identifier.stderr   |   8 +-
 .../ui/parser/keyword-return-as-identifier.rs |   2 +-
 .../keyword-return-as-identifier.stderr       |   8 +-
 .../ui/parser/keyword-static-as-identifier.rs |   2 +-
 .../keyword-static-as-identifier.stderr       |   8 +-
 .../ui/parser/keyword-struct-as-identifier.rs |   2 +-
 .../keyword-struct-as-identifier.stderr       |   8 +-
 .../ui/parser/keyword-trait-as-identifier.rs  |   2 +-
 .../parser/keyword-trait-as-identifier.stderr |   8 +-
 .../keyword-try-as-identifier-edition2018.rs  |   2 +-
 ...yword-try-as-identifier-edition2018.stderr |   8 +-
 .../ui/parser/keyword-type-as-identifier.rs   |   2 +-
 .../parser/keyword-type-as-identifier.stderr  |   8 +-
 src/test/ui/parser/keyword-typeof.rs          |   2 +-
 src/test/ui/parser/keyword-typeof.stderr      |   8 +-
 .../ui/parser/keyword-unsafe-as-identifier.rs |   2 +-
 .../keyword-unsafe-as-identifier.stderr       |   8 +-
 .../ui/parser/keyword-use-as-identifier.rs    |   2 +-
 .../parser/keyword-use-as-identifier.stderr   |   8 +-
 .../ui/parser/keyword-where-as-identifier.rs  |   2 +-
 .../parser/keyword-where-as-identifier.stderr |   8 +-
 .../ui/parser/keyword-while-as-identifier.rs  |   2 +-
 .../parser/keyword-while-as-identifier.stderr |   8 +-
 src/test/ui/parser/mut-patterns.rs            |  30 +++-
 src/test/ui/parser/mut-patterns.stderr        |  68 ++++++++-
 src/test/ui/reserved/reserved-become.rs       |   2 +-
 src/test/ui/reserved/reserved-become.stderr   |   8 +-
 src/test/ui/self/self_type_keyword.rs         |   3 +-
 src/test/ui/self/self_type_keyword.stderr     |  30 ++--
 71 files changed, 449 insertions(+), 147 deletions(-)

diff --git a/src/libsyntax/parse/parser/pat.rs b/src/libsyntax/parse/parser/pat.rs
index 78c9a289b3702..7b228a700a748 100644
--- a/src/libsyntax/parse/parser/pat.rs
+++ b/src/libsyntax/parse/parser/pat.rs
@@ -4,6 +4,7 @@ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
 use crate::ptr::P;
 use crate::ast::{self, Attribute, Pat, PatKind, FieldPat, RangeEnd, RangeSyntax, Mac};
 use crate::ast::{BindingMode, Ident, Mutability, Path, QSelf, Expr, ExprKind};
+use crate::mut_visit::{noop_visit_pat, MutVisitor};
 use crate::parse::token::{self};
 use crate::print::pprust;
 use crate::source_map::{respan, Span, Spanned};
@@ -273,7 +274,7 @@ impl<'a> Parser<'a> {
                 // Parse _
                 PatKind::Wild
             } else if self.eat_keyword(kw::Mut) {
-                self.recover_pat_ident_mut_first()?
+                self.parse_pat_ident_mut()?
             } else if self.eat_keyword(kw::Ref) {
                 // Parse ref ident @ pat / ref mut ident @ pat
                 let mutbl = self.parse_mutability();
@@ -281,13 +282,12 @@ impl<'a> Parser<'a> {
             } else if self.eat_keyword(kw::Box) {
                 // Parse `box pat`
                 PatKind::Box(self.parse_pat_with_range_pat(false, None)?)
-            } else if self.token.is_ident() && !self.token.is_reserved_ident() &&
-                      self.parse_as_ident() {
+            } else if self.can_be_ident_pat() {
                 // Parse `ident @ pat`
                 // This can give false positives and parse nullary enums,
                 // they are dealt with later in resolve.
                 self.parse_pat_ident(BindingMode::ByValue(Mutability::Immutable))?
-            } else if self.token.is_path_start() {
+            } else if self.is_start_of_pat_with_path() {
                 // Parse pattern starting with a path
                 let (qself, path) = if self.eat_lt() {
                     // Parse a qualified path
@@ -384,24 +384,85 @@ impl<'a> Parser<'a> {
         })
     }
 
+    fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> {
+        let mut_span = self.prev_span;
+
+        if self.eat_keyword(kw::Ref) {
+            return self.recover_mut_ref_ident(mut_span)
+        }
+
+        self.recover_additional_muts();
+
+        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);
+
+        // 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),
+        }
+
+        Ok(pat.node)
+    }
+
     /// Recover on `mut ref? ident @ pat` and suggest
     /// that the order of `mut` and `ref` is incorrect.
-    fn recover_pat_ident_mut_first(&mut self) -> PResult<'a, PatKind> {
-        let mutref_span = self.prev_span.to(self.token.span);
-        let binding_mode = if self.eat_keyword(kw::Ref) {
-            self.struct_span_err(mutref_span, "the order of `mut` and `ref` is incorrect")
-                .span_suggestion(
-                    mutref_span,
-                    "try switching the order",
-                    "ref mut".into(),
-                    Applicability::MachineApplicable
-                )
-                .emit();
-            BindingMode::ByRef(Mutability::Mutable)
-        } else {
-            BindingMode::ByValue(Mutability::Mutable)
-        };
-        self.parse_pat_ident(binding_mode)
+    fn recover_mut_ref_ident(&mut self, lo: Span) -> PResult<'a, PatKind> {
+        let mutref_span = lo.to(self.prev_span);
+        self.struct_span_err(mutref_span, "the order of `mut` and `ref` is incorrect")
+            .span_suggestion(
+                mutref_span,
+                "try switching the order",
+                "ref mut".into(),
+                Applicability::MachineApplicable
+            )
+            .emit();
+
+        self.parse_pat_ident(BindingMode::ByRef(Mutability::Mutable))
+    }
+
+    /// Error on `mut $pat` where `$pat` is not an ident.
+    fn ban_mut_general_pat(&self, lo: Span, pat: &Pat) {
+        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();
+    }
+
+    /// Eat any extraneous `mut`s and error + recover if we ate any.
+    fn recover_additional_muts(&mut self) {
+        let lo = self.token.span;
+        while self.eat_keyword(kw::Mut) {}
+        if lo == self.token.span {
+            return;
+        }
+
+        let span = lo.to(self.prev_span);
+        self.struct_span_err(span, "`mut` on a binding may not be repeated")
+            .span_suggestion(
+                span,
+                "remove the additional `mut`s",
+                String::new(),
+                Applicability::MachineApplicable,
+            )
+            .emit();
     }
 
     /// Parse macro invocation
@@ -479,17 +540,6 @@ impl<'a> Parser<'a> {
         Err(err)
     }
 
-    // Helper function to decide whether to parse as ident binding
-    // or to try to do something more complex like range patterns.
-    fn parse_as_ident(&mut self) -> bool {
-        self.look_ahead(1, |t| match t.kind {
-            token::OpenDelim(token::Paren) | token::OpenDelim(token::Brace) |
-            token::DotDotDot | token::DotDotEq | token::DotDot |
-            token::ModSep | token::Not => false,
-            _ => true,
-        })
-    }
-
     /// Is the current token suitable as the start of a range patterns end?
     fn is_pat_range_end_start(&self) -> bool {
         self.token.is_path_start() // e.g. `MY_CONST`;
@@ -563,6 +613,30 @@ impl<'a> Parser<'a> {
         }
     }
 
+    /// Is this the start of a pattern beginning with a path?
+    fn is_start_of_pat_with_path(&mut self) -> bool {
+        self.check_path()
+        // Just for recovery (see `can_be_ident`).
+        || self.token.is_ident() && !self.token.is_bool_lit() && !self.token.is_keyword(kw::In)
+    }
+
+    /// Would `parse_pat_ident` be appropriate here?
+    fn can_be_ident_pat(&mut self) -> bool {
+        self.check_ident()
+        && !self.token.is_bool_lit() // Avoid `true` or `false` as a binding as it is a literal.
+        && !self.token.is_path_segment_keyword() // Avoid e.g. `Self` as it is a path.
+        // Avoid `in`. Due to recovery in the list parser this messes with `for ( $pat in $expr )`.
+        && !self.token.is_keyword(kw::In)
+        && self.look_ahead(1, |t| match t.kind { // Try to do something more complex?
+            token::OpenDelim(token::Paren) // A tuple struct pattern.
+            | token::OpenDelim(token::Brace) // A struct pattern.
+            | token::DotDotDot | token::DotDotEq | token::DotDot // A range pattern.
+            | token::ModSep // A tuple / struct variant pattern.
+            | token::Not => false, // A macro expanding to a pattern.
+            _ => true,
+        })
+    }
+
     /// Parses `ident` or `ident @ pat`.
     /// Used by the copy foo and ref foo patterns to give a good
     /// error message when parsing mistakes like `ref foo(a, b)`.
diff --git a/src/test/ui/keyword/extern/keyword-extern-as-identifier-pat.rs b/src/test/ui/keyword/extern/keyword-extern-as-identifier-pat.rs
index f9b6bad7c2552..8a420f7203cac 100644
--- a/src/test/ui/keyword/extern/keyword-extern-as-identifier-pat.rs
+++ b/src/test/ui/keyword/extern/keyword-extern-as-identifier-pat.rs
@@ -1,3 +1,3 @@
 fn main() {
-    let extern = 0; //~ ERROR expected pattern, found keyword `extern`
+    let extern = 0; //~ ERROR expected identifier, found keyword `extern`
 }
diff --git a/src/test/ui/keyword/extern/keyword-extern-as-identifier-pat.stderr b/src/test/ui/keyword/extern/keyword-extern-as-identifier-pat.stderr
index d7b9ad2abe97a..73ac113f1b1e0 100644
--- a/src/test/ui/keyword/extern/keyword-extern-as-identifier-pat.stderr
+++ b/src/test/ui/keyword/extern/keyword-extern-as-identifier-pat.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `extern`
+error: expected identifier, found keyword `extern`
   --> $DIR/keyword-extern-as-identifier-pat.rs:2:9
    |
 LL |     let extern = 0;
-   |         ^^^^^^ expected pattern
+   |         ^^^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#extern = 0;
+   |         ^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/issue-32501.rs b/src/test/ui/parser/issue-32501.rs
index 9c01a5c6d20e3..695baf8187276 100644
--- a/src/test/ui/parser/issue-32501.rs
+++ b/src/test/ui/parser/issue-32501.rs
@@ -4,5 +4,6 @@ fn main() {
     let _ = 0;
     let mut b = 0;
     let mut _b = 0;
-    let mut _ = 0; //~ ERROR expected identifier, found reserved identifier `_`
+    let mut _ = 0;
+    //~^ ERROR `mut` must be attached to each individual binding
 }
diff --git a/src/test/ui/parser/issue-32501.stderr b/src/test/ui/parser/issue-32501.stderr
index 97efb89593579..f5d3300cf9c56 100644
--- a/src/test/ui/parser/issue-32501.stderr
+++ b/src/test/ui/parser/issue-32501.stderr
@@ -1,8 +1,8 @@
-error: expected identifier, found reserved identifier `_`
-  --> $DIR/issue-32501.rs:7:13
+error: `mut` must be attached to each individual binding
+  --> $DIR/issue-32501.rs:7:9
    |
 LL |     let mut _ = 0;
-   |             ^ expected identifier, found reserved identifier
+   |         ^^^^^ help: add `mut` to each binding: `_`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-abstract.rs b/src/test/ui/parser/keyword-abstract.rs
index 890802ac134a0..570206575ab07 100644
--- a/src/test/ui/parser/keyword-abstract.rs
+++ b/src/test/ui/parser/keyword-abstract.rs
@@ -1,3 +1,3 @@
 fn main() {
-    let abstract = (); //~ ERROR expected pattern, found reserved keyword `abstract`
+    let abstract = (); //~ ERROR expected identifier, found reserved keyword `abstract`
 }
diff --git a/src/test/ui/parser/keyword-abstract.stderr b/src/test/ui/parser/keyword-abstract.stderr
index 2c79598a81b18..eb2c810099e16 100644
--- a/src/test/ui/parser/keyword-abstract.stderr
+++ b/src/test/ui/parser/keyword-abstract.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found reserved keyword `abstract`
+error: expected identifier, found reserved keyword `abstract`
   --> $DIR/keyword-abstract.rs:2:9
    |
 LL |     let abstract = ();
-   |         ^^^^^^^^ expected pattern
+   |         ^^^^^^^^ expected identifier, found reserved keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#abstract = ();
+   |         ^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-as-as-identifier.rs b/src/test/ui/parser/keyword-as-as-identifier.rs
index 23ff259db3048..cd47c8a3907d9 100644
--- a/src/test/ui/parser/keyword-as-as-identifier.rs
+++ b/src/test/ui/parser/keyword-as-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py as'
 
 fn main() {
-    let as = "foo"; //~ error: expected pattern, found keyword `as`
+    let as = "foo"; //~ error: expected identifier, found keyword `as`
 }
diff --git a/src/test/ui/parser/keyword-as-as-identifier.stderr b/src/test/ui/parser/keyword-as-as-identifier.stderr
index ef466488ad061..5648652be9bca 100644
--- a/src/test/ui/parser/keyword-as-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-as-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `as`
+error: expected identifier, found keyword `as`
   --> $DIR/keyword-as-as-identifier.rs:4:9
    |
 LL |     let as = "foo";
-   |         ^^ expected pattern
+   |         ^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#as = "foo";
+   |         ^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-break-as-identifier.rs b/src/test/ui/parser/keyword-break-as-identifier.rs
index 5ee111d38c9c3..04b25a7aaf613 100644
--- a/src/test/ui/parser/keyword-break-as-identifier.rs
+++ b/src/test/ui/parser/keyword-break-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py break'
 
 fn main() {
-    let break = "foo"; //~ error: expected pattern, found keyword `break`
+    let break = "foo"; //~ error: expected identifier, found keyword `break`
 }
diff --git a/src/test/ui/parser/keyword-break-as-identifier.stderr b/src/test/ui/parser/keyword-break-as-identifier.stderr
index 690bd84221a94..820193db70b0f 100644
--- a/src/test/ui/parser/keyword-break-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-break-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `break`
+error: expected identifier, found keyword `break`
   --> $DIR/keyword-break-as-identifier.rs:4:9
    |
 LL |     let break = "foo";
-   |         ^^^^^ expected pattern
+   |         ^^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#break = "foo";
+   |         ^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-const-as-identifier.rs b/src/test/ui/parser/keyword-const-as-identifier.rs
index 48fc142cf64b1..6a2d926bf5796 100644
--- a/src/test/ui/parser/keyword-const-as-identifier.rs
+++ b/src/test/ui/parser/keyword-const-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py const'
 
 fn main() {
-    let const = "foo"; //~ error: expected pattern, found keyword `const`
+    let const = "foo"; //~ error: expected identifier, found keyword `const`
 }
diff --git a/src/test/ui/parser/keyword-const-as-identifier.stderr b/src/test/ui/parser/keyword-const-as-identifier.stderr
index 6da47f88d04e3..95b536c99c75a 100644
--- a/src/test/ui/parser/keyword-const-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-const-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `const`
+error: expected identifier, found keyword `const`
   --> $DIR/keyword-const-as-identifier.rs:4:9
    |
 LL |     let const = "foo";
-   |         ^^^^^ expected pattern
+   |         ^^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#const = "foo";
+   |         ^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-continue-as-identifier.rs b/src/test/ui/parser/keyword-continue-as-identifier.rs
index 06315a48349ff..cfdd62a2d1bce 100644
--- a/src/test/ui/parser/keyword-continue-as-identifier.rs
+++ b/src/test/ui/parser/keyword-continue-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py continue'
 
 fn main() {
-    let continue = "foo"; //~ error: expected pattern, found keyword `continue`
+    let continue = "foo"; //~ error: expected identifier, found keyword `continue`
 }
diff --git a/src/test/ui/parser/keyword-continue-as-identifier.stderr b/src/test/ui/parser/keyword-continue-as-identifier.stderr
index 4b0a659f9ad7e..6b24422a5557e 100644
--- a/src/test/ui/parser/keyword-continue-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-continue-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `continue`
+error: expected identifier, found keyword `continue`
   --> $DIR/keyword-continue-as-identifier.rs:4:9
    |
 LL |     let continue = "foo";
-   |         ^^^^^^^^ expected pattern
+   |         ^^^^^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#continue = "foo";
+   |         ^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-else-as-identifier.rs b/src/test/ui/parser/keyword-else-as-identifier.rs
index 0c69105cf9457..f12dac3ff75eb 100644
--- a/src/test/ui/parser/keyword-else-as-identifier.rs
+++ b/src/test/ui/parser/keyword-else-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py else'
 
 fn main() {
-    let else = "foo"; //~ error: expected pattern, found keyword `else`
+    let else = "foo"; //~ error: expected identifier, found keyword `else`
 }
diff --git a/src/test/ui/parser/keyword-else-as-identifier.stderr b/src/test/ui/parser/keyword-else-as-identifier.stderr
index bec7b7ba01e12..f28635cd08cd6 100644
--- a/src/test/ui/parser/keyword-else-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-else-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `else`
+error: expected identifier, found keyword `else`
   --> $DIR/keyword-else-as-identifier.rs:4:9
    |
 LL |     let else = "foo";
-   |         ^^^^ expected pattern
+   |         ^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#else = "foo";
+   |         ^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-enum-as-identifier.rs b/src/test/ui/parser/keyword-enum-as-identifier.rs
index d1675800a2791..fe66230d02830 100644
--- a/src/test/ui/parser/keyword-enum-as-identifier.rs
+++ b/src/test/ui/parser/keyword-enum-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py enum'
 
 fn main() {
-    let enum = "foo"; //~ error: expected pattern, found keyword `enum`
+    let enum = "foo"; //~ error: expected identifier, found keyword `enum`
 }
diff --git a/src/test/ui/parser/keyword-enum-as-identifier.stderr b/src/test/ui/parser/keyword-enum-as-identifier.stderr
index 51a834f797c32..fc54dce1b68f4 100644
--- a/src/test/ui/parser/keyword-enum-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-enum-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `enum`
+error: expected identifier, found keyword `enum`
   --> $DIR/keyword-enum-as-identifier.rs:4:9
    |
 LL |     let enum = "foo";
-   |         ^^^^ expected pattern
+   |         ^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#enum = "foo";
+   |         ^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-final.rs b/src/test/ui/parser/keyword-final.rs
index e1cecd0e8e07a..a79a11032a018 100644
--- a/src/test/ui/parser/keyword-final.rs
+++ b/src/test/ui/parser/keyword-final.rs
@@ -1,3 +1,3 @@
 fn main() {
-    let final = (); //~ ERROR expected pattern, found reserved keyword `final`
+    let final = (); //~ ERROR expected identifier, found reserved keyword `final`
 }
diff --git a/src/test/ui/parser/keyword-final.stderr b/src/test/ui/parser/keyword-final.stderr
index e8372643be6b7..291710d05cbfd 100644
--- a/src/test/ui/parser/keyword-final.stderr
+++ b/src/test/ui/parser/keyword-final.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found reserved keyword `final`
+error: expected identifier, found reserved keyword `final`
   --> $DIR/keyword-final.rs:2:9
    |
 LL |     let final = ();
-   |         ^^^^^ expected pattern
+   |         ^^^^^ expected identifier, found reserved keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#final = ();
+   |         ^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-fn-as-identifier.rs b/src/test/ui/parser/keyword-fn-as-identifier.rs
index bca2d5996a54b..f30e115f7947e 100644
--- a/src/test/ui/parser/keyword-fn-as-identifier.rs
+++ b/src/test/ui/parser/keyword-fn-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py fn'
 
 fn main() {
-    let fn = "foo"; //~ error: expected pattern, found keyword `fn`
+    let fn = "foo"; //~ error: expected identifier, found keyword `fn`
 }
diff --git a/src/test/ui/parser/keyword-fn-as-identifier.stderr b/src/test/ui/parser/keyword-fn-as-identifier.stderr
index a071a40a70e0d..692f195b2888d 100644
--- a/src/test/ui/parser/keyword-fn-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-fn-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `fn`
+error: expected identifier, found keyword `fn`
   --> $DIR/keyword-fn-as-identifier.rs:4:9
    |
 LL |     let fn = "foo";
-   |         ^^ expected pattern
+   |         ^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#fn = "foo";
+   |         ^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-for-as-identifier.rs b/src/test/ui/parser/keyword-for-as-identifier.rs
index ce49fd90d9101..9e8a2ad53420c 100644
--- a/src/test/ui/parser/keyword-for-as-identifier.rs
+++ b/src/test/ui/parser/keyword-for-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py for'
 
 fn main() {
-    let for = "foo"; //~ error: expected pattern, found keyword `for`
+    let for = "foo"; //~ error: expected identifier, found keyword `for`
 }
diff --git a/src/test/ui/parser/keyword-for-as-identifier.stderr b/src/test/ui/parser/keyword-for-as-identifier.stderr
index 090046cebdc56..bcaf421286e76 100644
--- a/src/test/ui/parser/keyword-for-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-for-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `for`
+error: expected identifier, found keyword `for`
   --> $DIR/keyword-for-as-identifier.rs:4:9
    |
 LL |     let for = "foo";
-   |         ^^^ expected pattern
+   |         ^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#for = "foo";
+   |         ^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-if-as-identifier.rs b/src/test/ui/parser/keyword-if-as-identifier.rs
index a1302970689c4..0bd5756afce7f 100644
--- a/src/test/ui/parser/keyword-if-as-identifier.rs
+++ b/src/test/ui/parser/keyword-if-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py if'
 
 fn main() {
-    let if = "foo"; //~ error: expected pattern, found keyword `if`
+    let if = "foo"; //~ error: expected identifier, found keyword `if`
 }
diff --git a/src/test/ui/parser/keyword-if-as-identifier.stderr b/src/test/ui/parser/keyword-if-as-identifier.stderr
index 98bfdb46e9770..43fbcd7148a1d 100644
--- a/src/test/ui/parser/keyword-if-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-if-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `if`
+error: expected identifier, found keyword `if`
   --> $DIR/keyword-if-as-identifier.rs:4:9
    |
 LL |     let if = "foo";
-   |         ^^ expected pattern
+   |         ^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#if = "foo";
+   |         ^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-impl-as-identifier.rs b/src/test/ui/parser/keyword-impl-as-identifier.rs
index 95a34483ad21a..df529bae07214 100644
--- a/src/test/ui/parser/keyword-impl-as-identifier.rs
+++ b/src/test/ui/parser/keyword-impl-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py impl'
 
 fn main() {
-    let impl = "foo"; //~ error: expected pattern, found keyword `impl`
+    let impl = "foo"; //~ error: expected identifier, found keyword `impl`
 }
diff --git a/src/test/ui/parser/keyword-impl-as-identifier.stderr b/src/test/ui/parser/keyword-impl-as-identifier.stderr
index 2672959b7c68e..01886eb45cb6d 100644
--- a/src/test/ui/parser/keyword-impl-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-impl-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `impl`
+error: expected identifier, found keyword `impl`
   --> $DIR/keyword-impl-as-identifier.rs:4:9
    |
 LL |     let impl = "foo";
-   |         ^^^^ expected pattern
+   |         ^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#impl = "foo";
+   |         ^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-let-as-identifier.rs b/src/test/ui/parser/keyword-let-as-identifier.rs
index 07c0ddf8ce573..9b1183501b28d 100644
--- a/src/test/ui/parser/keyword-let-as-identifier.rs
+++ b/src/test/ui/parser/keyword-let-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py let'
 
 fn main() {
-    let let = "foo"; //~ error: expected pattern, found keyword `let`
+    let let = "foo"; //~ error: expected identifier, found keyword `let`
 }
diff --git a/src/test/ui/parser/keyword-let-as-identifier.stderr b/src/test/ui/parser/keyword-let-as-identifier.stderr
index 99dbc0530f3fe..f6c39077be23b 100644
--- a/src/test/ui/parser/keyword-let-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-let-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `let`
+error: expected identifier, found keyword `let`
   --> $DIR/keyword-let-as-identifier.rs:4:9
    |
 LL |     let let = "foo";
-   |         ^^^ expected pattern
+   |         ^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#let = "foo";
+   |         ^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-loop-as-identifier.rs b/src/test/ui/parser/keyword-loop-as-identifier.rs
index 8643ffe434505..46914a19be2bd 100644
--- a/src/test/ui/parser/keyword-loop-as-identifier.rs
+++ b/src/test/ui/parser/keyword-loop-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py loop'
 
 fn main() {
-    let loop = "foo"; //~ error: expected pattern, found keyword `loop`
+    let loop = "foo"; //~ error: expected identifier, found keyword `loop`
 }
diff --git a/src/test/ui/parser/keyword-loop-as-identifier.stderr b/src/test/ui/parser/keyword-loop-as-identifier.stderr
index 783507eb35cd7..f0c282faa29f1 100644
--- a/src/test/ui/parser/keyword-loop-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-loop-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `loop`
+error: expected identifier, found keyword `loop`
   --> $DIR/keyword-loop-as-identifier.rs:4:9
    |
 LL |     let loop = "foo";
-   |         ^^^^ expected pattern
+   |         ^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#loop = "foo";
+   |         ^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-match-as-identifier.rs b/src/test/ui/parser/keyword-match-as-identifier.rs
index 8ef6b6810a56b..d3cecb991b8f9 100644
--- a/src/test/ui/parser/keyword-match-as-identifier.rs
+++ b/src/test/ui/parser/keyword-match-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py match'
 
 fn main() {
-    let match = "foo"; //~ error: expected pattern, found keyword `match`
+    let match = "foo"; //~ error: expected identifier, found keyword `match`
 }
diff --git a/src/test/ui/parser/keyword-match-as-identifier.stderr b/src/test/ui/parser/keyword-match-as-identifier.stderr
index e56a115c91636..f1f4397d194f0 100644
--- a/src/test/ui/parser/keyword-match-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-match-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `match`
+error: expected identifier, found keyword `match`
   --> $DIR/keyword-match-as-identifier.rs:4:9
    |
 LL |     let match = "foo";
-   |         ^^^^^ expected pattern
+   |         ^^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#match = "foo";
+   |         ^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-mod-as-identifier.rs b/src/test/ui/parser/keyword-mod-as-identifier.rs
index 96bcdccf0a096..b9c7b6c78ed6c 100644
--- a/src/test/ui/parser/keyword-mod-as-identifier.rs
+++ b/src/test/ui/parser/keyword-mod-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py mod'
 
 fn main() {
-    let mod = "foo"; //~ error: expected pattern, found keyword `mod`
+    let mod = "foo"; //~ error: expected identifier, found keyword `mod`
 }
diff --git a/src/test/ui/parser/keyword-mod-as-identifier.stderr b/src/test/ui/parser/keyword-mod-as-identifier.stderr
index a8be2ceb037d6..65ae3baa8c21d 100644
--- a/src/test/ui/parser/keyword-mod-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-mod-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `mod`
+error: expected identifier, found keyword `mod`
   --> $DIR/keyword-mod-as-identifier.rs:4:9
    |
 LL |     let mod = "foo";
-   |         ^^^ expected pattern
+   |         ^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#mod = "foo";
+   |         ^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-move-as-identifier.rs b/src/test/ui/parser/keyword-move-as-identifier.rs
index 2193af530bd7a..65be02e3c70cf 100644
--- a/src/test/ui/parser/keyword-move-as-identifier.rs
+++ b/src/test/ui/parser/keyword-move-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py move'
 
 fn main() {
-    let move = "foo"; //~ error: expected pattern, found keyword `move`
+    let move = "foo"; //~ error: expected identifier, found keyword `move`
 }
diff --git a/src/test/ui/parser/keyword-move-as-identifier.stderr b/src/test/ui/parser/keyword-move-as-identifier.stderr
index e0687e27eb585..216f7c931eea7 100644
--- a/src/test/ui/parser/keyword-move-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-move-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `move`
+error: expected identifier, found keyword `move`
   --> $DIR/keyword-move-as-identifier.rs:4:9
    |
 LL |     let move = "foo";
-   |         ^^^^ expected pattern
+   |         ^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#move = "foo";
+   |         ^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-override.rs b/src/test/ui/parser/keyword-override.rs
index 948a20095f1ea..009bebd7ddba8 100644
--- a/src/test/ui/parser/keyword-override.rs
+++ b/src/test/ui/parser/keyword-override.rs
@@ -1,3 +1,3 @@
 fn main() {
-    let override = (); //~ ERROR expected pattern, found reserved keyword `override`
+    let override = (); //~ ERROR expected identifier, found reserved keyword `override`
 }
diff --git a/src/test/ui/parser/keyword-override.stderr b/src/test/ui/parser/keyword-override.stderr
index 1bfc6c9b3858d..3183fa510c2d1 100644
--- a/src/test/ui/parser/keyword-override.stderr
+++ b/src/test/ui/parser/keyword-override.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found reserved keyword `override`
+error: expected identifier, found reserved keyword `override`
   --> $DIR/keyword-override.rs:2:9
    |
 LL |     let override = ();
-   |         ^^^^^^^^ expected pattern
+   |         ^^^^^^^^ expected identifier, found reserved keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#override = ();
+   |         ^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-pub-as-identifier.rs b/src/test/ui/parser/keyword-pub-as-identifier.rs
index 2ed8cc6b268ca..2b2bb14118d7d 100644
--- a/src/test/ui/parser/keyword-pub-as-identifier.rs
+++ b/src/test/ui/parser/keyword-pub-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py pub'
 
 fn main() {
-    let pub = "foo"; //~ error: expected pattern, found keyword `pub`
+    let pub = "foo"; //~ error: expected identifier, found keyword `pub`
 }
diff --git a/src/test/ui/parser/keyword-pub-as-identifier.stderr b/src/test/ui/parser/keyword-pub-as-identifier.stderr
index 526ddcd6ee0ff..f81078b12bd3c 100644
--- a/src/test/ui/parser/keyword-pub-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-pub-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `pub`
+error: expected identifier, found keyword `pub`
   --> $DIR/keyword-pub-as-identifier.rs:4:9
    |
 LL |     let pub = "foo";
-   |         ^^^ expected pattern
+   |         ^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#pub = "foo";
+   |         ^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-return-as-identifier.rs b/src/test/ui/parser/keyword-return-as-identifier.rs
index 920931b00f954..e1a2db5e4d82d 100644
--- a/src/test/ui/parser/keyword-return-as-identifier.rs
+++ b/src/test/ui/parser/keyword-return-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py return'
 
 fn main() {
-    let return = "foo"; //~ error: expected pattern, found keyword `return`
+    let return = "foo"; //~ error: expected identifier, found keyword `return`
 }
diff --git a/src/test/ui/parser/keyword-return-as-identifier.stderr b/src/test/ui/parser/keyword-return-as-identifier.stderr
index c0156a63fa9d1..8cc4d12fbbb9a 100644
--- a/src/test/ui/parser/keyword-return-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-return-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `return`
+error: expected identifier, found keyword `return`
   --> $DIR/keyword-return-as-identifier.rs:4:9
    |
 LL |     let return = "foo";
-   |         ^^^^^^ expected pattern
+   |         ^^^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#return = "foo";
+   |         ^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-static-as-identifier.rs b/src/test/ui/parser/keyword-static-as-identifier.rs
index 3ccbfccfc9394..423b9854b8aa1 100644
--- a/src/test/ui/parser/keyword-static-as-identifier.rs
+++ b/src/test/ui/parser/keyword-static-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py static'
 
 fn main() {
-    let static = "foo"; //~ error: expected pattern, found keyword `static`
+    let static = "foo"; //~ error: expected identifier, found keyword `static`
 }
diff --git a/src/test/ui/parser/keyword-static-as-identifier.stderr b/src/test/ui/parser/keyword-static-as-identifier.stderr
index 00a65977732f8..7d22bc97d66ae 100644
--- a/src/test/ui/parser/keyword-static-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-static-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `static`
+error: expected identifier, found keyword `static`
   --> $DIR/keyword-static-as-identifier.rs:4:9
    |
 LL |     let static = "foo";
-   |         ^^^^^^ expected pattern
+   |         ^^^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#static = "foo";
+   |         ^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-struct-as-identifier.rs b/src/test/ui/parser/keyword-struct-as-identifier.rs
index 69d8f19065533..18cfe11592aed 100644
--- a/src/test/ui/parser/keyword-struct-as-identifier.rs
+++ b/src/test/ui/parser/keyword-struct-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py struct'
 
 fn main() {
-    let struct = "foo"; //~ error: expected pattern, found keyword `struct`
+    let struct = "foo"; //~ error: expected identifier, found keyword `struct`
 }
diff --git a/src/test/ui/parser/keyword-struct-as-identifier.stderr b/src/test/ui/parser/keyword-struct-as-identifier.stderr
index b2d6639e72ecb..b109fa6247dcd 100644
--- a/src/test/ui/parser/keyword-struct-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-struct-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `struct`
+error: expected identifier, found keyword `struct`
   --> $DIR/keyword-struct-as-identifier.rs:4:9
    |
 LL |     let struct = "foo";
-   |         ^^^^^^ expected pattern
+   |         ^^^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#struct = "foo";
+   |         ^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-trait-as-identifier.rs b/src/test/ui/parser/keyword-trait-as-identifier.rs
index f62858442d252..67f81167dbdd3 100644
--- a/src/test/ui/parser/keyword-trait-as-identifier.rs
+++ b/src/test/ui/parser/keyword-trait-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py trait'
 
 fn main() {
-    let trait = "foo"; //~ error: expected pattern, found keyword `trait`
+    let trait = "foo"; //~ error: expected identifier, found keyword `trait`
 }
diff --git a/src/test/ui/parser/keyword-trait-as-identifier.stderr b/src/test/ui/parser/keyword-trait-as-identifier.stderr
index b31c0df28c008..ccc675cdb3a7c 100644
--- a/src/test/ui/parser/keyword-trait-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-trait-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `trait`
+error: expected identifier, found keyword `trait`
   --> $DIR/keyword-trait-as-identifier.rs:4:9
    |
 LL |     let trait = "foo";
-   |         ^^^^^ expected pattern
+   |         ^^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#trait = "foo";
+   |         ^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-try-as-identifier-edition2018.rs b/src/test/ui/parser/keyword-try-as-identifier-edition2018.rs
index 13a938b2e0988..4fa37bdb057b7 100644
--- a/src/test/ui/parser/keyword-try-as-identifier-edition2018.rs
+++ b/src/test/ui/parser/keyword-try-as-identifier-edition2018.rs
@@ -1,5 +1,5 @@
 // compile-flags: --edition 2018
 
 fn main() {
-    let try = "foo"; //~ error: expected pattern, found reserved keyword `try`
+    let try = "foo"; //~ error: expected identifier, found reserved keyword `try`
 }
diff --git a/src/test/ui/parser/keyword-try-as-identifier-edition2018.stderr b/src/test/ui/parser/keyword-try-as-identifier-edition2018.stderr
index c342e3a76fbb4..f71b889a30db5 100644
--- a/src/test/ui/parser/keyword-try-as-identifier-edition2018.stderr
+++ b/src/test/ui/parser/keyword-try-as-identifier-edition2018.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found reserved keyword `try`
+error: expected identifier, found reserved keyword `try`
   --> $DIR/keyword-try-as-identifier-edition2018.rs:4:9
    |
 LL |     let try = "foo";
-   |         ^^^ expected pattern
+   |         ^^^ expected identifier, found reserved keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#try = "foo";
+   |         ^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-type-as-identifier.rs b/src/test/ui/parser/keyword-type-as-identifier.rs
index 992547e6f59c0..04adddf72c6ff 100644
--- a/src/test/ui/parser/keyword-type-as-identifier.rs
+++ b/src/test/ui/parser/keyword-type-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py type'
 
 fn main() {
-    let type = "foo"; //~ error: expected pattern, found keyword `type`
+    let type = "foo"; //~ error: expected identifier, found keyword `type`
 }
diff --git a/src/test/ui/parser/keyword-type-as-identifier.stderr b/src/test/ui/parser/keyword-type-as-identifier.stderr
index b749c708d441d..88099d949a829 100644
--- a/src/test/ui/parser/keyword-type-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-type-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `type`
+error: expected identifier, found keyword `type`
   --> $DIR/keyword-type-as-identifier.rs:4:9
    |
 LL |     let type = "foo";
-   |         ^^^^ expected pattern
+   |         ^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#type = "foo";
+   |         ^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-typeof.rs b/src/test/ui/parser/keyword-typeof.rs
index 4ef102646ef1c..29dc77d276cbd 100644
--- a/src/test/ui/parser/keyword-typeof.rs
+++ b/src/test/ui/parser/keyword-typeof.rs
@@ -1,3 +1,3 @@
 fn main() {
-    let typeof = (); //~ ERROR expected pattern, found reserved keyword `typeof`
+    let typeof = (); //~ ERROR expected identifier, found reserved keyword `typeof`
 }
diff --git a/src/test/ui/parser/keyword-typeof.stderr b/src/test/ui/parser/keyword-typeof.stderr
index e7b18023e61a9..4a1b63d5c9357 100644
--- a/src/test/ui/parser/keyword-typeof.stderr
+++ b/src/test/ui/parser/keyword-typeof.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found reserved keyword `typeof`
+error: expected identifier, found reserved keyword `typeof`
   --> $DIR/keyword-typeof.rs:2:9
    |
 LL |     let typeof = ();
-   |         ^^^^^^ expected pattern
+   |         ^^^^^^ expected identifier, found reserved keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#typeof = ();
+   |         ^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-unsafe-as-identifier.rs b/src/test/ui/parser/keyword-unsafe-as-identifier.rs
index adb20ebe48c79..0ff6d188c6480 100644
--- a/src/test/ui/parser/keyword-unsafe-as-identifier.rs
+++ b/src/test/ui/parser/keyword-unsafe-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py unsafe'
 
 fn main() {
-    let unsafe = "foo"; //~ error: expected pattern, found keyword `unsafe`
+    let unsafe = "foo"; //~ error: expected identifier, found keyword `unsafe`
 }
diff --git a/src/test/ui/parser/keyword-unsafe-as-identifier.stderr b/src/test/ui/parser/keyword-unsafe-as-identifier.stderr
index 67935ce43ba04..205bb81df405b 100644
--- a/src/test/ui/parser/keyword-unsafe-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-unsafe-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `unsafe`
+error: expected identifier, found keyword `unsafe`
   --> $DIR/keyword-unsafe-as-identifier.rs:4:9
    |
 LL |     let unsafe = "foo";
-   |         ^^^^^^ expected pattern
+   |         ^^^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#unsafe = "foo";
+   |         ^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-use-as-identifier.rs b/src/test/ui/parser/keyword-use-as-identifier.rs
index 198444bafc5b8..821bedee08832 100644
--- a/src/test/ui/parser/keyword-use-as-identifier.rs
+++ b/src/test/ui/parser/keyword-use-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py use'
 
 fn main() {
-    let use = "foo"; //~ error: expected pattern, found keyword `use`
+    let use = "foo"; //~ error: expected identifier, found keyword `use`
 }
diff --git a/src/test/ui/parser/keyword-use-as-identifier.stderr b/src/test/ui/parser/keyword-use-as-identifier.stderr
index 2c69d0a8744a0..85a0492f5735f 100644
--- a/src/test/ui/parser/keyword-use-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-use-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `use`
+error: expected identifier, found keyword `use`
   --> $DIR/keyword-use-as-identifier.rs:4:9
    |
 LL |     let use = "foo";
-   |         ^^^ expected pattern
+   |         ^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#use = "foo";
+   |         ^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-where-as-identifier.rs b/src/test/ui/parser/keyword-where-as-identifier.rs
index 5624a8fc46038..56301bd20adfd 100644
--- a/src/test/ui/parser/keyword-where-as-identifier.rs
+++ b/src/test/ui/parser/keyword-where-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py where'
 
 fn main() {
-    let where = "foo"; //~ error: expected pattern, found keyword `where`
+    let where = "foo"; //~ error: expected identifier, found keyword `where`
 }
diff --git a/src/test/ui/parser/keyword-where-as-identifier.stderr b/src/test/ui/parser/keyword-where-as-identifier.stderr
index fc01183ca046b..b8b8506907636 100644
--- a/src/test/ui/parser/keyword-where-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-where-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `where`
+error: expected identifier, found keyword `where`
   --> $DIR/keyword-where-as-identifier.rs:4:9
    |
 LL |     let where = "foo";
-   |         ^^^^^ expected pattern
+   |         ^^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#where = "foo";
+   |         ^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/keyword-while-as-identifier.rs b/src/test/ui/parser/keyword-while-as-identifier.rs
index c0a539d350764..22026d15dcbfb 100644
--- a/src/test/ui/parser/keyword-while-as-identifier.rs
+++ b/src/test/ui/parser/keyword-while-as-identifier.rs
@@ -1,5 +1,5 @@
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py while'
 
 fn main() {
-    let while = "foo"; //~ error: expected pattern, found keyword `while`
+    let while = "foo"; //~ error: expected identifier, found keyword `while`
 }
diff --git a/src/test/ui/parser/keyword-while-as-identifier.stderr b/src/test/ui/parser/keyword-while-as-identifier.stderr
index f72ac87742099..bb0c0ac668a41 100644
--- a/src/test/ui/parser/keyword-while-as-identifier.stderr
+++ b/src/test/ui/parser/keyword-while-as-identifier.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found keyword `while`
+error: expected identifier, found keyword `while`
   --> $DIR/keyword-while-as-identifier.rs:4:9
    |
 LL |     let while = "foo";
-   |         ^^^^^ expected pattern
+   |         ^^^^^ expected identifier, found keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#while = "foo";
+   |         ^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/mut-patterns.rs b/src/test/ui/parser/mut-patterns.rs
index bffeb1e2e7c40..87e127f9d364a 100644
--- a/src/test/ui/parser/mut-patterns.rs
+++ b/src/test/ui/parser/mut-patterns.rs
@@ -1,7 +1,35 @@
 // Can't put mut in non-ident pattern
 
+// edition:2018
+
+#![feature(box_patterns)]
+#![allow(warnings)]
+
 pub fn main() {
+    let mut mut x = 0;
+    //~^ ERROR `mut` on a binding may not be repeated
+    //~| remove the additional `mut`s
+
     struct Foo { x: isize }
     let mut Foo { x: x } = Foo { x: 3 };
-    //~^ ERROR: expected one of `:`, `;`, `=`, `@`, or `|`, found `{`
+    //~^ ERROR `mut` must be attached to each individual binding
+    //~| add `mut` to each binding
+
+    let mut Foo { x } = Foo { x: 3 };
+    //~^ ERROR `mut` must be attached to each individual binding
+    //~| add `mut` to each binding
+
+    struct r#yield(u8, u8);
+    let mut mut yield(become, await) = r#yield(0, 0);
+    //~^ ERROR `mut` on a binding may not be repeated
+    //~| ERROR `mut` must be attached to each individual binding
+    //~| ERROR expected identifier, found reserved keyword `yield`
+    //~| ERROR expected identifier, found reserved keyword `become`
+    //~| ERROR expected identifier, found reserved keyword `await`
+
+    struct W<T, U>(T, U);
+    struct B { f: Box<u8> }
+    let mut W(mut a, W(b, W(ref c, W(d, B { box f }))))
+    //~^ ERROR `mut` must be attached to each individual binding
+        = W(0, W(1, W(2, W(3, B { f: Box::new(4u8) }))));
 }
diff --git a/src/test/ui/parser/mut-patterns.stderr b/src/test/ui/parser/mut-patterns.stderr
index b39209afd4295..a251e2908f02c 100644
--- a/src/test/ui/parser/mut-patterns.stderr
+++ b/src/test/ui/parser/mut-patterns.stderr
@@ -1,8 +1,68 @@
-error: expected one of `:`, `;`, `=`, `@`, or `|`, found `{`
-  --> $DIR/mut-patterns.rs:5:17
+error: `mut` on a binding may not be repeated
+  --> $DIR/mut-patterns.rs:9: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
    |
 LL |     let mut Foo { x: x } = Foo { x: 3 };
-   |                 ^ expected one of `:`, `;`, `=`, `@`, or `|` here
+   |         ^^^^^^^^^^^^^^^^ help: add `mut` to each binding: `Foo { x: mut x }`
+
+error: `mut` must be attached to each individual binding
+  --> $DIR/mut-patterns.rs:18:9
+   |
+LL |     let mut Foo { x } = Foo { x: 3 };
+   |         ^^^^^^^^^^^^^ help: add `mut` to each binding: `Foo { mut x }`
+
+error: `mut` on a binding may not be repeated
+  --> $DIR/mut-patterns.rs:23: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
+   |
+LL |     let mut mut yield(become, await) = r#yield(0, 0);
+   |                 ^^^^^ expected identifier, found reserved keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+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
+   |
+LL |     let mut mut yield(become, await) = r#yield(0, 0);
+   |                       ^^^^^^ expected identifier, found reserved keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+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
+   |
+LL |     let mut mut yield(become, await) = r#yield(0, 0);
+   |                               ^^^^^ expected identifier, found reserved keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+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
+   |
+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)`
+
+error: `mut` must be attached to each individual binding
+  --> $DIR/mut-patterns.rs:32: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 }))))`
 
-error: aborting due to previous error
+error: aborting due to 9 previous errors
 
diff --git a/src/test/ui/reserved/reserved-become.rs b/src/test/ui/reserved/reserved-become.rs
index 2279a05e6b2db..56645255ee5f6 100644
--- a/src/test/ui/reserved/reserved-become.rs
+++ b/src/test/ui/reserved/reserved-become.rs
@@ -1,4 +1,4 @@
 fn main() {
     let become = 0;
-    //~^ ERROR expected pattern, found reserved keyword `become`
+    //~^ ERROR expected identifier, found reserved keyword `become`
 }
diff --git a/src/test/ui/reserved/reserved-become.stderr b/src/test/ui/reserved/reserved-become.stderr
index f9fe78e18b393..3ce9fb33c289e 100644
--- a/src/test/ui/reserved/reserved-become.stderr
+++ b/src/test/ui/reserved/reserved-become.stderr
@@ -1,8 +1,12 @@
-error: expected pattern, found reserved keyword `become`
+error: expected identifier, found reserved keyword `become`
   --> $DIR/reserved-become.rs:2:9
    |
 LL |     let become = 0;
-   |         ^^^^^^ expected pattern
+   |         ^^^^^^ expected identifier, found reserved keyword
+help: you can escape reserved keywords to use them as identifiers
+   |
+LL |     let r#become = 0;
+   |         ^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/self/self_type_keyword.rs b/src/test/ui/self/self_type_keyword.rs
index 01b3309fcacb1..d479905932be0 100644
--- a/src/test/ui/self/self_type_keyword.rs
+++ b/src/test/ui/self/self_type_keyword.rs
@@ -14,7 +14,8 @@ pub fn main() {
         ref Self => (),
         //~^ ERROR expected identifier, found keyword `Self`
         mut Self => (),
-        //~^ ERROR expected identifier, found keyword `Self`
+        //~^ ERROR `mut` must be attached to each individual binding
+        //~| ERROR cannot find unit struct/variant or constant `Self`
         ref mut Self => (),
         //~^ ERROR expected identifier, found keyword `Self`
         Self!() => (),
diff --git a/src/test/ui/self/self_type_keyword.stderr b/src/test/ui/self/self_type_keyword.stderr
index b63de98b8e702..fdae06ccdd9f5 100644
--- a/src/test/ui/self/self_type_keyword.stderr
+++ b/src/test/ui/self/self_type_keyword.stderr
@@ -10,38 +10,38 @@ error: expected identifier, found keyword `Self`
 LL |         ref Self => (),
    |             ^^^^ expected identifier, found keyword
 
-error: expected identifier, found keyword `Self`
-  --> $DIR/self_type_keyword.rs:16:13
+error: `mut` must be attached to each individual binding
+  --> $DIR/self_type_keyword.rs:16:9
    |
 LL |         mut Self => (),
-   |             ^^^^ expected identifier, found keyword
+   |         ^^^^^^^^ help: add `mut` to each binding: `Self`
 
 error: expected identifier, found keyword `Self`
-  --> $DIR/self_type_keyword.rs:18:17
+  --> $DIR/self_type_keyword.rs:19:17
    |
 LL |         ref mut Self => (),
    |                 ^^^^ expected identifier, found keyword
 
 error: expected identifier, found keyword `Self`
-  --> $DIR/self_type_keyword.rs:22:15
+  --> $DIR/self_type_keyword.rs:23:15
    |
 LL |         Foo { Self } => (),
    |               ^^^^ expected identifier, found keyword
 
 error: expected identifier, found keyword `Self`
-  --> $DIR/self_type_keyword.rs:28:26
+  --> $DIR/self_type_keyword.rs:29:26
    |
 LL |     extern crate core as Self;
    |                          ^^^^ expected identifier, found keyword
 
 error: expected identifier, found keyword `Self`
-  --> $DIR/self_type_keyword.rs:33:32
+  --> $DIR/self_type_keyword.rs:34:32
    |
 LL |     use std::option::Option as Self;
    |                                ^^^^ expected identifier, found keyword
 
 error: expected identifier, found keyword `Self`
-  --> $DIR/self_type_keyword.rs:38:11
+  --> $DIR/self_type_keyword.rs:39:11
    |
 LL |     trait Self {}
    |           ^^^^ expected identifier, found keyword
@@ -53,11 +53,21 @@ LL | struct Bar<'Self>;
    |            ^^^^^
 
 error: cannot find macro `Self!` in this scope
-  --> $DIR/self_type_keyword.rs:20:9
+  --> $DIR/self_type_keyword.rs:21:9
    |
 LL |         Self!() => (),
    |         ^^^^
 
+error[E0531]: cannot find unit struct/variant or constant `Self` in this scope
+  --> $DIR/self_type_keyword.rs:16:13
+   |
+LL |         mut Self => (),
+   |             ^^^^ not found in this scope
+help: possible candidate is found in another module, you can import it into scope
+   |
+LL | use foo::Self;
+   |
+
 error[E0392]: parameter `'Self` is never used
   --> $DIR/self_type_keyword.rs:6:12
    |
@@ -66,6 +76,6 @@ LL | struct Bar<'Self>;
    |
    = help: consider removing `'Self` or using a marker such as `std::marker::PhantomData`
 
-error: aborting due to 11 previous errors
+error: aborting due to 12 previous errors
 
 For more information about this error, try `rustc --explain E0392`.

From dbbe3363c94b120d1eba9cba01dadddd862716b8 Mon Sep 17 00:00:00 2001
From: Mazdak Farrokhzad <twingoow@gmail.com>
Date: Tue, 27 Aug 2019 19:51:21 +0200
Subject: [PATCH 4/5] Ensure 'let mut ;' where ':pat' is banned.

---
 src/libsyntax/parse/parser/pat.rs      |  9 +++++++++
 src/test/ui/parser/mut-patterns.rs     |  8 ++++++++
 src/test/ui/parser/mut-patterns.stderr | 11 ++++++++++-
 3 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/src/libsyntax/parse/parser/pat.rs b/src/libsyntax/parse/parser/pat.rs
index 7b228a700a748..08934e8533049 100644
--- a/src/libsyntax/parse/parser/pat.rs
+++ b/src/libsyntax/parse/parser/pat.rs
@@ -384,6 +384,7 @@ impl<'a> Parser<'a> {
         })
     }
 
+    /// Parse a mutable binding with the `mut` token already eaten.
     fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> {
         let mut_span = self.prev_span;
 
@@ -393,6 +394,14 @@ impl<'a> Parser<'a> {
 
         self.recover_additional_muts();
 
+        // Make sure we don't allow e.g. `let mut $p;` where `$p:pat`.
+        if let token::Interpolated(ref nt) = self.token.kind {
+             if let token::NtPat(_) = **nt {
+                 self.expected_ident_found().emit();
+             }
+        }
+
+        // Parse the pattern we hope to be an identifier.
         let mut pat = self.parse_pat(Some("identifier"))?;
 
         // Add `mut` to any binding in the parsed pattern.
diff --git a/src/test/ui/parser/mut-patterns.rs b/src/test/ui/parser/mut-patterns.rs
index 87e127f9d364a..0c78ca726e003 100644
--- a/src/test/ui/parser/mut-patterns.rs
+++ b/src/test/ui/parser/mut-patterns.rs
@@ -32,4 +32,12 @@ pub fn main() {
     let mut W(mut a, W(b, W(ref c, W(d, B { box f }))))
     //~^ ERROR `mut` must be attached to each individual binding
         = W(0, W(1, W(2, W(3, B { f: Box::new(4u8) }))));
+
+    // Make sure we don't accidentally allow `mut $p` where `$p:pat`.
+    macro_rules! foo {
+        ($p:pat) => {
+            let mut $p = 0; //~ ERROR expected identifier, found `x`
+        }
+    }
+    foo!(x);
 }
diff --git a/src/test/ui/parser/mut-patterns.stderr b/src/test/ui/parser/mut-patterns.stderr
index a251e2908f02c..a1293129e2eaf 100644
--- a/src/test/ui/parser/mut-patterns.stderr
+++ b/src/test/ui/parser/mut-patterns.stderr
@@ -64,5 +64,14 @@ error: `mut` must be attached to each individual binding
 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 }))))`
 
-error: aborting due to 9 previous errors
+error: expected identifier, found `x`
+  --> $DIR/mut-patterns.rs:39:21
+   |
+LL |             let mut $p = 0;
+   |                     ^^ expected identifier
+...
+LL |     foo!(x);
+   |     -------- in this macro invocation
+
+error: aborting due to 10 previous errors
 

From 42e895d4d99ec7724f3efd632f52170f3f99a5aa Mon Sep 17 00:00:00 2001
From: Mazdak Farrokhzad <twingoow@gmail.com>
Date: Tue, 27 Aug 2019 23:44:44 +0200
Subject: [PATCH 5/5] Improve 'mut ' diagnostic.

---
 src/libsyntax/parse/parser/pat.rs         | 54 ++++++++++++++---------
 src/test/ui/parser/issue-32501.rs         |  2 +-
 src/test/ui/parser/issue-32501.stderr     |  6 ++-
 src/test/ui/parser/mut-patterns.rs        |  3 ++
 src/test/ui/parser/mut-patterns.stderr    | 46 ++++++++++++++-----
 src/test/ui/self/self_type_keyword.rs     |  2 +-
 src/test/ui/self/self_type_keyword.stderr |  6 ++-
 7 files changed, 82 insertions(+), 37 deletions(-)

diff --git a/src/libsyntax/parse/parser/pat.rs b/src/libsyntax/parse/parser/pat.rs
index 08934e8533049..1ffb112a5e87e 100644
--- a/src/libsyntax/parse/parser/pat.rs
+++ b/src/libsyntax/parse/parser/pat.rs
@@ -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)
@@ -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.
diff --git a/src/test/ui/parser/issue-32501.rs b/src/test/ui/parser/issue-32501.rs
index 695baf8187276..500242030c655 100644
--- a/src/test/ui/parser/issue-32501.rs
+++ b/src/test/ui/parser/issue-32501.rs
@@ -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
 }
diff --git a/src/test/ui/parser/issue-32501.stderr b/src/test/ui/parser/issue-32501.stderr
index f5d3300cf9c56..d53302449a806 100644
--- a/src/test/ui/parser/issue-32501.stderr
+++ b/src/test/ui/parser/issue-32501.stderr
@@ -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
 
diff --git a/src/test/ui/parser/mut-patterns.rs b/src/test/ui/parser/mut-patterns.rs
index 0c78ca726e003..d46186a0fea0e 100644
--- a/src/test/ui/parser/mut-patterns.rs
+++ b/src/test/ui/parser/mut-patterns.rs
@@ -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
diff --git a/src/test/ui/parser/mut-patterns.stderr b/src/test/ui/parser/mut-patterns.stderr
index a1293129e2eaf..18ffaa5255870 100644
--- a/src/test/ui/parser/mut-patterns.stderr
+++ b/src/test/ui/parser/mut-patterns.stderr
@@ -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
@@ -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
@@ -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
@@ -53,19 +73,23 @@ 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
@@ -73,5 +97,5 @@ LL |             let mut $p = 0;
 LL |     foo!(x);
    |     -------- in this macro invocation
 
-error: aborting due to 10 previous errors
+error: aborting due to 12 previous errors
 
diff --git a/src/test/ui/self/self_type_keyword.rs b/src/test/ui/self/self_type_keyword.rs
index d479905932be0..844f13c2f896a 100644
--- a/src/test/ui/self/self_type_keyword.rs
+++ b/src/test/ui/self/self_type_keyword.rs
@@ -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`
diff --git a/src/test/ui/self/self_type_keyword.stderr b/src/test/ui/self/self_type_keyword.stderr
index fdae06ccdd9f5..bb631194bf3df 100644
--- a/src/test/ui/self/self_type_keyword.stderr
+++ b/src/test/ui/self/self_type_keyword.stderr
@@ -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