Skip to content

Commit d0a1851

Browse files
Deny keyword lifetimes pre-expansion
1 parent a91f7d7 commit d0a1851

14 files changed

+99
-70
lines changed

compiler/rustc_ast_passes/messages.ftl

-6
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,6 @@ ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation}
159159
.type = inherent impl for this type
160160
.only_trait = only trait implementations may be annotated with {$annotation}
161161
162-
ast_passes_invalid_label =
163-
invalid label name `{$name}`
164-
165162
ast_passes_invalid_unnamed_field =
166163
unnamed fields are not allowed outside of structs or unions
167164
.label = unnamed field declared here
@@ -176,9 +173,6 @@ ast_passes_item_invalid_safety = items outside of `unsafe extern {"{ }"}` cannot
176173
ast_passes_item_underscore = `{$kind}` items in this context need a name
177174
.label = `_` is not a valid name for this `{$kind}` item
178175
179-
ast_passes_keyword_lifetime =
180-
lifetimes cannot use keyword names
181-
182176
ast_passes_match_arm_with_no_body =
183177
`match` arm with no body
184178
.suggestion = add a body after the pattern

compiler/rustc_ast_passes/src/ast_validation.rs

-30
Original file line numberDiff line numberDiff line change
@@ -284,19 +284,6 @@ impl<'a> AstValidator<'a> {
284284
self.session.dcx()
285285
}
286286

287-
fn check_lifetime(&self, ident: Ident) {
288-
let valid_names = [kw::UnderscoreLifetime, kw::StaticLifetime, kw::Empty];
289-
if !valid_names.contains(&ident.name) && ident.without_first_quote().is_reserved() {
290-
self.dcx().emit_err(errors::KeywordLifetime { span: ident.span });
291-
}
292-
}
293-
294-
fn check_label(&self, ident: Ident) {
295-
if ident.without_first_quote().is_reserved() {
296-
self.dcx().emit_err(errors::InvalidLabel { span: ident.span, name: ident.name });
297-
}
298-
}
299-
300287
fn visibility_not_permitted(&self, vis: &Visibility, note: errors::VisibilityNotPermittedNote) {
301288
if let VisibilityKind::Inherited = vis.kind {
302289
return;
@@ -923,16 +910,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
923910
self.walk_ty(ty)
924911
}
925912

926-
fn visit_label(&mut self, label: &'a Label) {
927-
self.check_label(label.ident);
928-
visit::walk_label(self, label);
929-
}
930-
931-
fn visit_lifetime(&mut self, lifetime: &'a Lifetime, _: visit::LifetimeCtxt) {
932-
self.check_lifetime(lifetime.ident);
933-
visit::walk_lifetime(self, lifetime);
934-
}
935-
936913
fn visit_field_def(&mut self, field: &'a FieldDef) {
937914
self.deny_unnamed_field(field);
938915
visit::walk_field_def(self, field)
@@ -1371,13 +1348,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
13711348
}
13721349
}
13731350

1374-
fn visit_generic_param(&mut self, param: &'a GenericParam) {
1375-
if let GenericParamKind::Lifetime { .. } = param.kind {
1376-
self.check_lifetime(param.ident);
1377-
}
1378-
visit::walk_generic_param(self, param);
1379-
}
1380-
13811351
fn visit_param_bound(&mut self, bound: &'a GenericBound, ctxt: BoundKind) {
13821352
match bound {
13831353
GenericBound::Trait(trait_ref, modifiers) => {

compiler/rustc_ast_passes/src/errors.rs

-15
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,6 @@ use rustc_span::{symbol::Ident, Span, Symbol};
99

1010
use crate::fluent_generated as fluent;
1111

12-
#[derive(Diagnostic)]
13-
#[diag(ast_passes_keyword_lifetime)]
14-
pub struct KeywordLifetime {
15-
#[primary_span]
16-
pub span: Span,
17-
}
18-
19-
#[derive(Diagnostic)]
20-
#[diag(ast_passes_invalid_label)]
21-
pub struct InvalidLabel {
22-
#[primary_span]
23-
pub span: Span,
24-
pub name: Symbol,
25-
}
26-
2712
#[derive(Diagnostic)]
2813
#[diag(ast_passes_visibility_not_permitted, code = E0449)]
2914
pub struct VisibilityNotPermitted {

compiler/rustc_parse/messages.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,9 @@ parse_invalid_dyn_keyword = invalid `dyn` keyword
388388
parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else`
389389
parse_invalid_identifier_with_leading_number = identifiers cannot start with a number
390390
391+
parse_invalid_label =
392+
invalid label name `{$name}`
393+
391394
parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid
392395
.label = invalid suffix `{$suffix}`
393396
.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
414417
parse_invalid_variable_declaration =
415418
invalid variable declaration
416419
420+
parse_keyword_lifetime =
421+
lifetimes cannot use keyword names
422+
417423
parse_kw_bad_case = keyword `{$kw}` is written in the wrong case
418424
.suggestion = write it in the correct case
419425

compiler/rustc_parse/src/errors.rs

+15
Original file line numberDiff line numberDiff line change
@@ -2009,6 +2009,21 @@ pub struct CannotBeRawIdent {
20092009
pub ident: Symbol,
20102010
}
20112011

2012+
#[derive(Diagnostic)]
2013+
#[diag(parse_keyword_lifetime)]
2014+
pub struct KeywordLifetime {
2015+
#[primary_span]
2016+
pub span: Span,
2017+
}
2018+
2019+
#[derive(Diagnostic)]
2020+
#[diag(parse_invalid_label)]
2021+
pub struct InvalidLabel {
2022+
#[primary_span]
2023+
pub span: Span,
2024+
pub name: Symbol,
2025+
}
2026+
20122027
#[derive(Diagnostic)]
20132028
#[diag(parse_cr_doc_comment)]
20142029
pub struct CrDocComment {

compiler/rustc_parse/src/parser/expr.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -2932,10 +2932,17 @@ impl<'a> Parser<'a> {
29322932
}
29332933

29342934
pub(crate) fn eat_label(&mut self) -> Option<Label> {
2935-
self.token.lifetime().map(|ident| {
2935+
if let Some(ident) = self.token.lifetime() {
2936+
// Disallow `'fn`, but with a better error message than `expect_lifetime`.
2937+
if ident.without_first_quote().is_reserved() {
2938+
self.dcx().emit_err(errors::InvalidLabel { span: ident.span, name: ident.name });
2939+
}
2940+
29362941
self.bump();
2937-
Label { ident }
2938-
})
2942+
Some(Label { ident })
2943+
} else {
2944+
None
2945+
}
29392946
}
29402947

