mirror of
https://github.com/rust-lang/rust.git
synced 2024-10-30 22:12:15 +00:00
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:
commit
bec7011a2c
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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],
|
||||
|
@ -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)) {
|
||||
|
@ -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() {
|
||||
|
2
tests/ui/parser/integer-literal-start-ident.rs
Normal file
2
tests/ui/parser/integer-literal-start-ident.rs
Normal file
@ -0,0 +1,2 @@
|
||||
fn 1main() {}
|
||||
//~^ ERROR expected identifier, found `1main`
|
10
tests/ui/parser/integer-literal-start-ident.stderr
Normal file
10
tests/ui/parser/integer-literal-start-ident.stderr
Normal 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
|
||||
|
@ -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() {}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user