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

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