29412948
/// Parses a `match ... { ... }` expression (`match` token already eaten).

compiler/rustc_parse/src/parser/nonterminal.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,11 @@ impl<'a> Parser<'a> {
177177
.collect_tokens_no_attrs(|this| this.parse_visibility(FollowedByType::Yes))?))
178178
}
179179
NonterminalKind::Lifetime => {
180-
return if self.check_lifetime() {
181-
Ok(ParseNtResult::Lifetime(self.expect_lifetime().ident))
180+
// We want to keep `'keyword` parsing, just like `keyword` is still
181+
// an ident for nonterminal purposes.
182+
return if let Some(ident) = self.token.lifetime() {
183+
self.bump();
184+
Ok(ParseNtResult::Lifetime(ident))
182185
} else {
183186
Err(self.dcx().create_err(UnexpectedNonterminal::Lifetime {
184187
span: self.token.span,

compiler/rustc_parse/src/parser/pat.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -542,12 +542,12 @@ impl<'a> Parser<'a> {
542542
None => PatKind::Path(qself, path),
543543
}
544544
}
545-
} else if let token::Lifetime(lt) = self.token.kind
545+
} else if let Some(lt) = self.token.lifetime()
546546
// In pattern position, we're totally fine with using "next token isn't colon"
547547
// as a heuristic. We could probably just always try to recover if it's a lifetime,
548548
// because we never have `'a: label {}` in a pattern position anyways, but it does
549549
// keep us from suggesting something like `let 'a: Ty = ..` => `let 'a': Ty = ..`
550-
&& could_be_unclosed_char_literal(Ident::with_dummy_span(lt))
550+
&& could_be_unclosed_char_literal(lt)
551551
&& !self.look_ahead(1, |token| matches!(token.kind, token::Colon))
552552
{
553553
// Recover a `'a` as a `'a'` literal
@@ -683,12 +683,12 @@ impl<'a> Parser<'a> {
683683
/// Parse `&pat` / `&mut pat`.
684684
fn parse_pat_deref(&mut self, expected: Option<Expected>) -> PResult<'a, PatKind> {
685685
self.expect_and()?;
686-
if let token::Lifetime(name) = self.token.kind {
686+
if let Some(lifetime) = self.token.lifetime() {
687687
self.bump(); // `'a`
688688

689689
self.dcx().emit_err(UnexpectedLifetimeInPattern {
690690
span: self.prev_token.span,
691-
symbol: name,
691+
symbol: lifetime.name,
692692
suggestion: self.prev_token.span.until(self.token.span),
693693
});
694694
}

compiler/rustc_parse/src/parser/ty.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1230,6 +1230,12 @@ impl<'a> Parser<'a> {
12301230
/// Parses a single lifetime `'a` or panics.
12311231
pub(super) fn expect_lifetime(&mut self) -> Lifetime {
12321232
if let Some(ident) = self.token.lifetime() {
1233+
if ident.without_first_quote().is_reserved()
1234+
&& ![kw::UnderscoreLifetime, kw::StaticLifetime].contains(&ident.name)
1235+
{
1236+
self.dcx().emit_err(errors::KeywordLifetime { span: ident.span });
1237+
}
1238+
12331239
self.bump();
12341240
Lifetime { ident, id: ast::DUMMY_NODE_ID }
12351241
} else {
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Disallow `'keyword` even in cfg'd code.
2+
3+
#[cfg(any())]
4+
fn hello() -> &'ref () {}
5+
//~^ ERROR lifetimes cannot use keyword names
6+
7+
macro_rules! macro_invocation {
8+
($i:item) => {}
9+
}
10+
macro_invocation! {
11+
fn hello() -> &'ref () {}
12+
//~^ ERROR lifetimes cannot use keyword names
13+
}
14+
15+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: lifetimes cannot use keyword names
2+
--> $DIR/cfg-keyword-lifetime.rs:4:16
3+
|
4+
LL | fn hello() -> &'ref () {}
5+
| ^^^^
6+
7+
error: lifetimes cannot use keyword names
8+
--> $DIR/cfg-keyword-lifetime.rs:11:20
9+
|
10+
LL | fn hello() -> &'ref () {}
11+
| ^^^^
12+
13+
error: aborting due to 2 previous errors
14+

tests/ui/parser/require-parens-for-chained-comparison.rs

+2
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ fn main() {
2424
//~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
2525
//~| ERROR expected
2626
//~| HELP add `'` to close the char literal
27+
//~| ERROR invalid label name
2728

2829
f<'_>();
2930
//~^ comparison operators cannot be chained
3031
//~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
3132
//~| ERROR expected
3233
//~| HELP add `'` to close the char literal
34+
//~| ERROR invalid label name
3335

3436
let _ = f<u8>;
3537
//~^ ERROR comparison operators cannot be chained

tests/ui/parser/require-parens-for-chained-comparison.stderr

+16-4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ help: use `::<...>` instead of `<...>` to specify lifetime, type, or const argum
5353
LL | let _ = f::<u8, i8>();
5454
| ++
5555

56+
error: invalid label name `'_`
57+
--> $DIR/require-parens-for-chained-comparison.rs:22:15
58+
|
59+
LL | let _ = f<'_, i8>();
60+
| ^^
61+
5662
error: expected `while`, `for`, `loop` or `{` after a label
5763
--> $DIR/require-parens-for-chained-comparison.rs:22:17
5864
|
@@ -75,8 +81,14 @@ help: use `::<...>` instead of `<...>` to specify lifetime, type, or const argum
7581
LL | let _ = f::<'_, i8>();
7682
| ++
7783

84+
error: invalid label name `'_`
85+
--> $DIR/require-parens-for-chained-comparison.rs:29:7
86+
|
87+
LL | f<'_>();
88+
| ^^
89+
7890
error: expected `while`, `for`, `loop` or `{` after a label
79-
--> $DIR/require-parens-for-chained-comparison.rs:28:9
91+
--> $DIR/require-parens-for-chained-comparison.rs:29:9
8092
|
8193
LL | f<'_>();
8294
| ^ expected `while`, `for`, `loop` or `{` after a label
@@ -87,7 +99,7 @@ LL | f<'_'>();
8799
| +
88100

89101
error: comparison operators cannot be chained
90-
--> $DIR/require-parens-for-chained-comparison.rs:28:6
102+
--> $DIR/require-parens-for-chained-comparison.rs:29:6
91103
|
92104
LL | f<'_>();
93105
| ^ ^
@@ -98,13 +110,13 @@ LL | f::<'_>();
98110
| ++
99111

100112
error: comparison operators cannot be chained
101-
--> $DIR/require-parens-for-chained-comparison.rs:34:14
113+
--> $DIR/require-parens-for-chained-comparison.rs:36:14
102114
|
103115
LL | let _ = f<u8>;
104116
| ^ ^
105117
|
106118
= help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
107119
= help: or use `(...)` if you meant to specify fn arguments
108120

109-
error: aborting due to 10 previous errors
121+
error: aborting due to 12 previous errors
110122

tests/ui/self/self_type_keyword.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ error: expected identifier, found keyword `Self`
44
LL | struct Self;
55
| ^^^^ expected identifier, found keyword
66

7+
error: lifetimes cannot use keyword names
8+
--> $DIR/self_type_keyword.rs:6:12
9+
|
10+
LL | struct Bar<'Self>;
11+
| ^^^^^
12+
713
error: expected identifier, found keyword `Self`
814
--> $DIR/self_type_keyword.rs:14:13
915
|
@@ -53,12 +59,6 @@ error: expected identifier, found keyword `Self`
5359
LL | trait Self {}
5460
| ^^^^ expected identifier, found keyword
5561

56-
error: lifetimes cannot use keyword names
57-
--> $DIR/self_type_keyword.rs:6:12
58-
|
59-
LL | struct Bar<'Self>;
60-
| ^^^^^
61-
6262
error: cannot find macro `Self` in this scope
6363
--> $DIR/self_type_keyword.rs:21:9
6464
|

0 commit comments

Comments
 (0)