diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 1ea01d95a134e..6bdd6ece1fd37 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -235,6 +235,12 @@ impl<'a> Parser<'a> { }) } + let prev_token_to_string = pprust::token_kind_to_string(&self.prev_token.kind); + let prev_token_seems_reserved_token = !self.prev_token.is_path_segment_keyword() + && self.prev_token.is_ident() + && Ident::from_str(&prev_token_to_string.to_lowercase()).is_reserved() + && prev_token_to_string != prev_token_to_string.to_lowercase(); + let mut expected = edible .iter() .map(|x| TokenType::Token(x.clone())) @@ -246,15 +252,28 @@ impl<'a> Parser<'a> { let expect = tokens_to_string(&expected[..]); let actual = super::token_descr(&self.token); let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 { - let short_expect = if expected.len() > 6 { - format!("{} possible tokens", expected.len()) + if prev_token_seems_reserved_token { + ( + format!("`{:?}` is not a keyword", prev_token_to_string), + ( + self.prev_token.span, + format!("try `{:?}` instead", prev_token_to_string.to_lowercase()), + ), + ) } else { - expect.clone() - }; - ( - format!("expected one of {}, found {}", expect, actual), - (self.prev_token.span.shrink_to_hi(), format!("expected one of {}", short_expect)), - ) + let short_expect = if expected.len() > 6 { + format!("{} possible tokens", expected.len()) + } else { + expect.clone() + }; + ( + format!("expected one of {}, found {}", expect, actual), + ( + self.prev_token.span.shrink_to_hi(), + format!("expected one of {}", short_expect), + ), + ) + } } else if expected.is_empty() { ( format!("unexpected token: {}", actual), @@ -267,13 +286,19 @@ impl<'a> Parser<'a> { ) }; self.last_unexpected_token_span = Some(self.token.span); - let mut err = self.struct_span_err(self.token.span, &msg_exp); + let mut err = if !prev_token_seems_reserved_token { + self.struct_span_err(self.token.span, &msg_exp) + } else { + self.struct_span_err(self.prev_token.span, &msg_exp) + }; + let sp = if self.token == token::Eof { // This is EOF; don't want to point at the following char, but rather the last token. self.prev_token.span } else { label_sp }; + match self.recover_closing_delimiter( &expected .iter() @@ -295,7 +320,9 @@ impl<'a> Parser<'a> { } let sm = self.sess.source_map(); - if self.prev_token.span == DUMMY_SP { + if prev_token_seems_reserved_token { + err.span_label(self.prev_token.span, label_exp); + } else if self.prev_token.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); diff --git a/src/test/ui/parser/mistyped_reserved_token.rs b/src/test/ui/parser/mistyped_reserved_token.rs new file mode 100644 index 0000000000000..9d463fa26ca14 --- /dev/null +++ b/src/test/ui/parser/mistyped_reserved_token.rs @@ -0,0 +1,5 @@ +fn main() { + let a = While true { //~ ERROR + break; + }; +} diff --git a/src/test/ui/parser/mistyped_reserved_token.stderr b/src/test/ui/parser/mistyped_reserved_token.stderr new file mode 100644 index 0000000000000..d6f76f98b5038 --- /dev/null +++ b/src/test/ui/parser/mistyped_reserved_token.stderr @@ -0,0 +1,8 @@ +error: `"While"` is not a keyword + --> $DIR/mistyped_reserved_token.rs:2:13 + | +LL | let a = While true { + | ^^^^^ try `"while"` instead + +error: aborting due to previous error +