Skip to content

Commit bec7011

Browse files
authored
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.
2 parents aedd94f + 252e0b3 commit bec7011

File tree

9 files changed

+68
-45
lines changed

9 files changed

+68
-45
lines changed

compiler/rustc_parse/locales/en-US.ftl

+1-2
Original file line numberDiff line numberDiff line change
@@ -412,8 +412,7 @@ parse_fn_ptr_with_generics = function pointer types may not have generic paramet
412412
*[false] a
413413
} `for` parameter list
414414
415-
parse_invalid_identifier_with_leading_number = expected identifier, found number literal
416-
.label = identifiers cannot start with a number
415+
parse_invalid_identifier_with_leading_number = identifiers cannot start with a number
417416
418417
parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of `fn`
419418
.suggestion = replace `fn` with `impl` here

compiler/rustc_parse/src/errors.rs

+9-8
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,7 @@ pub(crate) struct ExpectedIdentifier {
939939
pub token: Token,
940940
pub suggest_raw: Option<SuggEscapeToUseAsIdentifier>,
941941
pub suggest_remove_comma: Option<SuggRemoveComma>,
942+
pub help_cannot_start_number: Option<HelpIdentifierStartsWithNumber>,
942943
}
943944

944945
impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedIdentifier {
@@ -975,10 +976,18 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedIdentifier {
975976
sugg.add_to_diagnostic(&mut diag);
976977
}
977978

979+
if let Some(help) = self.help_cannot_start_number {
980+
help.add_to_diagnostic(&mut diag);
981+
}
982+
978983
diag
979984
}
980985
}
981986

