Rollup merge of #108854 - Ezrashaw:improve-int-idents, r=oli-obk

feat/refactor: improve errors in case of ident with number at start

Improve parser code when we parse a integer (or float) literal but expect an identifier. We emit an error message saying that identifiers can't begin with numbers. This PR just improves that code and expands it to all identifiers. Note that I haven't implemented error recovery (this didn't exist before anyway), I might do that in a follow up PR.
This commit is contained in:
Matthias Krüger 2023-03-09 12:11:53 +01:00 committed by GitHub
commit bec7011a2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 68 additions and 45 deletions

View File

@ -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

View File

@ -939,6 +939,7 @@ pub(crate) struct ExpectedIdentifier {
pub token: Token,
pub suggest_raw: Option<SuggEscapeToUseAsIdentifier>,
pub suggest_remove_comma: Option<SuggRemoveComma>,
pub help_cannot_start_number: Option<HelpIdentifierStartsWithNumber>,
}
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 {

View File

@ -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],

View File

@ -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)) {

View File

@ -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() {

View File

@ -0,0 +1,2 @@
fn 1main() {}
//~^ ERROR expected identifier, found `1main`

View File

@ -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

View File

@ -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() {}

View File

@ -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