about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libsyntax/parse/parser/diagnostics.rs150
-rw-r--r--src/libsyntax/parse/parser/item.rs24
-rw-r--r--src/libsyntax/parse/parser/stmt.rs6
-rw-r--r--src/libsyntax/parse/token.rs51
-rw-r--r--src/test/ui/async-await/no-async-const.rs2
-rw-r--r--src/test/ui/async-await/no-async-const.stderr2
-rw-r--r--src/test/ui/async-await/no-unsafe-async.rs4
-rw-r--r--src/test/ui/async-await/no-unsafe-async.stderr4
-rw-r--r--src/test/ui/can-begin-expr-check.rs2
-rw-r--r--src/test/ui/can-begin-expr-check.stderr2
-rw-r--r--src/test/ui/issues/issue-43196.stderr2
-rw-r--r--src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr2
-rw-r--r--src/test/ui/macros/issue-54441.stderr4
-rw-r--r--src/test/ui/parser/default.stderr2
-rw-r--r--src/test/ui/parser/duplicate-visibility.stderr2
-rw-r--r--src/test/ui/parser/extern-expected-fn-or-brace.rs2
-rw-r--r--src/test/ui/parser/extern-expected-fn-or-brace.stderr2
-rw-r--r--src/test/ui/parser/impl-parsing.stderr2
-rw-r--r--src/test/ui/parser/issue-15980.rs2
-rw-r--r--src/test/ui/parser/issue-15980.stderr2
-rw-r--r--src/test/ui/parser/issue-19398.rs2
-rw-r--r--src/test/ui/parser/issue-19398.stderr4
-rw-r--r--src/test/ui/parser/issue-3036.rs4
-rw-r--r--src/test/ui/parser/issue-3036.stderr8
-rw-r--r--src/test/ui/parser/macro/trait-non-item-macros.stderr2
-rw-r--r--src/test/ui/parser/raw/raw-literal-keywords.rs2
-rw-r--r--src/test/ui/parser/raw/raw-literal-keywords.stderr2
-rw-r--r--src/test/ui/parser/recover-for-loop-parens-around-head.rs2
-rw-r--r--src/test/ui/parser/recover-for-loop-parens-around-head.stderr2
-rw-r--r--src/test/ui/parser/recover-missing-semi.rs4
-rw-r--r--src/test/ui/parser/recover-missing-semi.stderr20
-rw-r--r--src/test/ui/parser/removed-syntax-static-fn.stderr2
-rw-r--r--src/test/ui/parser/removed-syntax-uniq-mut-ty.rs3
-rw-r--r--src/test/ui/parser/removed-syntax-uniq-mut-ty.stderr2
-rw-r--r--src/test/ui/parser/underscore_item_not_const.rs2
-rw-r--r--src/test/ui/parser/underscore_item_not_const.stderr2
36 files changed, 175 insertions, 157 deletions
diff --git a/src/libsyntax/parse/parser/diagnostics.rs b/src/libsyntax/parse/parser/diagnostics.rs
index 06982c789db..ab2b4519cb7 100644
--- a/src/libsyntax/parse/parser/diagnostics.rs
+++ b/src/libsyntax/parse/parser/diagnostics.rs
@@ -6,7 +6,7 @@ use crate::ast::{
     self, Param, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Ident, Item, ItemKind,
     Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind,
 };
-use crate::parse::token::{self, TokenKind};
+use crate::parse::token::{self, TokenKind, token_can_begin_expr};
 use crate::print::pprust;
 use crate::ptr::P;
 use crate::symbol::{kw, sym};
@@ -274,23 +274,23 @@ impl<'a> Parser<'a> {
         expected.sort_by_cached_key(|x| x.to_string());
         expected.dedup();
         let expect = tokens_to_string(&expected[..]);
-        let actual = self.this_token_to_string();
+        let actual = self.this_token_descr();
         let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 {
             let short_expect = if expected.len() > 6 {
                 format!("{} possible tokens", expected.len())
             } else {
                 expect.clone()
             };
-            (format!("expected one of {}, found `{}`", expect, actual),
+            (format!("expected one of {}, found {}", expect, actual),
                 (self.sess.source_map().next_point(self.prev_span),
                 format!("expected one of {} here", short_expect)))
         } else if expected.is_empty() {
-            (format!("unexpected token: `{}`", actual),
+            (format!("unexpected token: {}", actual),
                 (self.prev_span, "unexpected token after this".to_string()))
         } else {
-            (format!("expected {}, found `{}`", expect, actual),
+            (format!("expected {}, found {}", expect, actual),
                 (self.sess.source_map().next_point(self.prev_span),
-                format!("expected {} here", expect)))
+                format!("expected {}", expect)))
         };
         self.last_unexpected_token_span = Some(self.token.span);
         let mut err = self.fatal(&msg_exp);
@@ -326,58 +326,28 @@ impl<'a> Parser<'a> {
             }
         }
 
-        let is_semi_suggestable = expected.iter().any(|t| match t {
-            TokenType::Token(token::Semi) => true, // We expect a `;` here.
-            _ => false,
-        }) && ( // A `;` would be expected before the current keyword.
-            self.token.is_keyword(kw::Break) ||
-            self.token.is_keyword(kw::Continue) ||
-            self.token.is_keyword(kw::For) ||
-            self.token.is_keyword(kw::If) ||
-            self.token.is_keyword(kw::Let) ||
-            self.token.is_keyword(kw::Loop) ||
-            self.token.is_keyword(kw::Match) ||
-            self.token.is_keyword(kw::Return) ||
-            self.token.is_keyword(kw::While)
-        );
         let sm = self.sess.source_map();
-        match (sm.lookup_line(self.token.span.lo()), sm.lookup_line(sp.lo())) {
-            (Ok(ref a), Ok(ref b)) if a.line != b.line && is_semi_suggestable => {
-                // The spans are in different lines, expected `;` and found `let` or `return`.
-                // High likelihood that it is only a missing `;`.
-                err.span_suggestion_short(
-                    label_sp,
-                    "a semicolon may be missing here",
-                    ";".to_string(),
-                    Applicability::MaybeIncorrect,
-                );
-                err.emit();
-                return Ok(true);
-            }
-            (Ok(ref a), Ok(ref b)) if a.line == b.line => {
-                // When the spans are in the same line, it means that the only content between
-                // them is whitespace, point at the found token in that case:
-                //
-                // X |     () => { syntax error };
-                //   |                    ^^^^^ expected one of 8 possible tokens here
-                //
-                // instead of having:
-                //
-                // X |     () => { syntax error };
-                //   |                   -^^^^^ unexpected token
-                //   |                   |
-                //   |                   expected one of 8 possible tokens here
-                err.span_label(self.token.span, label_exp);
-            }
-            _ if self.prev_span == syntax_pos::DUMMY_SP => {
-                // Account for macro context where the previous span might not be
-                // available to avoid incorrect output (#54841).
-                err.span_label(self.token.span, "unexpected token");
-            }
-            _ => {
-                err.span_label(sp, label_exp);
-                err.span_label(self.token.span, "unexpected token");
-            }
+        if self.prev_span == DUMMY_SP {
+            // Account for macro context where the previous span might not be
+            // available to avoid incorrect output (#54841).
+            err.span_label(self.token.span, label_exp);
+        } else if !sm.is_multiline(self.token.span.shrink_to_hi().until(sp.shrink_to_lo())) {
+            // When the spans are in the same line, it means that the only content between
+            // them is whitespace, point at the found token in that case:
+            //
+            // X |     () => { syntax error };
+            //   |                    ^^^^^ expected one of 8 possible tokens here
+            //
+            // instead of having:
+            //
+            // X |     () => { syntax error };
+            //   |                   -^^^^^ unexpected token
+            //   |                   |
+            //   |                   expected one of 8 possible tokens here
+            err.span_label(self.token.span, label_exp);
+        } else {
+            err.span_label(sp, label_exp);
+            err.span_label(self.token.span, "unexpected token");
         }
         self.maybe_annotate_with_ascription(&mut err, false);
         Err(err)
@@ -902,20 +872,64 @@ impl<'a> Parser<'a> {
             }
         }
         let sm = self.sess.source_map();
-        match (sm.lookup_line(prev_sp.lo()), sm.lookup_line(sp.lo())) {
-            (Ok(ref a), Ok(ref b)) if a.line == b.line => {
-                // When the spans are in the same line, it means that the only content
-                // between them is whitespace, point only at the found token.
-                err.span_label(sp, label_exp);
-            }
-            _ => {
-                err.span_label(prev_sp, label_exp);
-                err.span_label(sp, "unexpected token");
-            }
+        if !sm.is_multiline(prev_sp.until(sp)) {
+            // When the spans are in the same line, it means that the only content
+            // between them is whitespace, point only at the found token.
+            err.span_label(sp, label_exp);
+        } else {
+            err.span_label(prev_sp, label_exp);
+            err.span_label(sp, "unexpected token");
         }
         Err(err)
     }
 
+    pub(super) fn expect_semi(&mut self) -> PResult<'a, ()> {
+        if self.eat(&token::Semi) {
+            return Ok(());
+        }
+        let sm = self.sess.source_map();
+        let msg = format!("expected `;`, found `{}`", self.this_token_descr());
+        let appl = Applicability::MachineApplicable;
+        if self.token.span == DUMMY_SP || self.prev_span == DUMMY_SP {
+            // Likely inside a macro, can't provide meaninful suggestions.
+            return self.expect(&token::Semi).map(|_| ());
+        } else if !sm.is_multiline(self.prev_span.until(self.token.span)) {
+            // The current token is in the same line as the prior token, not recoverable.
+        } else if self.look_ahead(1, |t| t == &token::CloseDelim(token::Brace)
+            || token_can_begin_expr(t) && t.kind != token::Colon
+        ) && [token::Comma, token::Colon].contains(&self.token.kind) {
+            // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is
+            // either `,` or `:`, and the next token could either start a new statement or is a
+            // block close. For example:
+            //
+            //   let x = 32:
+            //   let y = 42;
+            self.bump();
+            let sp = self.prev_span;
+            self.struct_span_err(sp, &msg)
+                .span_suggestion(sp, "change this to `;`", ";".to_string(), appl)
+                .emit();
+            return Ok(())
+        } else if self.look_ahead(0, |t| t == &token::CloseDelim(token::Brace) || (
+                token_can_begin_expr(t)
+                && t != &token::Semi
+                && t != &token::Pound // Avoid triggering with too many trailing `#` in raw string.
+        )) {
+            // Missing semicolon typo. This is triggered if the next token could either start a
+            // new statement or is a block close. For example:
+            //
+            //   let x = 32
+            //   let y = 42;
+            let sp = self.prev_span.shrink_to_hi();
+            self.struct_span_err(sp, &msg)
+                .span_label(self.token.span, "unexpected token")
+                .span_suggestion_short(sp, "add `;` here", ";".to_string(), appl)
+                .emit();
+            return Ok(())
+        }
+        self.expect(&token::Semi).map(|_| ()) // Error unconditionally
+    }
+
     pub(super) fn parse_semi_or_incorrect_foreign_fn_body(
         &mut self,
         ident: &Ident,
@@ -943,7 +957,7 @@ impl<'a> Parser<'a> {
                 Err(mut err) => {
                     err.cancel();
                     mem::replace(self, parser_snapshot);
-                    self.expect(&token::Semi)?;
+                    self.expect_semi()?;
                 }
             }
         } else {
diff --git a/src/libsyntax/parse/parser/item.rs b/src/libsyntax/parse/parser/item.rs
index 506a1a2a27a..fe125336190 100644
--- a/src/libsyntax/parse/parser/item.rs
+++ b/src/libsyntax/parse/parser/item.rs
@@ -98,7 +98,7 @@ impl<'a> Parser<'a> {
         if self.eat_keyword(kw::Use) {
             // USE ITEM
             let item_ = ItemKind::Use(P(self.parse_use_tree()?));
-            self.expect(&token::Semi)?;
+            self.expect_semi()?;
 
             let span = lo.to(self.prev_span);
             let item = self.mk_item(span, Ident::invalid(), item_, vis, attrs);
@@ -526,7 +526,7 @@ impl<'a> Parser<'a> {
             // eat a matched-delimiter token tree:
             let (delim, tts) = self.expect_delimited_token_tree()?;
             if delim != MacDelimiter::Brace {
-                self.expect(&token::Semi)?;
+                self.expect_semi()?;
             }
 
             Ok(Some(Mac {
@@ -776,7 +776,7 @@ impl<'a> Parser<'a> {
         let typ = self.parse_ty()?;
         self.expect(&token::Eq)?;
         let expr = self.parse_expr()?;
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
         Ok((name, ImplItemKind::Const(typ, expr), Generics::default()))
     }
 
@@ -813,7 +813,7 @@ impl<'a> Parser<'a> {
 
             let bounds = self.parse_generic_bounds(None)?;
             tps.where_clause = self.parse_where_clause()?;
-            self.expect(&token::Semi)?;
+            self.expect_semi()?;
 
             let whole_span = lo.to(self.prev_span);
             if is_auto == IsAuto::Yes {
@@ -927,7 +927,7 @@ impl<'a> Parser<'a> {
         } else {
             None
         };
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
         Ok((ident, TraitItemKind::Const(ty, default), Generics::default()))
     }
 
@@ -951,7 +951,7 @@ impl<'a> Parser<'a> {
         } else {
             None
         };
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
 
         Ok((ident, TraitItemKind::Type(bounds, default), generics))
     }
@@ -1054,7 +1054,7 @@ impl<'a> Parser<'a> {
         } else {
             (orig_name, None)
         };
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
 
         let span = lo.to(self.prev_span);
         Ok(self.mk_item(span, item_name, ItemKind::ExternCrate(orig_name), visibility, attrs))
@@ -1217,7 +1217,7 @@ impl<'a> Parser<'a> {
         self.expect(&token::Colon)?;
         let ty = self.parse_ty()?;
         let hi = self.token.span;
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
         Ok(ForeignItem {
             ident,
             attrs,
@@ -1235,7 +1235,7 @@ impl<'a> Parser<'a> {
 
         let ident = self.parse_ident()?;
         let hi = self.token.span;
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
         Ok(ast::ForeignItem {
             ident,
             attrs,
@@ -1282,7 +1282,7 @@ impl<'a> Parser<'a> {
 
         self.expect(&token::Eq)?;
         let e = self.parse_expr()?;
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
         let item = match m {
             Some(m) => ItemKind::Static(ty, m, e),
             None => ItemKind::Const(ty, e),
@@ -1344,7 +1344,7 @@ impl<'a> Parser<'a> {
             let ty = self.parse_ty()?;
             AliasKind::Weak(ty)
         };
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
         Ok((ident, alias, tps))
     }
 
@@ -1468,7 +1468,7 @@ impl<'a> Parser<'a> {
         } else if self.token == token::OpenDelim(token::Paren) {
             let body = VariantData::Tuple(self.parse_tuple_struct_body()?, DUMMY_NODE_ID);
             generics.where_clause = self.parse_where_clause()?;
-            self.expect(&token::Semi)?;
+            self.expect_semi()?;
             body
         } else {
             let token_str = self.this_token_descr();
diff --git a/src/libsyntax/parse/parser/stmt.rs b/src/libsyntax/parse/parser/stmt.rs
index ea7e4c05ea1..4f51fefe66f 100644
--- a/src/libsyntax/parse/parser/stmt.rs
+++ b/src/libsyntax/parse/parser/stmt.rs
@@ -432,6 +432,7 @@ impl<'a> Parser<'a> {
             None => return Ok(None),
         };
 
+        let mut eat_semi = true;
         match stmt.kind {
             StmtKind::Expr(ref expr) if self.token != token::Eof => {
                 // expression without semicolon
@@ -453,13 +454,14 @@ impl<'a> Parser<'a> {
                 if macro_legacy_warnings && self.token != token::Semi {
                     self.warn_missing_semicolon();
                 } else {
-                    self.expect_one_of(&[], &[token::Semi])?;
+                    self.expect_semi()?;
+                    eat_semi = false;
                 }
             }
             _ => {}
         }
 
-        if self.eat(&token::Semi) {
+        if eat_semi && self.eat(&token::Semi) {
             stmt = stmt.add_trailing_semicolon();
         }
         stmt.span = stmt.span.to(self.prev_span);
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index 4a8b25c6107..03e77b199cc 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -143,34 +143,35 @@ impl Lit {
 
 pub(crate) fn ident_can_begin_expr(name: ast::Name, span: Span, is_raw: bool) -> bool {
     let ident_token = Token::new(Ident(name, is_raw), span);
+    token_can_begin_expr(&ident_token)
+}
 
+pub(crate) fn token_can_begin_expr(ident_token: &Token) -> bool {
     !ident_token.is_reserved_ident() ||
     ident_token.is_path_segment_keyword() ||
-    [
-        kw::Async,
-
-        // FIXME: remove when `await!(..)` syntax is removed
-        // https://github.com/rust-lang/rust/issues/60610
-        kw::Await,
-
-        kw::Do,
-        kw::Box,
-        kw::Break,
-        kw::Continue,
-        kw::False,
-        kw::For,
-        kw::If,
-        kw::Let,
-        kw::Loop,
-        kw::Match,
-        kw::Move,
-        kw::Return,
-        kw::True,
-        kw::Unsafe,
-        kw::While,
-        kw::Yield,
-        kw::Static,
-    ].contains(&name)
+    match ident_token.kind {
+        TokenKind::Ident(ident, _) => [
+            kw::Async,
+            kw::Do,
+            kw::Box,
+            kw::Break,
+            kw::Continue,
+            kw::False,
+            kw::For,
+            kw::If,
+            kw::Let,
+            kw::Loop,
+            kw::Match,
+            kw::Move,
+            kw::Return,
+            kw::True,
+            kw::Unsafe,
+            kw::While,
+            kw::Yield,
+            kw::Static,
+        ].contains(&ident),
+        _=> false,
+    }
 }
 
 fn ident_can_begin_type(name: ast::Name, span: Span, is_raw: bool) -> bool {
diff --git a/src/test/ui/async-await/no-async-const.rs b/src/test/ui/async-await/no-async-const.rs
index 7a6eb498b2e..44f02d1a7b1 100644
--- a/src/test/ui/async-await/no-async-const.rs
+++ b/src/test/ui/async-await/no-async-const.rs
@@ -3,4 +3,4 @@
 // compile-flags: --crate-type lib
 
 pub async const fn x() {}
-//~^ ERROR expected one of `fn` or `unsafe`, found `const`
+//~^ ERROR expected one of `fn` or `unsafe`, found keyword `const`
diff --git a/src/test/ui/async-await/no-async-const.stderr b/src/test/ui/async-await/no-async-const.stderr
index edbdfb56522..f89d1810ba4 100644
--- a/src/test/ui/async-await/no-async-const.stderr
+++ b/src/test/ui/async-await/no-async-const.stderr
@@ -1,4 +1,4 @@
-error: expected one of `fn` or `unsafe`, found `const`
+error: expected one of `fn` or `unsafe`, found keyword `const`
   --> $DIR/no-async-const.rs:5:11
    |
 LL | pub async const fn x() {}
diff --git a/src/test/ui/async-await/no-unsafe-async.rs b/src/test/ui/async-await/no-unsafe-async.rs
index 81e0cd799ad..1ac1bdffda9 100644
--- a/src/test/ui/async-await/no-unsafe-async.rs
+++ b/src/test/ui/async-await/no-unsafe-async.rs
@@ -4,8 +4,8 @@ struct S;
 
 impl S {
     #[cfg(FALSE)]
-    unsafe async fn g() {} //~ ERROR expected one of `extern` or `fn`, found `async`
+    unsafe async fn g() {} //~ ERROR expected one of `extern` or `fn`, found keyword `async`
 }
 
 #[cfg(FALSE)]
-unsafe async fn f() {} //~ ERROR expected one of `extern`, `fn`, or `{`, found `async`
+unsafe async fn f() {} //~ ERROR expected one of `extern`, `fn`, or `{`, found keyword `async`
diff --git a/src/test/ui/async-await/no-unsafe-async.stderr b/src/test/ui/async-await/no-unsafe-async.stderr
index c339c7c3bf5..79d9f1befd6 100644
--- a/src/test/ui/async-await/no-unsafe-async.stderr
+++ b/src/test/ui/async-await/no-unsafe-async.stderr
@@ -1,10 +1,10 @@
-error: expected one of `extern` or `fn`, found `async`
+error: expected one of `extern` or `fn`, found keyword `async`
   --> $DIR/no-unsafe-async.rs:7:12
    |
 LL |     unsafe async fn g() {}
    |            ^^^^^ expected one of `extern` or `fn` here
 
-error: expected one of `extern`, `fn`, or `{`, found `async`
+error: expected one of `extern`, `fn`, or `{`, found keyword `async`
   --> $DIR/no-unsafe-async.rs:11:8
    |
 LL | unsafe async fn f() {}
diff --git a/src/test/ui/can-begin-expr-check.rs b/src/test/ui/can-begin-expr-check.rs
index 35aed067c69..8974d9f48c1 100644
--- a/src/test/ui/can-begin-expr-check.rs
+++ b/src/test/ui/can-begin-expr-check.rs
@@ -16,5 +16,5 @@ pub fn main() {
         return break as ();
     }
 
-    return enum; //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `enum`
+    return enum; //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found keyword `enum`
 }
diff --git a/src/test/ui/can-begin-expr-check.stderr b/src/test/ui/can-begin-expr-check.stderr
index 676c2cb661e..0e03e9915fc 100644
--- a/src/test/ui/can-begin-expr-check.stderr
+++ b/src/test/ui/can-begin-expr-check.stderr
@@ -1,4 +1,4 @@
-error: expected one of `.`, `;`, `?`, `}`, or an operator, found `enum`
+error: expected one of `.`, `;`, `?`, `}`, or an operator, found keyword `enum`
   --> $DIR/can-begin-expr-check.rs:19:12
    |
 LL |     return enum;
diff --git a/src/test/ui/issues/issue-43196.stderr b/src/test/ui/issues/issue-43196.stderr
index 32efe23c72b..4f7ed5cc6fd 100644
--- a/src/test/ui/issues/issue-43196.stderr
+++ b/src/test/ui/issues/issue-43196.stderr
@@ -2,7 +2,7 @@ error: expected `|`, found `}`
   --> $DIR/issue-43196.rs:3:1
    |
 LL |     |
-   |      - expected `|` here
+   |      - expected `|`
 LL | }
    | ^ unexpected token
 
diff --git a/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr b/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr
index 447c76a5bbc..48c2f556f1d 100644
--- a/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr
+++ b/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr
@@ -2,7 +2,7 @@ error: expected `fn`, found `::`
   --> $DIR/keyword-extern-as-identifier-type.rs:1:16
    |
 LL | type A = extern::foo::bar;
-   |                ^^ expected `fn` here
+   |                ^^ expected `fn`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/macros/issue-54441.stderr b/src/test/ui/macros/issue-54441.stderr
index af0ee3ae8ec..287d579c76d 100644
--- a/src/test/ui/macros/issue-54441.stderr
+++ b/src/test/ui/macros/issue-54441.stderr
@@ -1,8 +1,8 @@
-error: expected one of `crate`, `fn`, `pub`, `static`, or `type`, found `let`
+error: expected one of `crate`, `fn`, `pub`, `static`, or `type`, found keyword `let`
   --> $DIR/issue-54441.rs:3:9
    |
 LL |         let
-   |         ^^^ unexpected token
+   |         ^^^ expected one of `crate`, `fn`, `pub`, `static`, or `type` here
 ...
 LL |     m!();
    |     ----- in this macro invocation
diff --git a/src/test/ui/parser/default.stderr b/src/test/ui/parser/default.stderr
index e199045134e..8843fd303ec 100644
--- a/src/test/ui/parser/default.stderr
+++ b/src/test/ui/parser/default.stderr
@@ -1,4 +1,4 @@
-error: expected one of `async`, `const`, `extern`, `fn`, `type`, or `unsafe`, found `pub`
+error: expected one of `async`, `const`, `extern`, `fn`, `type`, or `unsafe`, found keyword `pub`
   --> $DIR/default.rs:22:13
    |
 LL |     default pub fn foo<T: Default>() -> T { T::default() }
diff --git a/src/test/ui/parser/duplicate-visibility.stderr b/src/test/ui/parser/duplicate-visibility.stderr
index 880b536cd18..675adb88d20 100644
--- a/src/test/ui/parser/duplicate-visibility.stderr
+++ b/src/test/ui/parser/duplicate-visibility.stderr
@@ -1,4 +1,4 @@
-error: expected one of `(`, `fn`, `static`, or `type`, found `pub`
+error: expected one of `(`, `fn`, `static`, or `type`, found keyword `pub`
   --> $DIR/duplicate-visibility.rs:3:9
    |
 LL |     pub pub fn foo();
diff --git a/src/test/ui/parser/extern-expected-fn-or-brace.rs b/src/test/ui/parser/extern-expected-fn-or-brace.rs
index dd46b87fa42..907de0d8f91 100644
--- a/src/test/ui/parser/extern-expected-fn-or-brace.rs
+++ b/src/test/ui/parser/extern-expected-fn-or-brace.rs
@@ -1,4 +1,4 @@
 // Verifies that the expected token errors for `extern crate` are
 // raised
 
-extern "C" mod foo; //~ERROR expected one of `fn` or `{`, found `mod`
+extern "C" mod foo; //~ERROR expected one of `fn` or `{`, found keyword `mod`
diff --git a/src/test/ui/parser/extern-expected-fn-or-brace.stderr b/src/test/ui/parser/extern-expected-fn-or-brace.stderr
index 0fb99355341..691f4cddff2 100644
--- a/src/test/ui/parser/extern-expected-fn-or-brace.stderr
+++ b/src/test/ui/parser/extern-expected-fn-or-brace.stderr
@@ -1,4 +1,4 @@
-error: expected one of `fn` or `{`, found `mod`
+error: expected one of `fn` or `{`, found keyword `mod`
   --> $DIR/extern-expected-fn-or-brace.rs:4:12
    |
 LL | extern "C" mod foo;
diff --git a/src/test/ui/parser/impl-parsing.stderr b/src/test/ui/parser/impl-parsing.stderr
index 935e93963e1..e929fa53620 100644
--- a/src/test/ui/parser/impl-parsing.stderr
+++ b/src/test/ui/parser/impl-parsing.stderr
@@ -26,7 +26,7 @@ error: expected `impl`, found `FAIL`
   --> $DIR/impl-parsing.rs:11:16
    |
 LL | default unsafe FAIL
-   |                ^^^^ expected `impl` here
+   |                ^^^^ expected `impl`
 
 error: aborting due to 5 previous errors
 
diff --git a/src/test/ui/parser/issue-15980.rs b/src/test/ui/parser/issue-15980.rs
index ba874fb4476..beb94c8042d 100644
--- a/src/test/ui/parser/issue-15980.rs
+++ b/src/test/ui/parser/issue-15980.rs
@@ -11,7 +11,7 @@ fn main(){
         }
         //~^ NOTE expected one of `.`, `=>`, `?`, or an operator here
         _ => {}
-        //~^ ERROR expected one of `.`, `=>`, `?`, or an operator, found `_`
+        //~^ ERROR expected one of `.`, `=>`, `?`, or an operator, found reserved identifier `_`
         //~| NOTE unexpected token
     }
 }
diff --git a/src/test/ui/parser/issue-15980.stderr b/src/test/ui/parser/issue-15980.stderr
index 47c275110b4..26f75d45fa2 100644
--- a/src/test/ui/parser/issue-15980.stderr
+++ b/src/test/ui/parser/issue-15980.stderr
@@ -12,7 +12,7 @@ help: you can escape reserved keywords to use them as identifiers
 LL |             r#return
    |
 
-error: expected one of `.`, `=>`, `?`, or an operator, found `_`
+error: expected one of `.`, `=>`, `?`, or an operator, found reserved identifier `_`
   --> $DIR/issue-15980.rs:13:9
    |
 LL |         }
diff --git a/src/test/ui/parser/issue-19398.rs b/src/test/ui/parser/issue-19398.rs
index 822f4a6fde2..90221039b41 100644
--- a/src/test/ui/parser/issue-19398.rs
+++ b/src/test/ui/parser/issue-19398.rs
@@ -1,5 +1,5 @@
 trait T {
-    extern "Rust" unsafe fn foo(); //~ ERROR expected `fn`, found `unsafe`
+    extern "Rust" unsafe fn foo(); //~ ERROR expected `fn`, found keyword `unsafe`
 }
 
 fn main() {}
diff --git a/src/test/ui/parser/issue-19398.stderr b/src/test/ui/parser/issue-19398.stderr
index d5f1f972d55..41ec4f3ced4 100644
--- a/src/test/ui/parser/issue-19398.stderr
+++ b/src/test/ui/parser/issue-19398.stderr
@@ -1,8 +1,8 @@
-error: expected `fn`, found `unsafe`
+error: expected `fn`, found keyword `unsafe`
   --> $DIR/issue-19398.rs:2:19
    |
 LL |     extern "Rust" unsafe fn foo();
-   |                   ^^^^^^ expected `fn` here
+   |                   ^^^^^^ expected `fn`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/issue-3036.rs b/src/test/ui/parser/issue-3036.rs
index 00b241b9054..6a8b67fefa7 100644
--- a/src/test/ui/parser/issue-3036.rs
+++ b/src/test/ui/parser/issue-3036.rs
@@ -2,5 +2,5 @@
 
 fn main()
 {
-    let x = 3
-} //~ ERROR: expected one of `.`, `;`, `?`, or an operator, found `}`
+    let x = 3 //~ ERROR: expected `;`
+}
diff --git a/src/test/ui/parser/issue-3036.stderr b/src/test/ui/parser/issue-3036.stderr
index 18947b8fa40..b6557163d45 100644
--- a/src/test/ui/parser/issue-3036.stderr
+++ b/src/test/ui/parser/issue-3036.stderr
@@ -1,10 +1,10 @@
-error: expected one of `.`, `;`, `?`, or an operator, found `}`
-  --> $DIR/issue-3036.rs:6:1
+error: expected `;`, found ``}``
+  --> $DIR/issue-3036.rs:5:14
    |
 LL |     let x = 3
-   |              - expected one of `.`, `;`, `?`, or an operator here
+   |              ^ help: add `;` here
 LL | }
-   | ^ unexpected token
+   | - unexpected token
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/macro/trait-non-item-macros.stderr b/src/test/ui/parser/macro/trait-non-item-macros.stderr
index 76670e0bde0..a953e23a710 100644
--- a/src/test/ui/parser/macro/trait-non-item-macros.stderr
+++ b/src/test/ui/parser/macro/trait-non-item-macros.stderr
@@ -2,7 +2,7 @@ error: expected one of `async`, `const`, `extern`, `fn`, `type`, or `unsafe`, fo
   --> $DIR/trait-non-item-macros.rs:2:19
    |
 LL |     ($a:expr) => ($a)
-   |                   ^^ unexpected token
+   |                   ^^ expected one of `async`, `const`, `extern`, `fn`, `type`, or `unsafe` here
 ...
 LL |     bah!(2);
    |     -------- in this macro invocation
diff --git a/src/test/ui/parser/raw/raw-literal-keywords.rs b/src/test/ui/parser/raw/raw-literal-keywords.rs
index 6b055fbb117..bf9cbcdab2e 100644
--- a/src/test/ui/parser/raw/raw-literal-keywords.rs
+++ b/src/test/ui/parser/raw/raw-literal-keywords.rs
@@ -1,5 +1,5 @@
 fn test_if() {
-    r#if true { } //~ ERROR found `true`
+    r#if true { } //~ ERROR found keyword `true`
 }
 
 fn test_struct() {
diff --git a/src/test/ui/parser/raw/raw-literal-keywords.stderr b/src/test/ui/parser/raw/raw-literal-keywords.stderr
index f39e29cfaa8..4cea605be6f 100644
--- a/src/test/ui/parser/raw/raw-literal-keywords.stderr
+++ b/src/test/ui/parser/raw/raw-literal-keywords.stderr
@@ -1,4 +1,4 @@
-error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `true`
+error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found keyword `true`
   --> $DIR/raw-literal-keywords.rs:2:10
    |
 LL |     r#if true { }
diff --git a/src/test/ui/parser/recover-for-loop-parens-around-head.rs b/src/test/ui/parser/recover-for-loop-parens-around-head.rs
index c6be2c90667..779e1646344 100644
--- a/src/test/ui/parser/recover-for-loop-parens-around-head.rs
+++ b/src/test/ui/parser/recover-for-loop-parens-around-head.rs
@@ -8,7 +8,7 @@ fn main() {
     let vec = vec![1, 2, 3];
 
     for ( elem in vec ) {
-        //~^ ERROR expected one of `)`, `,`, `@`, or `|`, found `in`
+        //~^ ERROR expected one of `)`, `,`, `@`, or `|`, found keyword `in`
         //~| ERROR unexpected closing `)`
         const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types
     }
diff --git a/src/test/ui/parser/recover-for-loop-parens-around-head.stderr b/src/test/ui/parser/recover-for-loop-parens-around-head.stderr
index 1b5b6cca092..1a1f395ee21 100644
--- a/src/test/ui/parser/recover-for-loop-parens-around-head.stderr
+++ b/src/test/ui/parser/recover-for-loop-parens-around-head.stderr
@@ -1,4 +1,4 @@
-error: expected one of `)`, `,`, `@`, or `|`, found `in`
+error: expected one of `)`, `,`, `@`, or `|`, found keyword `in`
   --> $DIR/recover-for-loop-parens-around-head.rs:10:16
    |
 LL |     for ( elem in vec ) {
diff --git a/src/test/ui/parser/recover-missing-semi.rs b/src/test/ui/parser/recover-missing-semi.rs
index 1893dc716be..f47d5e6805f 100644
--- a/src/test/ui/parser/recover-missing-semi.rs
+++ b/src/test/ui/parser/recover-missing-semi.rs
@@ -1,13 +1,13 @@
 fn main() {
     let _: usize = ()
     //~^ ERROR mismatched types
+    //~| ERROR expected `;`
     let _ = 3;
-    //~^ ERROR expected one of
 }
 
 fn foo() -> usize {
     let _: usize = ()
     //~^ ERROR mismatched types
+    //~| ERROR expected `;`
     return 3;
-    //~^ ERROR expected one of
 }
diff --git a/src/test/ui/parser/recover-missing-semi.stderr b/src/test/ui/parser/recover-missing-semi.stderr
index 99339e4dd50..c40918ee2bd 100644
--- a/src/test/ui/parser/recover-missing-semi.stderr
+++ b/src/test/ui/parser/recover-missing-semi.stderr
@@ -1,20 +1,20 @@
-error: expected one of `.`, `;`, `?`, or an operator, found `let`
-  --> $DIR/recover-missing-semi.rs:4:5
+error: expected `;`, found `keyword `let``
+  --> $DIR/recover-missing-semi.rs:2:22
    |
 LL |     let _: usize = ()
-   |                      - help: a semicolon may be missing here
-LL |
+   |                      ^ help: add `;` here
+...
 LL |     let _ = 3;
-   |     ^^^
+   |     --- unexpected token
 
-error: expected one of `.`, `;`, `?`, or an operator, found `return`
-  --> $DIR/recover-missing-semi.rs:11:5
+error: expected `;`, found `keyword `return``
+  --> $DIR/recover-missing-semi.rs:9:22
    |
 LL |     let _: usize = ()
-   |                      - help: a semicolon may be missing here
-LL |
+   |                      ^ help: add `;` here
+...
 LL |     return 3;
-   |     ^^^^^^
+   |     ------ unexpected token
 
 error[E0308]: mismatched types
   --> $DIR/recover-missing-semi.rs:2:20
diff --git a/src/test/ui/parser/removed-syntax-static-fn.stderr b/src/test/ui/parser/removed-syntax-static-fn.stderr
index 21cb71df657..af148e69711 100644
--- a/src/test/ui/parser/removed-syntax-static-fn.stderr
+++ b/src/test/ui/parser/removed-syntax-static-fn.stderr
@@ -1,4 +1,4 @@
-error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `static`
+error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found keyword `static`
   --> $DIR/removed-syntax-static-fn.rs:4:5
    |
 LL | impl S {
diff --git a/src/test/ui/parser/removed-syntax-uniq-mut-ty.rs b/src/test/ui/parser/removed-syntax-uniq-mut-ty.rs
index 79d51f5595d..f9a9d071a3d 100644
--- a/src/test/ui/parser/removed-syntax-uniq-mut-ty.rs
+++ b/src/test/ui/parser/removed-syntax-uniq-mut-ty.rs
@@ -1 +1,2 @@
-type mut_box = Box<mut isize>; //~ ERROR expected one of `>`, const, lifetime, or type, found `mut`
+type mut_box = Box<mut isize>;
+//~^ ERROR expected one of `>`, const, lifetime, or type, found keyword `mut`
diff --git a/src/test/ui/parser/removed-syntax-uniq-mut-ty.stderr b/src/test/ui/parser/removed-syntax-uniq-mut-ty.stderr
index b6c5749c031..9c47e3db67d 100644
--- a/src/test/ui/parser/removed-syntax-uniq-mut-ty.stderr
+++ b/src/test/ui/parser/removed-syntax-uniq-mut-ty.stderr
@@ -1,4 +1,4 @@
-error: expected one of `>`, const, lifetime, or type, found `mut`
+error: expected one of `>`, const, lifetime, or type, found keyword `mut`
   --> $DIR/removed-syntax-uniq-mut-ty.rs:1:20
    |
 LL | type mut_box = Box<mut isize>;
diff --git a/src/test/ui/parser/underscore_item_not_const.rs b/src/test/ui/parser/underscore_item_not_const.rs
index 375bdc3a463..7b0d128f06f 100644
--- a/src/test/ui/parser/underscore_item_not_const.rs
+++ b/src/test/ui/parser/underscore_item_not_const.rs
@@ -25,6 +25,6 @@ use _ as g; //~ ERROR expected identifier, found reserved identifier `_`
 trait _ {} //~ ERROR expected identifier, found reserved identifier `_`
 trait _ = Copy; //~ ERROR expected identifier, found reserved identifier `_`
 macro_rules! _ { () => {} } //~ ERROR expected identifier, found reserved identifier `_`
-union _ { f: u8 } //~ ERROR expected one of `!` or `::`, found `_`
+union _ { f: u8 } //~ ERROR expected one of `!` or `::`, found reserved identifier `_`
 
 fn main() {}
diff --git a/src/test/ui/parser/underscore_item_not_const.stderr b/src/test/ui/parser/underscore_item_not_const.stderr
index deb4a012e32..8814aa35271 100644
--- a/src/test/ui/parser/underscore_item_not_const.stderr
+++ b/src/test/ui/parser/underscore_item_not_const.stderr
@@ -82,7 +82,7 @@ error: expected identifier, found reserved identifier `_`
 LL | macro_rules! _ { () => {} }
    |              ^ expected identifier, found reserved identifier
 
-error: expected one of `!` or `::`, found `_`
+error: expected one of `!` or `::`, found reserved identifier `_`
   --> $DIR/underscore_item_not_const.rs:28:7
    |
 LL | union _ { f: u8 }