987+
#[derive(Subdiagnostic)]
988+
#[help(parse_invalid_identifier_with_leading_number)]
989+
pub(crate) struct HelpIdentifierStartsWithNumber;
990+
982991
pub(crate) struct ExpectedSemi {
983992
pub span: Span,
984993
pub token: Token,
@@ -1207,14 +1216,6 @@ pub(crate) struct SelfParamNotFirst {
12071216
pub span: Span,
12081217
}
12091218

1210-
#[derive(Diagnostic)]
1211-
#[diag(parse_invalid_identifier_with_leading_number)]
1212-
pub(crate) struct InvalidIdentiferStartsWithNumber {
1213-
#[primary_span]
1214-
#[label]
1215-
pub span: Span,
1216-
}
1217-
12181219
#[derive(Diagnostic)]
12191220
#[diag(parse_const_generic_without_braces)]
12201221
pub(crate) struct ConstGenericWithoutBraces {

compiler/rustc_parse/src/parser/diagnostics.rs

+27-14
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ use crate::errors::{
88
ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg,
99
ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, DocCommentOnParamType,
1010
DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
11-
GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg, InInTypo,
12-
IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, ParenthesesInForHead,
13-
ParenthesesInForHeadSugg, PatternMethodParamWithoutBody, QuestionMarkInType,
14-
QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
15-
StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, StructLiteralNeedingParensSugg,
16-
SuggEscapeToUseAsIdentifier, SuggRemoveComma, UnexpectedConstInGenericParam,
17-
UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets,
18-
UseEqInstead,
11+
GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg,
12+
HelpIdentifierStartsWithNumber, InInTypo, IncorrectAwait, IncorrectSemicolon,
13+
IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg,
14+
PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst,
15+
StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens,
16+
StructLiteralNeedingParensSugg, SuggEscapeToUseAsIdentifier, SuggRemoveComma,
17+
UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
18+
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead,
1919
};
2020

2121
use crate::fluent_generated as fluent;
@@ -280,6 +280,7 @@ impl<'a> Parser<'a> {
280280
TokenKind::CloseDelim(Delimiter::Brace),
281281
TokenKind::CloseDelim(Delimiter::Parenthesis),
282282
];
283+
283284
let suggest_raw = match self.token.ident() {
284285
Some((ident, false))
285286
if ident.is_raw_guess()
@@ -295,18 +296,19 @@ impl<'a> Parser<'a> {
295296
_ => None,
296297
};
297298

298-
let suggest_remove_comma =
299-
if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) {
300-
Some(SuggRemoveComma { span: self.token.span })
301-
} else {
302-
None
303-
};
299+
let suggest_remove_comma = (self.token == token::Comma
300+
&& self.look_ahead(1, |t| t.is_ident()))
301+
.then_some(SuggRemoveComma { span: self.token.span });
302+
303+
let help_cannot_start_number =
304+
self.is_lit_bad_ident().then_some(HelpIdentifierStartsWithNumber);
304305

305306
let err = ExpectedIdentifier {
306307
span: self.token.span,
307308
token: self.token.clone(),
308309
suggest_raw,
309310
suggest_remove_comma,
311+
help_cannot_start_number,
310312
};
311313
let mut err = err.into_diagnostic(&self.sess.span_diagnostic);
312314

@@ -365,6 +367,17 @@ impl<'a> Parser<'a> {
365367
err
366368
}
367369

370+
/// Checks if the current token is a integer or float literal and looks like
371+
/// it could be a invalid identifier with digits at the start.
372+
pub(super) fn is_lit_bad_ident(&mut self) -> bool {
373+
matches!(self.token.uninterpolate().kind, token::Literal(Lit { kind: token::LitKind::Integer | token::LitKind::Float, .. })
374+
// ensure that the integer literal is followed by a *invalid*
375+
// suffix: this is how we know that it is a identifier with an
376+
// invalid beginning.
377+
if rustc_ast::MetaItemLit::from_token(&self.token).is_none()
378+
)
379+
}
380+
368381
pub(super) fn expected_one_of_not_found(
369382
&mut self,
370383
edible: &[TokenKind],

compiler/rustc_parse/src/parser/pat.rs

+4
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,10 @@ impl<'a> Parser<'a> {
348348
lo = self.token.span;
349349
}
350350

351+
if self.is_lit_bad_ident() {
352+
return Err(self.expected_ident_found());
353+
}
354+
351355
let pat = if self.check(&token::BinOp(token::And)) || self.token.kind == token::AndAnd {
352356
self.parse_pat_deref(expected)?
353357
} else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {

compiler/rustc_parse/src/parser/stmt.rs

-12
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,6 @@ impl<'a> Parser<'a> {
273273
self.bump();
274274
}
275275

276-
self.report_invalid_identifier_error()?;
277276
let (pat, colon) =
278277
self.parse_pat_before_ty(None, RecoverComma::Yes, PatternLocation::LetBinding)?;
279278

@@ -366,17 +365,6 @@ impl<'a> Parser<'a> {
366365
Ok(P(ast::Local { ty, pat, kind, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None }))
367366
}
368367

369-
/// report error for `let 1x = 123`
370-
pub fn report_invalid_identifier_error(&mut self) -> PResult<'a, ()> {
371-
if let token::Literal(lit) = self.token.uninterpolate().kind &&
372-
rustc_ast::MetaItemLit::from_token(&self.token).is_none() &&
373-
(lit.kind == token::LitKind::Integer || lit.kind == token::LitKind::Float) &&
374-
self.look_ahead(1, |t| matches!(t.kind, token::Eq) || matches!(t.kind, token::Colon ) ) {
375-
return Err(self.sess.create_err(errors::InvalidIdentiferStartsWithNumber { span: self.token.span }));
376-
}
377-
Ok(())
378-
}
379-
380368
fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
381369
if let ast::ExprKind::Binary(op, ..) = init.kind {
382370
if op.node.lazy() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
fn 1main() {}
2+
//~^ ERROR expected identifier, found `1main`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: expected identifier, found `1main`
2+
--> $DIR/integer-literal-start-ident.rs:1:4
3+
|
4+
LL | fn 1main() {}
5+
| ^^^^^ expected identifier
6+
|
7+
= help: identifiers cannot start with a number
8+
9+
error: aborting due to previous error
10+

tests/ui/parser/issues/issue-104088.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ fn test() {
44

55
fn test_2() {
66
let 1x = 123;
7-
//~^ ERROR expected identifier, found number literal
7+
//~^ ERROR expected identifier, found `1x`
88
}
99

1010
fn test_3() {
1111
let 2x: i32 = 123;
12-
//~^ ERROR expected identifier, found number literal
12+
//~^ ERROR expected identifier, found `2x`
1313
}
1414

1515
fn test_4() {
@@ -20,7 +20,7 @@ fn test_4() {
2020

2121
fn test_5() {
2222
let 23name = 123;
23-
//~^ ERROR expected identifier, found number literal
23+
//~^ ERROR expected identifier, found `23name`
2424
}
2525

2626
fn main() {}

tests/ui/parser/issues/issue-104088.stderr

+12-6
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
1-
error: expected identifier, found number literal
1+
error: expected identifier, found `1x`
22
--> $DIR/issue-104088.rs:6:9
33
|
44
LL | let 1x = 123;
5-
| ^^ identifiers cannot start with a number
5+
| ^^ expected identifier
6+
|
7+
= help: identifiers cannot start with a number
68

7-
error: expected identifier, found number literal
9+
error: expected identifier, found `2x`
810
--> $DIR/issue-104088.rs:11:9
911
|
1012
LL | let 2x: i32 = 123;
11-
| ^^ identifiers cannot start with a number
13+
| ^^ expected identifier
14+
|
15+
= help: identifiers cannot start with a number
1216

13-
error: expected identifier, found number literal
17+
error: expected identifier, found `23name`
1418
--> $DIR/issue-104088.rs:22:9
1519
|
1620
LL | let 23name = 123;
17-
| ^^^^^^ identifiers cannot start with a number
21+
| ^^^^^^ expected identifier
22+
|
23+
= help: identifiers cannot start with a number
1824

1925
error[E0308]: mismatched types
2026
--> $DIR/issue-104088.rs:16:12

0 commit comments

Comments
 (0)