]> git.lizzy.rs Git - rust.git/commitdiff
Improves parser diagnostics, fixes #93867
authorthreadexception <hannes.gaumann@outlook.de>
Sun, 1 May 2022 17:05:35 +0000 (19:05 +0200)
committerthreadexception <hannes.gaumann@outlook.de>
Sun, 12 Jun 2022 15:48:52 +0000 (17:48 +0200)
22 files changed:
compiler/rustc_parse/src/parser/diagnostics.rs
compiler/rustc_parse/src/parser/expr.rs
compiler/rustc_parse/src/parser/mod.rs
compiler/rustc_parse/src/parser/path.rs
compiler/rustc_parse/src/parser/stmt.rs
src/test/ui/parser/can-begin-expr-check.rs
src/test/ui/parser/can-begin-expr-check.stderr
src/test/ui/parser/duplicate-visibility.rs
src/test/ui/parser/duplicate-visibility.stderr
src/test/ui/parser/issues/issue-20616-2.rs
src/test/ui/parser/issues/issue-20616-2.stderr
src/test/ui/parser/issues/issue-62660.rs
src/test/ui/parser/issues/issue-62660.stderr
src/test/ui/parser/issues/issue-84117.rs
src/test/ui/parser/issues/issue-84117.stderr
src/test/ui/parser/issues/issue-93282.stderr
src/test/ui/parser/issues/issue-93867.rs [new file with mode: 0644]
src/test/ui/parser/issues/issue-93867.stderr [new file with mode: 0644]
src/test/ui/parser/lifetime-semicolon.fixed
src/test/ui/parser/lifetime-semicolon.rs
src/test/ui/parser/lifetime-semicolon.stderr
src/test/ui/parser/require-parens-for-chained-comparison.stderr

index beffbdc5de4101c3bbd421f4e6433468df5e4f10..d8bca17cce97dc0a561d747cccb1f2610a19b709 100644 (file)
@@ -29,6 +29,7 @@
 
 use std::mem::take;
 
+use crate::parser;
 use tracing::{debug, trace};
 
 const TURBOFISH_SUGGESTION_STR: &str =
@@ -400,6 +401,35 @@ fn tokens_to_string(tokens: &[TokenType]) -> String {
             .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();
index 13e9a5e660fe63f381392e67a9f5a34ca3666aa6..e2d600cc7f98b15e0f50a1346134ccb2219de82e 100644 (file)
@@ -977,12 +977,26 @@ pub(super) fn parse_dot_or_call_expr_with(
 
     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 @@ fn parse_labeled_expr(
             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();
index cd61584a876620c7dddc3a624730599015d7db81..181fbc6c3cfb0a24e38ead71d2a497f2604794a9 100644 (file)
@@ -548,6 +548,22 @@ fn check(&mut self, tok: &TokenKind) -> bool {
         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);
index 5c6fb376cd41a0d51051b4c8a6b27917e5029ce1..c15ef5cae674c823ce510b8fef96030d44c3b0d1 100644 (file)
@@ -2,7 +2,7 @@
 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 @@ pub(super) fn parse_qpath(&mut self, style: PathStyle) -> PResult<'a, (QSelf, Pa
     ///                ^ 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 @@ pub(super) fn parse_angle_args(
         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 @@ fn parse_angle_arg(
         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 @@ fn parse_angle_arg(
                         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)))
                 }
             }
index ac693597662e6e55c27ca68c7fbabcfa5cd958bf..f18f7222b7ab62c2b7c16fec29dd8e2738dd2a38 100644 (file)
@@ -262,7 +262,10 @@ fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> {
                     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 {
index 8974d9f48c1e3076d759d036ae102e13bc568a35..e5be8de79a958461eeb0e711b39f36afc6d46bbe 100644 (file)
@@ -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`
 }
index d674fc36bc2bdcf7429336ee63c41f17fd161b51..9569ababad8d52995f6d1674adcf09882b8217f8 100644 (file)
@@ -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
 
index 32aeee294728a74b59e55cc97688824f2933fa2d..54955944c7d35e0d72207f7f7db8e94a916e07f8 100644 (file)
@@ -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
index 97144ac2f642d3b7672f402a9ebda101d321a9c9..8ecebf01f17a3f4758f3553966ecdeb621fd03ab 100644 (file)
@@ -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 | }
index f108ae5de1483cf219ebe129b9f8a58472dce51c..2f2c6903a9f4c515acf71cf45a2375a90fc67aba 100644 (file)
@@ -9,7 +9,7 @@
 //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<T> = Box<T,,>; // error: expected type, found `,`
index 13e6aa7d605c773278f1b82da3d0493c43b553a8..42059685c5cc42b41aa295f04791759fd36074c3 100644 (file)
@@ -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
    |
index 4f866b78976612a1e0ebaba7f5efbc64ec4d614e..33c8a9fa328ca2ee696ec3e88907f6f14c1f0069 100644 (file)
@@ -5,7 +5,7 @@
 
 impl Foo {
     pub fn foo(_: i32, self: Box<Self) {}
-    //~^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, `:`, `<`, `=`, or `>`, found `)`
+    //~^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `)`
 }
 
 fn main() {}
index be0b9a524df84bf5dd1adc14e07e2230d0ab7196..14c0bdcb111b8a53768ffb334b1ae90da65c48aa 100644 (file)
@@ -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<Self) {}
-   |                                      ^ expected one of 9 possible tokens
+   |                                      ^ expected one of 7 possible tokens
    |
 help: you might have meant to end the type parameters here
    |
index 919585877cf9cfa899c7e234188c72a108f0c97c..c9ebf133588bfdd774aa9ef980b3c5a8e2e1be3d 100644 (file)
@@ -6,4 +6,4 @@ fn main() {
     //~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
     //~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
 }
-//~^ ERROR expected one of `,`, `:`, `=`, or `>`, found `}`
+//~^ ERROR expected one of `,` or `>`, found `}`
index a2407affeef8d0095d3af9f0f73c19ca887eba58..237bc11bd060ddfc37d86df2af234a99a0d6d23f 100644 (file)
@@ -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
index 900f21a7ccef461ae7465a9c8f3d846439dadd5e..ee554784b3a247c4772751f52a90e21aa338aa4a 100644 (file)
@@ -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 (file)
index 0000000..5074479
--- /dev/null
@@ -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 (file)
index 0000000..ee0cb4e
--- /dev/null
@@ -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
+
index 89e87fe99885e1a545b40bc9582e6a1b10e5d7d1..482b7704695c371ef705df2b891ae9cabf98b9e7 100644 (file)
@@ -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() {}
index 744c93fc7c767cc73830d25244d02416f7960c00..21c8b0a7f88b3d49e12a1112bb9a644121798152 100644 (file)
@@ -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() {}
index 5de7a5f2d5dede8a8495290d5c116780b181eb8d..ee486c2366c9537d412e85921a69e198ea280132 100644 (file)
@@ -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
    |
index 92d700753dc1b24665bce4e45eb9e056f363e215..0bf52854ec206d64717710fae077a9c72d69fc32 100644 (file)
@@ -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
    |