From d673d0ac8462bd30612b0cce719ac0bf15dfaf86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 20 Oct 2019 14:35:46 -0700 Subject: [PATCH 1/3] Use heuristics to recover parsing of missing `;` - Detect `,` and `:` typos where `;` was intended. - When the next token could have been the start of a new statement, detect a missing semicolon. --- src/libsyntax/parse/parser/diagnostics.rs | 91 +++++++++++-------- src/libsyntax/parse/parser/item.rs | 24 ++--- src/libsyntax/parse/parser/stmt.rs | 6 +- src/libsyntax/parse/token.rs | 51 ++++++----- src/test/ui/parser/import-from-path.stderr | 2 +- src/test/ui/parser/import-from-rename.stderr | 4 +- src/test/ui/parser/import-glob-path.stderr | 2 +- src/test/ui/parser/import-glob-rename.stderr | 4 +- src/test/ui/parser/issue-3036.rs | 4 +- src/test/ui/parser/issue-3036.stderr | 8 +- src/test/ui/parser/recover-missing-semi.rs | 4 +- .../ui/parser/recover-missing-semi.stderr | 20 ++-- 12 files changed, 120 insertions(+), 100 deletions(-) diff --git a/src/libsyntax/parse/parser/diagnostics.rs b/src/libsyntax/parse/parser/diagnostics.rs index 06982c789db8b..677d16a40d9fc 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}; @@ -326,34 +326,8 @@ 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: @@ -902,18 +876,61 @@ 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); + 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.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; + if sm.is_multiline(self.prev_span.until(self.token.span)) { + 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(()) } - _ => { - err.span_label(prev_sp, label_exp); - err.span_label(sp, "unexpected token"); + } 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; + if sm.is_multiline(self.prev_span.until(self.token.span)) { + 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(()) } } - Err(err) + self.expect(&token::Semi).map(|_| ()) // Error unconditionally } pub(super) fn parse_semi_or_incorrect_foreign_fn_body( @@ -943,7 +960,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 506a1a2a27a34..fe12533619033 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 ea7e4c05ea1ae..4f51fefe66fba 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 4a8b25c61079b..03e77b199cc3b 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/parser/import-from-path.stderr b/src/test/ui/parser/import-from-path.stderr index 5842037fb8050..84c3b31df20be 100644 --- a/src/test/ui/parser/import-from-path.stderr +++ b/src/test/ui/parser/import-from-path.stderr @@ -2,7 +2,7 @@ error: expected `;`, found `::` --> $DIR/import-from-path.rs:2:15 | LL | use foo::{bar}::baz - | ^^ expected `;` + | ^^ expected `;` here error: aborting due to previous error diff --git a/src/test/ui/parser/import-from-rename.stderr b/src/test/ui/parser/import-from-rename.stderr index a966e9937373a..53ceb0280f987 100644 --- a/src/test/ui/parser/import-from-rename.stderr +++ b/src/test/ui/parser/import-from-rename.stderr @@ -1,8 +1,8 @@ -error: expected `;`, found keyword `as` +error: expected `;`, found `as` --> $DIR/import-from-rename.rs:3:16 | LL | use foo::{bar} as baz; - | ^^ expected `;` + | ^^ expected `;` here error: aborting due to previous error diff --git a/src/test/ui/parser/import-glob-path.stderr b/src/test/ui/parser/import-glob-path.stderr index ebca2db8305c3..44f4fc57a4a95 100644 --- a/src/test/ui/parser/import-glob-path.stderr +++ b/src/test/ui/parser/import-glob-path.stderr @@ -2,7 +2,7 @@ error: expected `;`, found `::` --> $DIR/import-glob-path.rs:2:11 | LL | use foo::*::bar - | ^^ expected `;` + | ^^ expected `;` here error: aborting due to previous error diff --git a/src/test/ui/parser/import-glob-rename.stderr b/src/test/ui/parser/import-glob-rename.stderr index 2853873278251..56f021c29d45f 100644 --- a/src/test/ui/parser/import-glob-rename.stderr +++ b/src/test/ui/parser/import-glob-rename.stderr @@ -1,8 +1,8 @@ -error: expected `;`, found keyword `as` +error: expected `;`, found `as` --> $DIR/import-glob-rename.rs:3:12 | LL | use foo::* as baz; - | ^^ expected `;` + | ^^ expected `;` here 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 00b241b9054cd..6a8b67fefa780 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 18947b8fa401e..b6557163d4520 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/recover-missing-semi.rs b/src/test/ui/parser/recover-missing-semi.rs index 1893dc716bedc..f47d5e6805f88 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 99339e4dd5025..c40918ee2bd5f 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 From 1b0836df6f633eb1a4c02d15a8d942bfe885f172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 22 Oct 2019 11:46:19 -0700 Subject: [PATCH 2/3] Tweak unexpected token wording --- src/libsyntax/parse/parser/diagnostics.rs | 10 +++++----- src/test/ui/async-await/no-async-const.rs | 2 +- src/test/ui/async-await/no-async-const.stderr | 2 +- src/test/ui/async-await/no-unsafe-async.rs | 4 ++-- src/test/ui/async-await/no-unsafe-async.stderr | 4 ++-- src/test/ui/can-begin-expr-check.rs | 2 +- src/test/ui/can-begin-expr-check.stderr | 2 +- src/test/ui/issues/issue-43196.stderr | 2 +- .../extern/keyword-extern-as-identifier-type.stderr | 2 +- src/test/ui/macros/issue-54441.stderr | 2 +- src/test/ui/parser/default.stderr | 2 +- src/test/ui/parser/duplicate-visibility.stderr | 2 +- src/test/ui/parser/extern-expected-fn-or-brace.rs | 2 +- src/test/ui/parser/extern-expected-fn-or-brace.stderr | 2 +- src/test/ui/parser/impl-parsing.stderr | 2 +- src/test/ui/parser/import-from-path.stderr | 2 +- src/test/ui/parser/import-from-rename.stderr | 4 ++-- src/test/ui/parser/import-glob-path.stderr | 2 +- src/test/ui/parser/import-glob-rename.stderr | 4 ++-- src/test/ui/parser/issue-15980.rs | 2 +- src/test/ui/parser/issue-15980.stderr | 2 +- src/test/ui/parser/issue-19398.rs | 2 +- src/test/ui/parser/issue-19398.stderr | 4 ++-- src/test/ui/parser/raw/raw-literal-keywords.rs | 2 +- src/test/ui/parser/raw/raw-literal-keywords.stderr | 2 +- .../ui/parser/recover-for-loop-parens-around-head.rs | 2 +- .../parser/recover-for-loop-parens-around-head.stderr | 2 +- src/test/ui/parser/removed-syntax-static-fn.stderr | 2 +- src/test/ui/parser/removed-syntax-uniq-mut-ty.rs | 3 ++- src/test/ui/parser/removed-syntax-uniq-mut-ty.stderr | 2 +- src/test/ui/parser/underscore_item_not_const.rs | 2 +- src/test/ui/parser/underscore_item_not_const.stderr | 2 +- 32 files changed, 42 insertions(+), 41 deletions(-) diff --git a/src/libsyntax/parse/parser/diagnostics.rs b/src/libsyntax/parse/parser/diagnostics.rs index 677d16a40d9fc..49949b775750d 100644 --- a/src/libsyntax/parse/parser/diagnostics.rs +++ b/src/libsyntax/parse/parser/diagnostics.rs @@ -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); diff --git a/src/test/ui/async-await/no-async-const.rs b/src/test/ui/async-await/no-async-const.rs index 7a6eb498b2ee0..44f02d1a7b19b 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 edbdfb5652281..f89d1810ba449 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 81e0cd799ad70..1ac1bdffda947 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 c339c7c3bf5bf..79d9f1befd66a 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 35aed067c697a..8974d9f48c1e3 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 676c2cb661e7b..0e03e9915fca2 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 32efe23c72b58..4f7ed5cc6fdd8 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 447c76a5bbceb..48c2f556f1dd9 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 af0ee3ae8eca3..9d18d1d517fc4 100644 --- a/src/test/ui/macros/issue-54441.stderr +++ b/src/test/ui/macros/issue-54441.stderr @@ -1,4 +1,4 @@ -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 diff --git a/src/test/ui/parser/default.stderr b/src/test/ui/parser/default.stderr index e199045134e78..8843fd303ec0c 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 { T::default() } diff --git a/src/test/ui/parser/duplicate-visibility.stderr b/src/test/ui/parser/duplicate-visibility.stderr index 880b536cd18c5..675adb88d2084 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 dd46b87fa426f..907de0d8f91d8 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 0fb993553417d..691f4cddff291 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 935e93963e1e6..e929fa53620f6 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/import-from-path.stderr b/src/test/ui/parser/import-from-path.stderr index 84c3b31df20be..5842037fb8050 100644 --- a/src/test/ui/parser/import-from-path.stderr +++ b/src/test/ui/parser/import-from-path.stderr @@ -2,7 +2,7 @@ error: expected `;`, found `::` --> $DIR/import-from-path.rs:2:15 | LL | use foo::{bar}::baz - | ^^ expected `;` here + | ^^ expected `;` error: aborting due to previous error diff --git a/src/test/ui/parser/import-from-rename.stderr b/src/test/ui/parser/import-from-rename.stderr index 53ceb0280f987..a966e9937373a 100644 --- a/src/test/ui/parser/import-from-rename.stderr +++ b/src/test/ui/parser/import-from-rename.stderr @@ -1,8 +1,8 @@ -error: expected `;`, found `as` +error: expected `;`, found keyword `as` --> $DIR/import-from-rename.rs:3:16 | LL | use foo::{bar} as baz; - | ^^ expected `;` here + | ^^ expected `;` error: aborting due to previous error diff --git a/src/test/ui/parser/import-glob-path.stderr b/src/test/ui/parser/import-glob-path.stderr index 44f4fc57a4a95..ebca2db8305c3 100644 --- a/src/test/ui/parser/import-glob-path.stderr +++ b/src/test/ui/parser/import-glob-path.stderr @@ -2,7 +2,7 @@ error: expected `;`, found `::` --> $DIR/import-glob-path.rs:2:11 | LL | use foo::*::bar - | ^^ expected `;` here + | ^^ expected `;` error: aborting due to previous error diff --git a/src/test/ui/parser/import-glob-rename.stderr b/src/test/ui/parser/import-glob-rename.stderr index 56f021c29d45f..2853873278251 100644 --- a/src/test/ui/parser/import-glob-rename.stderr +++ b/src/test/ui/parser/import-glob-rename.stderr @@ -1,8 +1,8 @@ -error: expected `;`, found `as` +error: expected `;`, found keyword `as` --> $DIR/import-glob-rename.rs:3:12 | LL | use foo::* as baz; - | ^^ expected `;` here + | ^^ expected `;` error: aborting due to previous error diff --git a/src/test/ui/parser/issue-15980.rs b/src/test/ui/parser/issue-15980.rs index ba874fb4476f0..beb94c8042d5f 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 47c275110b429..26f75d45fa241 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 822f4a6fde2c7..90221039b4148 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 d5f1f972d553d..41ec4f3ced4e9 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/raw/raw-literal-keywords.rs b/src/test/ui/parser/raw/raw-literal-keywords.rs index 6b055fbb11749..bf9cbcdab2e89 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 f39e29cfaa800..4cea605be6f5e 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 c6be2c90667c2..779e164634478 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 1b5b6cca09243..1a1f395ee213e 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/removed-syntax-static-fn.stderr b/src/test/ui/parser/removed-syntax-static-fn.stderr index 21cb71df65751..af148e697118c 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 79d51f5595d26..f9a9d071a3d1b 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; //~ ERROR expected one of `>`, const, lifetime, or type, found `mut` +type mut_box = Box; +//~^ 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 b6c5749c031e0..9c47e3db67dd3 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; diff --git a/src/test/ui/parser/underscore_item_not_const.rs b/src/test/ui/parser/underscore_item_not_const.rs index 375bdc3a46369..7b0d128f06f8a 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 deb4a012e32ab..8814aa3527153 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 } From e8016c2b13a7e16b6eed9e30b4b6bfe304750566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 24 Oct 2019 15:57:43 -0700 Subject: [PATCH 3/3] review comments --- src/libsyntax/parse/parser/diagnostics.rs | 81 +++++++++---------- src/test/ui/macros/issue-54441.stderr | 2 +- .../parser/macro/trait-non-item-macros.stderr | 2 +- 3 files changed, 41 insertions(+), 44 deletions(-) diff --git a/src/libsyntax/parse/parser/diagnostics.rs b/src/libsyntax/parse/parser/diagnostics.rs index 49949b775750d..ab2b4519cb72a 100644 --- a/src/libsyntax/parse/parser/diagnostics.rs +++ b/src/libsyntax/parse/parser/diagnostics.rs @@ -327,31 +327,27 @@ impl<'a> Parser<'a> { } 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 => { - // 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) @@ -894,7 +890,12 @@ impl<'a> Parser<'a> { let sm = self.sess.source_map(); let msg = format!("expected `;`, found `{}`", self.this_token_descr()); let appl = Applicability::MachineApplicable; - if self.look_ahead(1, |t| t == &token::CloseDelim(token::Brace) + 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 @@ -903,14 +904,12 @@ impl<'a> Parser<'a> { // // let x = 32: // let y = 42; - if sm.is_multiline(self.prev_span.until(self.token.span)) { - 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(()) - } + 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 @@ -921,14 +920,12 @@ impl<'a> Parser<'a> { // // let x = 32 // let y = 42; - if sm.is_multiline(self.prev_span.until(self.token.span)) { - 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(()) - } + 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 } diff --git a/src/test/ui/macros/issue-54441.stderr b/src/test/ui/macros/issue-54441.stderr index 9d18d1d517fc4..287d579c76d5f 100644 --- a/src/test/ui/macros/issue-54441.stderr +++ b/src/test/ui/macros/issue-54441.stderr @@ -2,7 +2,7 @@ error: expected one of `crate`, `fn`, `pub`, `static`, or `type`, found keyword --> $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/macro/trait-non-item-macros.stderr b/src/test/ui/parser/macro/trait-non-item-macros.stderr index 76670e0bde06a..a953e23a710fa 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