diff options
| author | threadexception <hannes.gaumann@outlook.de> | 2022-05-01 19:05:35 +0200 |
|---|---|---|
| committer | threadexception <hannes.gaumann@outlook.de> | 2022-06-12 17:48:52 +0200 |
| commit | 21fdd549f63499a6f15160c22175cc9c3bbeb473 (patch) | |
| tree | 6acdecfc403eaa4a08351197055753939932beda /compiler/rustc_parse/src/parser | |
| parent | d201c812d40932509b2b5307c0b20c1ce78d21da (diff) | |
| download | rust-21fdd549f63499a6f15160c22175cc9c3bbeb473.tar.gz rust-21fdd549f63499a6f15160c22175cc9c3bbeb473.zip | |
Improves parser diagnostics, fixes #93867
Diffstat (limited to 'compiler/rustc_parse/src/parser')
| -rw-r--r-- | compiler/rustc_parse/src/parser/diagnostics.rs | 30 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 26 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/mod.rs | 16 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/path.rs | 20 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/stmt.rs | 5 |
5 files changed, 88 insertions, 9 deletions
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index beffbdc5de4..d8bca17cce9 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::<Vec<_>>(); 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 13e9a5e660f..e2d600cc7f9 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<Expr>, lo: Span) -> PResult<'a, P<Expr>> { 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 cd61584a876..181fbc6c3cf 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 5c6fb376cd4..c15ef5cae67 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 ac693597662..f18f7222b7a 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 { |
