diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index beffbdc5de410..d8bca17cce97d 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -29,6 +29,7 @@ use std::ops::{Deref, DerefMut}; use std::mem::take; +use crate::parser; use tracing::{debug, trace}; const TURBOFISH_SUGGESTION_STR: &str = @@ -400,6 +401,35 @@ impl<'a> Parser<'a> { .map(|x| TokenType::Token(x.clone())) .chain(inedible.iter().map(|x| TokenType::Token(x.clone()))) .chain(self.expected_tokens.iter().cloned()) + .filter_map(|token| { + // filter out suggestions which suggest the same token which was found and deemed incorrect + fn is_ident_eq_keyword(found: &TokenKind, expected: &TokenType) -> bool { + if let TokenKind::Ident(current_sym, _) = found { + if let TokenType::Keyword(suggested_sym) = expected { + return current_sym == suggested_sym; + } + } + false + } + if token != parser::TokenType::Token(self.token.kind.clone()) { + let eq = is_ident_eq_keyword(&self.token.kind, &token); + // if the suggestion is a keyword and the found token is an ident, + // the content of which are equal to the suggestion's content, + // we can remove that suggestion (see the return None statement below) + + // if this isn't the case however, and the suggestion is a token the + // content of which is the same as the found token's, we remove it as well + if !eq { + if let TokenType::Token(kind) = &token { + if kind == &self.token.kind { + return None; + } + } + return Some(token); + } + } + return None; + }) .collect::>(); expected.sort_by_cached_key(|x| x.to_string()); expected.dedup(); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 13e9a5e660fe6..e2d600cc7f98b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -977,12 +977,26 @@ impl<'a> Parser<'a> { fn parse_dot_or_call_expr_with_(&mut self, mut e: P, lo: Span) -> PResult<'a, P> { loop { - if self.eat(&token::Question) { + let has_question = if self.prev_token.kind == TokenKind::Ident(kw::Return, false) { + // we are using noexpect here because we don't expect a `?` directly after a `return` + // which could be suggested otherwise + self.eat_noexpect(&token::Question) + } else { + self.eat(&token::Question) + }; + if has_question { // `expr?` e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e), AttrVec::new()); continue; } - if self.eat(&token::Dot) { + let has_dot = if self.prev_token.kind == TokenKind::Ident(kw::Return, false) { + // we are using noexpect here because we don't expect a `.` directly after a `return` + // which could be suggested otherwise + self.eat_noexpect(&token::Dot) + } else { + self.eat(&token::Dot) + }; + if has_dot { // expr.f e = self.parse_dot_suffix_expr(lo, e)?; continue; @@ -1536,9 +1550,13 @@ impl<'a> Parser<'a> { self.parse_for_expr(label, lo, attrs) } else if self.eat_keyword(kw::Loop) { self.parse_loop_expr(label, lo, attrs) - } else if self.check(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() { + } else if self.check_noexpect(&token::OpenDelim(Delimiter::Brace)) + || self.token.is_whole_block() + { self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs) - } else if !ate_colon && (self.check(&TokenKind::Comma) || self.check(&TokenKind::Gt)) { + } else if !ate_colon + && (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt)) + { // We're probably inside of a `Path<'a>` that needs a turbofish let msg = "expected `while`, `for`, `loop` or `{` after a label"; self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit(); diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index cd61584a87662..181fbc6c3cfb0 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -548,6 +548,22 @@ impl<'a> Parser<'a> { is_present } + fn check_noexpect(&self, tok: &TokenKind) -> bool { + self.token == *tok + } + + /// Consumes a token 'tok' if it exists. Returns whether the given token was present. + /// + /// the main purpose of this function is to reduce the cluttering of the suggestions list + /// which using the normal eat method could introduce in some cases. + pub fn eat_noexpect(&mut self, tok: &TokenKind) -> bool { + let is_present = self.check_noexpect(tok); + if is_present { + self.bump() + } + is_present + } + /// Consumes a token 'tok' if it exists. Returns whether the given token was present. pub fn eat(&mut self, tok: &TokenKind) -> bool { let is_present = self.check(tok); diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 5c6fb376cd41a..c15ef5cae674c 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -2,7 +2,7 @@ use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{Parser, Restrictions, TokenType}; use crate::maybe_whole; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, Token}; +use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::{ self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocConstraint, AssocConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs, @@ -96,7 +96,7 @@ impl<'a> Parser<'a> { /// ^ help: use double colon /// ``` fn recover_colon_before_qpath_proj(&mut self) -> bool { - if self.token.kind != token::Colon + if !self.check_noexpect(&TokenKind::Colon) || self.look_ahead(1, |t| !t.is_ident() || t.is_reserved_ident()) { return false; @@ -478,7 +478,7 @@ impl<'a> Parser<'a> { while let Some(arg) = self.parse_angle_arg(ty_generics)? { args.push(arg); if !self.eat(&token::Comma) { - if self.token.kind == token::Semi + if self.check_noexpect(&TokenKind::Semi) && self.look_ahead(1, |t| t.is_ident() || t.is_lifetime()) { // Add `>` to the list of expected tokens. @@ -517,7 +517,11 @@ impl<'a> Parser<'a> { let arg = self.parse_generic_arg(ty_generics)?; match arg { Some(arg) => { - if self.check(&token::Colon) | self.check(&token::Eq) { + // we are using noexpect here because we first want to find out if either `=` or `:` + // is present and then use that info to push the other token onto the tokens list + let separated = + self.check_noexpect(&token::Colon) || self.check_noexpect(&token::Eq); + if separated && (self.check(&token::Colon) | self.check(&token::Eq)) { let arg_span = arg.span(); let (binder, ident, gen_args) = match self.get_ident_from_generic_arg(&arg) { Ok(ident_gen_args) => ident_gen_args, @@ -553,6 +557,14 @@ impl<'a> Parser<'a> { AssocConstraint { id: ast::DUMMY_NODE_ID, ident, gen_args, kind, span }; Ok(Some(AngleBracketedArg::Constraint(constraint))) } else { + // we only want to suggest `:` and `=` in contexts where the previous token + // is an ident and the current token or the next token is an ident + if self.prev_token.is_ident() + && (self.token.is_ident() || self.look_ahead(1, |token| token.is_ident())) + { + self.check(&token::Colon); + self.check(&token::Eq); + } Ok(Some(AngleBracketedArg::Arg(arg))) } } diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index ac693597662e6..f18f7222b7ab6 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -262,7 +262,10 @@ impl<'a> Parser<'a> { if let Ok(snip) = self.span_to_snippet(pat.span) { err.span_label(pat.span, format!("while parsing the type for `{}`", snip)); } - let err = if self.check(&token::Eq) { + // we use noexpect here because we don't actually expect Eq to be here + // but we are still checking for it in order to be able to handle it if + // it is there + let err = if self.check_noexpect(&token::Eq) { err.emit(); None } else { diff --git a/src/test/ui/parser/can-begin-expr-check.rs b/src/test/ui/parser/can-begin-expr-check.rs index 8974d9f48c1e3..e5be8de79a958 100644 --- a/src/test/ui/parser/can-begin-expr-check.rs +++ b/src/test/ui/parser/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 keyword `enum` + return enum; //~ ERROR expected one of `;`, `}`, or an operator, found keyword `enum` } diff --git a/src/test/ui/parser/can-begin-expr-check.stderr b/src/test/ui/parser/can-begin-expr-check.stderr index d674fc36bc2bd..9569ababad8d5 100644 --- a/src/test/ui/parser/can-begin-expr-check.stderr +++ b/src/test/ui/parser/can-begin-expr-check.stderr @@ -1,8 +1,8 @@ -error: expected one of `.`, `;`, `?`, `}`, or an operator, found keyword `enum` +error: expected one of `;`, `}`, or an operator, found keyword `enum` --> $DIR/can-begin-expr-check.rs:19:12 | LL | return enum; - | ^^^^ expected one of `.`, `;`, `?`, `}`, or an operator + | ^^^^ expected one of `;`, `}`, or an operator error: aborting due to previous error diff --git a/src/test/ui/parser/duplicate-visibility.rs b/src/test/ui/parser/duplicate-visibility.rs index 32aeee294728a..54955944c7d35 100644 --- a/src/test/ui/parser/duplicate-visibility.rs +++ b/src/test/ui/parser/duplicate-visibility.rs @@ -2,8 +2,8 @@ fn main() {} extern "C" { //~ NOTE while parsing this item list starting here pub pub fn foo(); - //~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found keyword `pub` - //~| NOTE expected one of 9 possible tokens + //~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `unsafe`, or `use`, found keyword `pub` + //~| NOTE expected one of 8 possible tokens //~| HELP there is already a visibility modifier, remove one //~| NOTE explicit visibility first seen here } //~ NOTE the item list ends here diff --git a/src/test/ui/parser/duplicate-visibility.stderr b/src/test/ui/parser/duplicate-visibility.stderr index 97144ac2f642d..8ecebf01f17a3 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 `(`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found keyword `pub` +error: expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `unsafe`, or `use`, found keyword `pub` --> $DIR/duplicate-visibility.rs:4:9 | LL | extern "C" { @@ -6,7 +6,7 @@ LL | extern "C" { LL | pub pub fn foo(); | ^^^ | | - | expected one of 9 possible tokens + | expected one of 8 possible tokens | help: there is already a visibility modifier, remove one ... LL | } diff --git a/src/test/ui/parser/issues/issue-20616-2.rs b/src/test/ui/parser/issues/issue-20616-2.rs index f108ae5de1483..2f2c6903a9f4c 100644 --- a/src/test/ui/parser/issues/issue-20616-2.rs +++ b/src/test/ui/parser/issues/issue-20616-2.rs @@ -9,7 +9,7 @@ type Type_1_<'a, T> = &'a T; //type Type_1<'a T> = &'a T; // error: expected `,` or `>` after lifetime name, found `T` -type Type_2 = Type_1_<'static ()>; //~ error: expected one of `,`, `:`, `=`, or `>`, found `(` +type Type_2 = Type_1_<'static ()>; //~ error: expected one of `,` or `>`, found `(` //type Type_3 = Box; // error: expected type, found `,` diff --git a/src/test/ui/parser/issues/issue-20616-2.stderr b/src/test/ui/parser/issues/issue-20616-2.stderr index 13e6aa7d605c7..42059685c5cc4 100644 --- a/src/test/ui/parser/issues/issue-20616-2.stderr +++ b/src/test/ui/parser/issues/issue-20616-2.stderr @@ -1,8 +1,8 @@ -error: expected one of `,`, `:`, `=`, or `>`, found `(` +error: expected one of `,` or `>`, found `(` --> $DIR/issue-20616-2.rs:12:31 | LL | type Type_2 = Type_1_<'static ()>; - | ^ expected one of `,`, `:`, `=`, or `>` + | ^ expected one of `,` or `>` | help: you might have meant to end the type parameters here | diff --git a/src/test/ui/parser/issues/issue-62660.rs b/src/test/ui/parser/issues/issue-62660.rs index 4f866b7897661..33c8a9fa328ca 100644 --- a/src/test/ui/parser/issues/issue-62660.rs +++ b/src/test/ui/parser/issues/issue-62660.rs @@ -5,7 +5,7 @@ struct Foo; impl Foo { pub fn foo(_: i32, self: Box`, found `)` + //~^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `)` } fn main() {} diff --git a/src/test/ui/parser/issues/issue-62660.stderr b/src/test/ui/parser/issues/issue-62660.stderr index be0b9a524df84..14c0bdcb111b8 100644 --- a/src/test/ui/parser/issues/issue-62660.stderr +++ b/src/test/ui/parser/issues/issue-62660.stderr @@ -1,8 +1,8 @@ -error: expected one of `!`, `(`, `+`, `,`, `::`, `:`, `<`, `=`, or `>`, found `)` +error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `)` --> $DIR/issue-62660.rs:7:38 | LL | pub fn foo(_: i32, self: Box`, found `}` +//~^ ERROR expected one of `,` or `>`, found `}` diff --git a/src/test/ui/parser/issues/issue-84117.stderr b/src/test/ui/parser/issues/issue-84117.stderr index a2407affeef8d..237bc11bd060d 100644 --- a/src/test/ui/parser/issues/issue-84117.stderr +++ b/src/test/ui/parser/issues/issue-84117.stderr @@ -21,11 +21,11 @@ error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, fo LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } | ^ expected one of 8 possible tokens -error: expected one of `,`, `:`, `=`, or `>`, found `}` +error: expected one of `,` or `>`, found `}` --> $DIR/issue-84117.rs:8:1 | LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } - | ----------- while parsing the type for `outer_local` - expected one of `,`, `:`, `=`, or `>` + | ----------- while parsing the type for `outer_local` - expected one of `,` or `>` ... LL | } | ^ unexpected token diff --git a/src/test/ui/parser/issues/issue-93282.stderr b/src/test/ui/parser/issues/issue-93282.stderr index 900f21a7ccef4..ee554784b3a24 100644 --- a/src/test/ui/parser/issues/issue-93282.stderr +++ b/src/test/ui/parser/issues/issue-93282.stderr @@ -4,11 +4,11 @@ error: expected `while`, `for`, `loop` or `{` after a label LL | f<'a,> | ^ expected `while`, `for`, `loop` or `{` after a label -error: expected one of `.`, `:`, `;`, `?`, `for`, `loop`, `while`, `{`, `}`, or an operator, found `,` +error: expected one of `.`, `:`, `;`, `?`, `for`, `loop`, `while`, `}`, or an operator, found `,` --> $DIR/issue-93282.rs:2:9 | LL | f<'a,> - | ^ expected one of 10 possible tokens + | ^ expected one of 9 possible tokens | help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments | diff --git a/src/test/ui/parser/issues/issue-93867.rs b/src/test/ui/parser/issues/issue-93867.rs new file mode 100644 index 0000000000000..507447923915b --- /dev/null +++ b/src/test/ui/parser/issues/issue-93867.rs @@ -0,0 +1,10 @@ +pub struct Entry<'a, K, V> { + k: &'a mut K, + v: V, +} + +pub fn entry<'a, K, V>() -> Entry<'a K, V> { +// ^ missing comma +//~^^ expected one of `,` or `>`, found `K` + unimplemented!() +} diff --git a/src/test/ui/parser/issues/issue-93867.stderr b/src/test/ui/parser/issues/issue-93867.stderr new file mode 100644 index 0000000000000..ee0cb4efd74c1 --- /dev/null +++ b/src/test/ui/parser/issues/issue-93867.stderr @@ -0,0 +1,13 @@ +error: expected one of `,` or `>`, found `K` + --> $DIR/issue-93867.rs:6:38 + | +LL | pub fn entry<'a, K, V>() -> Entry<'a K, V> { + | ^ expected one of `,` or `>` + | +help: you might have meant to end the type parameters here + | +LL | pub fn entry<'a, K, V>() -> Entry<'a> K, V> { + | + + +error: aborting due to previous error + diff --git a/src/test/ui/parser/lifetime-semicolon.fixed b/src/test/ui/parser/lifetime-semicolon.fixed index 89e87fe99885e..482b7704695c3 100644 --- a/src/test/ui/parser/lifetime-semicolon.fixed +++ b/src/test/ui/parser/lifetime-semicolon.fixed @@ -5,6 +5,6 @@ struct Foo<'a, 'b> { } fn foo<'a, 'b>(_x: &mut Foo<'a, 'b>) {} -//~^ ERROR expected one of `,`, `:`, `=`, or `>`, found `;` +//~^ ERROR expected one of `,` or `>`, found `;` fn main() {} diff --git a/src/test/ui/parser/lifetime-semicolon.rs b/src/test/ui/parser/lifetime-semicolon.rs index 744c93fc7c767..21c8b0a7f88b3 100644 --- a/src/test/ui/parser/lifetime-semicolon.rs +++ b/src/test/ui/parser/lifetime-semicolon.rs @@ -5,6 +5,6 @@ struct Foo<'a, 'b> { } fn foo<'a, 'b>(_x: &mut Foo<'a; 'b>) {} -//~^ ERROR expected one of `,`, `:`, `=`, or `>`, found `;` +//~^ ERROR expected one of `,` or `>`, found `;` fn main() {} diff --git a/src/test/ui/parser/lifetime-semicolon.stderr b/src/test/ui/parser/lifetime-semicolon.stderr index 5de7a5f2d5ded..ee486c2366c95 100644 --- a/src/test/ui/parser/lifetime-semicolon.stderr +++ b/src/test/ui/parser/lifetime-semicolon.stderr @@ -1,8 +1,8 @@ -error: expected one of `,`, `:`, `=`, or `>`, found `;` +error: expected one of `,` or `>`, found `;` --> $DIR/lifetime-semicolon.rs:7:31 | LL | fn foo<'a, 'b>(_x: &mut Foo<'a; 'b>) {} - | ^ expected one of `,`, `:`, `=`, or `>` + | ^ expected one of `,` or `>` | help: use a comma to separate type parameters | diff --git a/src/test/ui/parser/require-parens-for-chained-comparison.stderr b/src/test/ui/parser/require-parens-for-chained-comparison.stderr index 92d700753dc1b..0bf52854ec206 100644 --- a/src/test/ui/parser/require-parens-for-chained-comparison.stderr +++ b/src/test/ui/parser/require-parens-for-chained-comparison.stderr @@ -59,11 +59,11 @@ error: expected `while`, `for`, `loop` or `{` after a label LL | let _ = f<'_, i8>(); | ^ expected `while`, `for`, `loop` or `{` after a label -error: expected one of `.`, `:`, `;`, `?`, `else`, `for`, `loop`, `while`, `{`, or an operator, found `,` +error: expected one of `.`, `:`, `;`, `?`, `else`, `for`, `loop`, `while`, or an operator, found `,` --> $DIR/require-parens-for-chained-comparison.rs:22:17 | LL | let _ = f<'_, i8>(); - | ^ expected one of 10 possible tokens + | ^ expected one of 9 possible tokens | help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments |