1
Fork 0

Deny keyword lifetimes pre-expansion

This commit is contained in:
Michael Goulet 2024-06-20 16:36:35 -04:00
parent a91f7d72f1
commit d0a1851ec2
14 changed files with 99 additions and 70 deletions

View file

@ -388,6 +388,9 @@ parse_invalid_dyn_keyword = invalid `dyn` keyword
parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else`
parse_invalid_identifier_with_leading_number = identifiers cannot start with a number
parse_invalid_label =
invalid label name `{$name}`
parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid
.label = invalid suffix `{$suffix}`
.tuple_exception_line_1 = `{$suffix}` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases
@ -414,6 +417,9 @@ parse_invalid_unicode_escape = invalid unicode character escape
parse_invalid_variable_declaration =
invalid variable declaration
parse_keyword_lifetime =
lifetimes cannot use keyword names
parse_kw_bad_case = keyword `{$kw}` is written in the wrong case
.suggestion = write it in the correct case

View file

@ -2009,6 +2009,21 @@ pub struct CannotBeRawIdent {
pub ident: Symbol,
}
#[derive(Diagnostic)]
#[diag(parse_keyword_lifetime)]
pub struct KeywordLifetime {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_invalid_label)]
pub struct InvalidLabel {
#[primary_span]
pub span: Span,
pub name: Symbol,
}
#[derive(Diagnostic)]
#[diag(parse_cr_doc_comment)]
pub struct CrDocComment {

View file

@ -2932,10 +2932,17 @@ impl<'a> Parser<'a> {
}
pub(crate) fn eat_label(&mut self) -> Option<Label> {
self.token.lifetime().map(|ident| {
if let Some(ident) = self.token.lifetime() {
// Disallow `'fn`, but with a better error message than `expect_lifetime`.
if ident.without_first_quote().is_reserved() {
self.dcx().emit_err(errors::InvalidLabel { span: ident.span, name: ident.name });
}
self.bump();
Label { ident }
})
Some(Label { ident })
} else {
None
}
}
/// Parses a `match ... { ... }` expression (`match` token already eaten).

View file

@ -177,8 +177,11 @@ impl<'a> Parser<'a> {
.collect_tokens_no_attrs(|this| this.parse_visibility(FollowedByType::Yes))?))
}
NonterminalKind::Lifetime => {
return if self.check_lifetime() {
Ok(ParseNtResult::Lifetime(self.expect_lifetime().ident))
// We want to keep `'keyword` parsing, just like `keyword` is still
// an ident for nonterminal purposes.
return if let Some(ident) = self.token.lifetime() {
self.bump();
Ok(ParseNtResult::Lifetime(ident))
} else {
Err(self.dcx().create_err(UnexpectedNonterminal::Lifetime {
span: self.token.span,

View file

@ -542,12 +542,12 @@ impl<'a> Parser<'a> {
None => PatKind::Path(qself, path),
}
}
} else if let token::Lifetime(lt) = self.token.kind
} else if let Some(lt) = self.token.lifetime()
// In pattern position, we're totally fine with using "next token isn't colon"
// as a heuristic. We could probably just always try to recover if it's a lifetime,
// because we never have `'a: label {}` in a pattern position anyways, but it does
// keep us from suggesting something like `let 'a: Ty = ..` => `let 'a': Ty = ..`
&& could_be_unclosed_char_literal(Ident::with_dummy_span(lt))
&& could_be_unclosed_char_literal(lt)
&& !self.look_ahead(1, |token| matches!(token.kind, token::Colon))
{
// Recover a `'a` as a `'a'` literal
@ -683,12 +683,12 @@ impl<'a> Parser<'a> {
/// Parse `&pat` / `&mut pat`.
fn parse_pat_deref(&mut self, expected: Option<Expected>) -> PResult<'a, PatKind> {
self.expect_and()?;
if let token::Lifetime(name) = self.token.kind {
if let Some(lifetime) = self.token.lifetime() {
self.bump(); // `'a`
self.dcx().emit_err(UnexpectedLifetimeInPattern {
span: self.prev_token.span,
symbol: name,
symbol: lifetime.name,
suggestion: self.prev_token.span.until(self.token.span),
});
}

View file

@ -1230,6 +1230,12 @@ impl<'a> Parser<'a> {
/// Parses a single lifetime `'a` or panics.
pub(super) fn expect_lifetime(&mut self) -> Lifetime {
if let Some(ident) = self.token.lifetime() {
if ident.without_first_quote().is_reserved()
&& ![kw::UnderscoreLifetime, kw::StaticLifetime].contains(&ident.name)
{
self.dcx().emit_err(errors::KeywordLifetime { span: ident.span });
}
self.bump();
Lifetime { ident, id: ast::DUMMY_NODE_ID }
} else {