From 252e0b338575e6dc1d5bde8f9d38fca91305203f Mon Sep 17 00:00:00 2001 From: Ezra Shaw Date: Tue, 7 Mar 2023 23:01:26 +1300 Subject: [PATCH] feat/refactor: improve errors in case of ident with number at start --- compiler/rustc_parse/locales/en-US.ftl | 3 +- compiler/rustc_parse/src/errors.rs | 17 ++++---- .../rustc_parse/src/parser/diagnostics.rs | 41 ++++++++++++------- compiler/rustc_parse/src/parser/pat.rs | 4 ++ compiler/rustc_parse/src/parser/stmt.rs | 12 ------ .../ui/parser/integer-literal-start-ident.rs | 2 + .../parser/integer-literal-start-ident.stderr | 10 +++++ tests/ui/parser/issues/issue-104088.rs | 6 +-- tests/ui/parser/issues/issue-104088.stderr | 18 +++++--- 9 files changed, 68 insertions(+), 45 deletions(-) create mode 100644 tests/ui/parser/integer-literal-start-ident.rs create mode 100644 tests/ui/parser/integer-literal-start-ident.stderr diff --git a/compiler/rustc_parse/locales/en-US.ftl b/compiler/rustc_parse/locales/en-US.ftl index e76e91fc1b135..5c7dc1e2abf91 100644 --- a/compiler/rustc_parse/locales/en-US.ftl +++ b/compiler/rustc_parse/locales/en-US.ftl @@ -412,8 +412,7 @@ parse_fn_ptr_with_generics = function pointer types may not have generic paramet *[false] a } `for` parameter list -parse_invalid_identifier_with_leading_number = expected identifier, found number literal - .label = identifiers cannot start with a number +parse_invalid_identifier_with_leading_number = identifiers cannot start with a number parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of `fn` .suggestion = replace `fn` with `impl` here diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 1662db36d10f9..63e5bc5051326 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -939,6 +939,7 @@ pub(crate) struct ExpectedIdentifier { pub token: Token, pub suggest_raw: Option, pub suggest_remove_comma: Option, + pub help_cannot_start_number: Option, } impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedIdentifier { @@ -975,10 +976,18 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedIdentifier { sugg.add_to_diagnostic(&mut diag); } + if let Some(help) = self.help_cannot_start_number { + help.add_to_diagnostic(&mut diag); + } + diag } } +#[derive(Subdiagnostic)] +#[help(parse_invalid_identifier_with_leading_number)] +pub(crate) struct HelpIdentifierStartsWithNumber; + pub(crate) struct ExpectedSemi { pub span: Span, pub token: Token, @@ -1207,14 +1216,6 @@ pub(crate) struct SelfParamNotFirst { pub span: Span, } -#[derive(Diagnostic)] -#[diag(parse_invalid_identifier_with_leading_number)] -pub(crate) struct InvalidIdentiferStartsWithNumber { - #[primary_span] - #[label] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(parse_const_generic_without_braces)] pub(crate) struct ConstGenericWithoutBraces { diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 0a65c37ea7b79..5b12bcc182222 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -8,14 +8,14 @@ use crate::errors::{ ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg, ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, DocCommentOnParamType, DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, - GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg, InInTypo, - IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, ParenthesesInForHead, - ParenthesesInForHeadSugg, PatternMethodParamWithoutBody, QuestionMarkInType, - QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath, - StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, StructLiteralNeedingParensSugg, - SuggEscapeToUseAsIdentifier, SuggRemoveComma, UnexpectedConstInGenericParam, - UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, - UseEqInstead, + GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg, + HelpIdentifierStartsWithNumber, InInTypo, IncorrectAwait, IncorrectSemicolon, + IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg, + PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, + StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, + StructLiteralNeedingParensSugg, SuggEscapeToUseAsIdentifier, SuggRemoveComma, + UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, + UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, }; use crate::fluent_generated as fluent; @@ -280,6 +280,7 @@ impl<'a> Parser<'a> { TokenKind::CloseDelim(Delimiter::Brace), TokenKind::CloseDelim(Delimiter::Parenthesis), ]; + let suggest_raw = match self.token.ident() { Some((ident, false)) if ident.is_raw_guess() @@ -295,18 +296,19 @@ impl<'a> Parser<'a> { _ => None, }; - let suggest_remove_comma = - if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) { - Some(SuggRemoveComma { span: self.token.span }) - } else { - None - }; + let suggest_remove_comma = (self.token == token::Comma + && self.look_ahead(1, |t| t.is_ident())) + .then_some(SuggRemoveComma { span: self.token.span }); + + let help_cannot_start_number = + self.is_lit_bad_ident().then_some(HelpIdentifierStartsWithNumber); let err = ExpectedIdentifier { span: self.token.span, token: self.token.clone(), suggest_raw, suggest_remove_comma, + help_cannot_start_number, }; let mut err = err.into_diagnostic(&self.sess.span_diagnostic); @@ -365,6 +367,17 @@ impl<'a> Parser<'a> { err } + /// Checks if the current token is a integer or float literal and looks like + /// it could be a invalid identifier with digits at the start. + pub(super) fn is_lit_bad_ident(&mut self) -> bool { + matches!(self.token.uninterpolate().kind, token::Literal(Lit { kind: token::LitKind::Integer | token::LitKind::Float, .. }) + // ensure that the integer literal is followed by a *invalid* + // suffix: this is how we know that it is a identifier with an + // invalid beginning. + if rustc_ast::MetaItemLit::from_token(&self.token).is_none() + ) + } + pub(super) fn expected_one_of_not_found( &mut self, edible: &[TokenKind], diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 8e920f1c42143..fc9f1d1330a72 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -348,6 +348,10 @@ impl<'a> Parser<'a> { lo = self.token.span; } + if self.is_lit_bad_ident() { + return Err(self.expected_ident_found()); + } + let pat = if self.check(&token::BinOp(token::And)) || self.token.kind == token::AndAnd { self.parse_pat_deref(expected)? } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 92a22ffc2b07b..fbe5b88c49eaa 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -273,7 +273,6 @@ impl<'a> Parser<'a> { self.bump(); } - self.report_invalid_identifier_error()?; let (pat, colon) = self.parse_pat_before_ty(None, RecoverComma::Yes, PatternLocation::LetBinding)?; @@ -366,17 +365,6 @@ impl<'a> Parser<'a> { Ok(P(ast::Local { ty, pat, kind, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None })) } - /// report error for `let 1x = 123` - pub fn report_invalid_identifier_error(&mut self) -> PResult<'a, ()> { - if let token::Literal(lit) = self.token.uninterpolate().kind && - rustc_ast::MetaItemLit::from_token(&self.token).is_none() && - (lit.kind == token::LitKind::Integer || lit.kind == token::LitKind::Float) && - self.look_ahead(1, |t| matches!(t.kind, token::Eq) || matches!(t.kind, token::Colon ) ) { - return Err(self.sess.create_err(errors::InvalidIdentiferStartsWithNumber { span: self.token.span })); - } - Ok(()) - } - fn check_let_else_init_bool_expr(&self, init: &ast::Expr) { if let ast::ExprKind::Binary(op, ..) = init.kind { if op.node.lazy() { diff --git a/tests/ui/parser/integer-literal-start-ident.rs b/tests/ui/parser/integer-literal-start-ident.rs new file mode 100644 index 0000000000000..12537482e0ff6 --- /dev/null +++ b/tests/ui/parser/integer-literal-start-ident.rs @@ -0,0 +1,2 @@ +fn 1main() {} +//~^ ERROR expected identifier, found `1main` diff --git a/tests/ui/parser/integer-literal-start-ident.stderr b/tests/ui/parser/integer-literal-start-ident.stderr new file mode 100644 index 0000000000000..51c37a0d24c3c --- /dev/null +++ b/tests/ui/parser/integer-literal-start-ident.stderr @@ -0,0 +1,10 @@ +error: expected identifier, found `1main` + --> $DIR/integer-literal-start-ident.rs:1:4 + | +LL | fn 1main() {} + | ^^^^^ expected identifier + | + = help: identifiers cannot start with a number + +error: aborting due to previous error + diff --git a/tests/ui/parser/issues/issue-104088.rs b/tests/ui/parser/issues/issue-104088.rs index 5f794fe2dc924..86988c8cd21da 100644 --- a/tests/ui/parser/issues/issue-104088.rs +++ b/tests/ui/parser/issues/issue-104088.rs @@ -4,12 +4,12 @@ fn test() { fn test_2() { let 1x = 123; - //~^ ERROR expected identifier, found number literal + //~^ ERROR expected identifier, found `1x` } fn test_3() { let 2x: i32 = 123; - //~^ ERROR expected identifier, found number literal + //~^ ERROR expected identifier, found `2x` } fn test_4() { @@ -20,7 +20,7 @@ fn test_4() { fn test_5() { let 23name = 123; - //~^ ERROR expected identifier, found number literal + //~^ ERROR expected identifier, found `23name` } fn main() {} diff --git a/tests/ui/parser/issues/issue-104088.stderr b/tests/ui/parser/issues/issue-104088.stderr index ff4b4bdb6953e..6511a313149f4 100644 --- a/tests/ui/parser/issues/issue-104088.stderr +++ b/tests/ui/parser/issues/issue-104088.stderr @@ -1,20 +1,26 @@ -error: expected identifier, found number literal +error: expected identifier, found `1x` --> $DIR/issue-104088.rs:6:9 | LL | let 1x = 123; - | ^^ identifiers cannot start with a number + | ^^ expected identifier + | + = help: identifiers cannot start with a number -error: expected identifier, found number literal +error: expected identifier, found `2x` --> $DIR/issue-104088.rs:11:9 | LL | let 2x: i32 = 123; - | ^^ identifiers cannot start with a number + | ^^ expected identifier + | + = help: identifiers cannot start with a number -error: expected identifier, found number literal +error: expected identifier, found `23name` --> $DIR/issue-104088.rs:22:9 | LL | let 23name = 123; - | ^^^^^^ identifiers cannot start with a number + | ^^^^^^ expected identifier + | + = help: identifiers cannot start with a number error[E0308]: mismatched types --> $DIR/issue-104088.rs:16:12