Skip to content

Commit e3a3c00

Browse files
authored
Rollup merge of rust-lang#95211 - terrarier2111:improve-parser, r=compiler-errors
Improve parser diagnostics This pr fixes rust-lang#93867 and contains a couple of diagnostics related changes to the parser. Here is a short list with some of the changes: - don't suggest the same thing that is the current token - suggest removing the current token if the following token is one of the suggestions (maybe incorrect) - tell the user to put a type or lifetime after where if there is none (as a warning) - reduce the amount of tokens suggested (via the new eat_noexpect and check_noexpect methods) If any of these changes are undesirable, i can remove them, thanks!
2 parents ca122c7 + 21fdd54 commit e3a3c00

22 files changed

+135
-33
lines changed

compiler/rustc_parse/src/parser/diagnostics.rs

+30
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use std::ops::{Deref, DerefMut};
2828

2929
use std::mem::take;
3030

31+
use crate::parser;
3132
use tracing::{debug, trace};
3233

3334
const TURBOFISH_SUGGESTION_STR: &str =
@@ -481,6 +482,35 @@ impl<'a> Parser<'a> {
481482
.map(|x| TokenType::Token(x.clone()))
482483
.chain(inedible.iter().map(|x| TokenType::Token(x.clone())))
483484
.chain(self.expected_tokens.iter().cloned())
485+
.filter_map(|token| {
486+
// filter out suggestions which suggest the same token which was found and deemed incorrect
487+
fn is_ident_eq_keyword(found: &TokenKind, expected: &TokenType) -> bool {
488+
if let TokenKind::Ident(current_sym, _) = found {
489+
if let TokenType::Keyword(suggested_sym) = expected {
490+
return current_sym == suggested_sym;
491+
}
492+
}
493+
false
494+
}
495+
if token != parser::TokenType::Token(self.token.kind.clone()) {
496+
let eq = is_ident_eq_keyword(&self.token.kind, &token);
497+
// if the suggestion is a keyword and the found token is an ident,
498+
// the content of which are equal to the suggestion's content,
499+
// we can remove that suggestion (see the return None statement below)
500+
501+
// if this isn't the case however, and the suggestion is a token the
502+
// content of which is the same as the found token's, we remove it as well
503+
if !eq {
504+
if let TokenType::Token(kind) = &token {
505+
if kind == &self.token.kind {
506+
return None;
507+
}
508+
}
509+
return Some(token);
510+
}
511+
}
512+
return None;
513+
})
484514
.collect::<Vec<_>>();
485515
expected.sort_by_cached_key(|x| x.to_string());
486516
expected.dedup();

compiler/rustc_parse/src/parser/expr.rs

+22-4
Original file line numberDiff line numberDiff line change
@@ -980,12 +980,26 @@ impl<'a> Parser<'a> {
980980

981981
fn parse_dot_or_call_expr_with_(&mut self, mut e: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
982982
loop {
983-
if self.eat(&token::Question) {
983+
let has_question = if self.prev_token.kind == TokenKind::Ident(kw::Return, false) {
984+
// we are using noexpect here because we don't expect a `?` directly after a `return`
985+
// which could be suggested otherwise
986+
self.eat_noexpect(&token::Question)
987+
} else {
988+
self.eat(&token::Question)
989+
};
990+
if has_question {
984991
// `expr?`
985992
e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e), AttrVec::new());
986993
continue;
987994
}
988-
if self.eat(&token::Dot) {
995+
let has_dot = if self.prev_token.kind == TokenKind::Ident(kw::Return, false) {
996+
// we are using noexpect here because we don't expect a `.` directly after a `return`
997+
// which could be suggested otherwise
998+
self.eat_noexpect(&token::Dot)
999+
} else {
1000+
self.eat(&token::Dot)
1001+
};
1002+
if has_dot {
9891003
// expr.f
9901004
e = self.parse_dot_suffix_expr(lo, e)?;
9911005
continue;
@@ -1541,9 +1555,13 @@ impl<'a> Parser<'a> {
15411555
self.parse_for_expr(label, lo, attrs)
15421556
} else if self.eat_keyword(kw::Loop) {
15431557
self.parse_loop_expr(label, lo, attrs)
1544-
} else if self.check(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() {
1558+
} else if self.check_noexpect(&token::OpenDelim(Delimiter::Brace))
1559+
|| self.token.is_whole_block()
1560+
{
15451561
self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs)
1546-
} else if !ate_colon && (self.check(&TokenKind::Comma) || self.check(&TokenKind::Gt)) {
1562+
} else if !ate_colon
1563+
&& (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt))
1564+
{
15471565
// We're probably inside of a `Path<'a>` that needs a turbofish
15481566
let msg = "expected `while`, `for`, `loop` or `{` after a label";
15491567
self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit();

compiler/rustc_parse/src/parser/mod.rs

+16
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,22 @@ impl<'a> Parser<'a> {
547547
is_present
548548
}
549549

550+
fn check_noexpect(&self, tok: &TokenKind) -> bool {
551+
self.token == *tok
552+
}
553+
554+
/// Consumes a token 'tok' if it exists. Returns whether the given token was present.
555+
///
556+
/// the main purpose of this function is to reduce the cluttering of the suggestions list
557+
/// which using the normal eat method could introduce in some cases.
558+
pub fn eat_noexpect(&mut self, tok: &TokenKind) -> bool {
559+
let is_present = self.check_noexpect(tok);
560+
if is_present {
561+
self.bump()
562+
}
563+
is_present
564+
}
565+
550566
/// Consumes a token 'tok' if it exists. Returns whether the given token was present.
551567
pub fn eat(&mut self, tok: &TokenKind) -> bool {
552568
let is_present = self.check(tok);

compiler/rustc_parse/src/parser/path.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
22
use super::{Parser, Restrictions, TokenType};
33
use crate::maybe_whole;
44
use rustc_ast::ptr::P;
5-
use rustc_ast::token::{self, Delimiter, Token};
5+
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
66
use rustc_ast::{
77
self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocConstraint,
88
AssocConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs,
@@ -96,7 +96,7 @@ impl<'a> Parser<'a> {
9696
/// ^ help: use double colon
9797
/// ```
9898
fn recover_colon_before_qpath_proj(&mut self) -> bool {
99-
if self.token.kind != token::Colon
99+
if !self.check_noexpect(&TokenKind::Colon)
100100
|| self.look_ahead(1, |t| !t.is_ident() || t.is_reserved_ident())
101101
{
102102
return false;
@@ -478,7 +478,7 @@ impl<'a> Parser<'a> {
478478
while let Some(arg) = self.parse_angle_arg(ty_generics)? {
479479
args.push(arg);
480480
if !self.eat(&token::Comma) {
481-
if self.token.kind == token::Semi
481+
if self.check_noexpect(&TokenKind::Semi)
482482
&& self.look_ahead(1, |t| t.is_ident() || t.is_lifetime())
483483
{
484484
// Add `>` to the list of expected tokens.
@@ -517,7 +517,11 @@ impl<'a> Parser<'a> {
517517
let arg = self.parse_generic_arg(ty_generics)?;
518518
match arg {
519519
Some(arg) => {
520-
if self.check(&token::Colon) | self.check(&token::Eq) {
520+
// we are using noexpect here because we first want to find out if either `=` or `:`
521+
// is present and then use that info to push the other token onto the tokens list
522+
let separated =
523+
self.check_noexpect(&token::Colon) || self.check_noexpect(&token::Eq);
524+
if separated && (self.check(&token::Colon) | self.check(&token::Eq)) {
521525
let arg_span = arg.span();
522526
let (binder, ident, gen_args) = match self.get_ident_from_generic_arg(&arg) {
523527
Ok(ident_gen_args) => ident_gen_args,
@@ -553,6 +557,14 @@ impl<'a> Parser<'a> {
553557
AssocConstraint { id: ast::DUMMY_NODE_ID, ident, gen_args, kind, span };
554558
Ok(Some(AngleBracketedArg::Constraint(constraint)))
555559
} else {
560+
// we only want to suggest `:` and `=` in contexts where the previous token
561+
// is an ident and the current token or the next token is an ident
562+
if self.prev_token.is_ident()
563+
&& (self.token.is_ident() || self.look_ahead(1, |token| token.is_ident()))
564+
{
565+
self.check(&token::Colon);
566+
self.check(&token::Eq);
567+
}
556568
Ok(Some(AngleBracketedArg::Arg(arg)))
557569
}
558570
}

compiler/rustc_parse/src/parser/stmt.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,10 @@ impl<'a> Parser<'a> {
260260
if let Ok(snip) = self.span_to_snippet(pat.span) {
261261
err.span_label(pat.span, format!("while parsing the type for `{}`", snip));
262262
}
263-
let err = if self.check(&token::Eq) {
263+
// we use noexpect here because we don't actually expect Eq to be here
264+
// but we are still checking for it in order to be able to handle it if
265+
// it is there
266+
let err = if self.check_noexpect(&token::Eq) {
264267
err.emit();
265268
None
266269
} else {

src/test/ui/parser/can-begin-expr-check.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ pub fn main() {
1616
return break as ();
1717
}
1818

19-
return enum; //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found keyword `enum`
19+
return enum; //~ ERROR expected one of `;`, `}`, or an operator, found keyword `enum`
2020
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected one of `.`, `;`, `?`, `}`, or an operator, found keyword `enum`
1+
error: expected one of `;`, `}`, or an operator, found keyword `enum`
22
--> $DIR/can-begin-expr-check.rs:19:12
33
|
44
LL | return enum;
5-
| ^^^^ expected one of `.`, `;`, `?`, `}`, or an operator
5+
| ^^^^ expected one of `;`, `}`, or an operator
66

77
error: aborting due to previous error
88

src/test/ui/parser/duplicate-visibility.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ fn main() {}
22

33
extern "C" { //~ NOTE while parsing this item list starting here
44
pub pub fn foo();
5-
//~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found keyword `pub`
6-
//~| NOTE expected one of 9 possible tokens
5+
//~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `unsafe`, or `use`, found keyword `pub`
6+
//~| NOTE expected one of 8 possible tokens
77
//~| HELP there is already a visibility modifier, remove one
88
//~| NOTE explicit visibility first seen here
99
} //~ NOTE the item list ends here

src/test/ui/parser/duplicate-visibility.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
error: expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found keyword `pub`
1+
error: expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `unsafe`, or `use`, found keyword `pub`
22
--> $DIR/duplicate-visibility.rs:4:9
33
|
44
LL | extern "C" {
55
| - while parsing this item list starting here
66
LL | pub pub fn foo();
77
| ^^^
88
| |
9-
| expected one of 9 possible tokens
9+
| expected one of 8 possible tokens
1010
| help: there is already a visibility modifier, remove one
1111
...
1212
LL | }

src/test/ui/parser/issues/issue-20616-2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ type Type_1_<'a, T> = &'a T;
99
//type Type_1<'a T> = &'a T; // error: expected `,` or `>` after lifetime name, found `T`
1010

1111

12-
type Type_2 = Type_1_<'static ()>; //~ error: expected one of `,`, `:`, `=`, or `>`, found `(`
12+
type Type_2 = Type_1_<'static ()>; //~ error: expected one of `,` or `>`, found `(`
1313

1414

1515
//type Type_3<T> = Box<T,,>; // error: expected type, found `,`

src/test/ui/parser/issues/issue-20616-2.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected one of `,`, `:`, `=`, or `>`, found `(`
1+
error: expected one of `,` or `>`, found `(`
22
--> $DIR/issue-20616-2.rs:12:31
33
|
44
LL | type Type_2 = Type_1_<'static ()>;
5-
| ^ expected one of `,`, `:`, `=`, or `>`
5+
| ^ expected one of `,` or `>`
66
|
77
help: you might have meant to end the type parameters here
88
|

src/test/ui/parser/issues/issue-62660.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ struct Foo;
55

66
impl Foo {
77
pub fn foo(_: i32, self: Box<Self) {}
8-
//~^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, `:`, `<`, `=`, or `>`, found `)`
8+
//~^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `)`
99
}
1010

1111
fn main() {}

src/test/ui/parser/issues/issue-62660.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected one of `!`, `(`, `+`, `,`, `::`, `:`, `<`, `=`, or `>`, found `)`
1+
error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `)`
22
--> $DIR/issue-62660.rs:7:38
33
|
44
LL | pub fn foo(_: i32, self: Box<Self) {}
5-
| ^ expected one of 9 possible tokens
5+
| ^ expected one of 7 possible tokens
66
|
77
help: you might have meant to end the type parameters here
88
|

src/test/ui/parser/issues/issue-84117.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ fn main() {
66
//~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
77
//~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
88
}
9-
//~^ ERROR expected one of `,`, `:`, `=`, or `>`, found `}`
9+
//~^ ERROR expected one of `,` or `>`, found `}`

src/test/ui/parser/issues/issue-84117.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, fo
2121
LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
2222
| ^ expected one of 8 possible tokens
2323

24-
error: expected one of `,`, `:`, `=`, or `>`, found `}`
24+
error: expected one of `,` or `>`, found `}`
2525
--> $DIR/issue-84117.rs:8:1
2626
|
2727
LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
28-
| ----------- while parsing the type for `outer_local` - expected one of `,`, `:`, `=`, or `>`
28+
| ----------- while parsing the type for `outer_local` - expected one of `,` or `>`
2929
...
3030
LL | }
3131
| ^ unexpected token

src/test/ui/parser/issues/issue-93282.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ error: expected `while`, `for`, `loop` or `{` after a label
44
LL | f<'a,>
55
| ^ expected `while`, `for`, `loop` or `{` after a label
66

7-
error: expected one of `.`, `:`, `;`, `?`, `for`, `loop`, `while`, `{`, `}`, or an operator, found `,`
7+
error: expected one of `.`, `:`, `;`, `?`, `for`, `loop`, `while`, `}`, or an operator, found `,`
88
--> $DIR/issue-93282.rs:2:9
99
|
1010
LL | f<'a,>
11-
| ^ expected one of 10 possible tokens
11+
| ^ expected one of 9 possible tokens
1212
|
1313
help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
1414
|
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
pub struct Entry<'a, K, V> {
2+
k: &'a mut K,
3+
v: V,
4+
}
5+
6+
pub fn entry<'a, K, V>() -> Entry<'a K, V> {
7+
// ^ missing comma
8+
//~^^ expected one of `,` or `>`, found `K`
9+
unimplemented!()
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: expected one of `,` or `>`, found `K`
2+
--> $DIR/issue-93867.rs:6:38
3+
|
4+
LL | pub fn entry<'a, K, V>() -> Entry<'a K, V> {
5+
| ^ expected one of `,` or `>`
6+
|
7+
help: you might have meant to end the type parameters here
8+
|
9+
LL | pub fn entry<'a, K, V>() -> Entry<'a> K, V> {
10+
| +
11+
12+
error: aborting due to previous error
13+

src/test/ui/parser/lifetime-semicolon.fixed

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ struct Foo<'a, 'b> {
55
}
66

77
fn foo<'a, 'b>(_x: &mut Foo<'a, 'b>) {}
8-
//~^ ERROR expected one of `,`, `:`, `=`, or `>`, found `;`
8+
//~^ ERROR expected one of `,` or `>`, found `;`
99

1010
fn main() {}

src/test/ui/parser/lifetime-semicolon.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ struct Foo<'a, 'b> {
55
}
66

77
fn foo<'a, 'b>(_x: &mut Foo<'a; 'b>) {}
8-
//~^ ERROR expected one of `,`, `:`, `=`, or `>`, found `;`
8+
//~^ ERROR expected one of `,` or `>`, found `;`
99

1010
fn main() {}

src/test/ui/parser/lifetime-semicolon.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected one of `,`, `:`, `=`, or `>`, found `;`
1+
error: expected one of `,` or `>`, found `;`
22
--> $DIR/lifetime-semicolon.rs:7:31
33
|
44
LL | fn foo<'a, 'b>(_x: &mut Foo<'a; 'b>) {}
5-
| ^ expected one of `,`, `:`, `=`, or `>`
5+
| ^ expected one of `,` or `>`
66
|
77
help: use a comma to separate type parameters
88
|

src/test/ui/parser/require-parens-for-chained-comparison.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ error: expected `while`, `for`, `loop` or `{` after a label
5959
LL | let _ = f<'_, i8>();
6060
| ^ expected `while`, `for`, `loop` or `{` after a label
6161

62-
error: expected one of `.`, `:`, `;`, `?`, `else`, `for`, `loop`, `while`, `{`, or an operator, found `,`
62+
error: expected one of `.`, `:`, `;`, `?`, `else`, `for`, `loop`, `while`, or an operator, found `,`
6363
--> $DIR/require-parens-for-chained-comparison.rs:22:17
6464
|
6565
LL | let _ = f<'_, i8>();
66-
| ^ expected one of 10 possible tokens
66+
| ^ expected one of 9 possible tokens
6767
|
6868
help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
6969
|

0 commit comments

Comments
 (0)