Skip to content

Commit

Permalink
feat/refactor: improve errors in case of ident with number at start
Browse files Browse the repository at this point in the history
  • Loading branch information
Ezrashaw committed Mar 9, 2023
1 parent 8824994 commit 252e0b3
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 45 deletions.
3 changes: 1 addition & 2 deletions compiler/rustc_parse/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 9 additions & 8 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
41 changes: 27 additions & 14 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()
Expand All @@ -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);

Expand Down Expand Up @@ -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],
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_parse/src/parser/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
12 changes: 0 additions & 12 deletions compiler/rustc_parse/src/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;

Expand Down Expand Up @@ -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() {
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/parser/integer-literal-start-ident.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fn 1main() {}
//~^ ERROR expected identifier, found `1main`
10 changes: 10 additions & 0 deletions tests/ui/parser/integer-literal-start-ident.stderr
Original file line number Diff line number Diff line change
@@ -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

6 changes: 3 additions & 3 deletions tests/ui/parser/issues/issue-104088.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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() {}
18 changes: 12 additions & 6 deletions tests/ui/parser/issues/issue-104088.stderr
Original file line number Diff line number Diff line change
@@ -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
Expand Down

0 comments on commit 252e0b3

Please sign in to comment.