Skip to content

Commit 83de2a8

Browse files
Reject keyword lifetimes in parsing
1 parent 3bc3857 commit 83de2a8

File tree

13 files changed

+92
-67
lines changed

13 files changed

+92
-67
lines changed

compiler/rustc_ast/src/token.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,10 @@ impl Token {
662662
}
663663

664664
/// Returns `true` if the token is a lifetime.
665+
///
666+
/// This does not check that the lifetime is a *valid* lifetime -- for that,
667+
/// you need to go through `expect_lifetime`, since we need to check that the
668+
/// lifetime name is not reserved.
665669
pub fn is_lifetime(&self) -> bool {
666670
self.lifetime().is_some()
667671
}

compiler/rustc_ast_passes/messages.ftl

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,6 @@ ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation}
156156
.type = inherent impl for this type
157157
.only_trait = only trait implementations may be annotated with {$annotation}
158158
159-
ast_passes_invalid_label =
160-
invalid label name `{$name}`
161-
162159
ast_passes_invalid_unnamed_field =
163160
unnamed fields are not allowed outside of structs or unions
164161
.label = unnamed field declared here
@@ -170,9 +167,6 @@ ast_passes_invalid_unnamed_field_ty =
170167
ast_passes_item_underscore = `{$kind}` items in this context need a name
171168
.label = `_` is not a valid name for this `{$kind}` item
172169
173-
ast_passes_keyword_lifetime =
174-
lifetimes cannot use keyword names
175-
176170
ast_passes_match_arm_with_no_body =
177171
`match` arm with no body
178172
.suggestion = add a body after the pattern

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -257,19 +257,6 @@ impl<'a> AstValidator<'a> {
257257
self.session.dcx()
258258
}
259259

260-
fn check_lifetime(&self, ident: Ident) {
261-
let valid_names = [kw::UnderscoreLifetime, kw::StaticLifetime, kw::Empty];
262-
if !valid_names.contains(&ident.name) && ident.without_first_quote().is_reserved() {
263-
self.dcx().emit_err(errors::KeywordLifetime { span: ident.span });
264-
}
265-
}
266-
267-
fn check_label(&self, ident: Ident) {
268-
if ident.without_first_quote().is_reserved() {
269-
self.dcx().emit_err(errors::InvalidLabel { span: ident.span, name: ident.name });
270-
}
271-
}
272-
273260
fn visibility_not_permitted(&self, vis: &Visibility, note: errors::VisibilityNotPermittedNote) {
274261
if let VisibilityKind::Inherited = vis.kind {
275262
return;
@@ -879,16 +866,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
879866
self.walk_ty(ty)
880867
}
881868

882-
fn visit_label(&mut self, label: &'a Label) {
883-
self.check_label(label.ident);
884-
visit::walk_label(self, label);
885-
}
886-
887-
fn visit_lifetime(&mut self, lifetime: &'a Lifetime, _: visit::LifetimeCtxt) {
888-
self.check_lifetime(lifetime.ident);
889-
visit::walk_lifetime(self, lifetime);
890-
}
891-
892869
fn visit_field_def(&mut self, field: &'a FieldDef) {
893870
self.deny_unnamed_field(field);
894871
visit::walk_field_def(self, field)
@@ -1315,9 +1292,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
13151292
}
13161293

13171294
fn visit_generic_param(&mut self, param: &'a GenericParam) {
1318-
if let GenericParamKind::Lifetime { .. } = param.kind {
1319-
self.check_lifetime(param.ident);
1320-
}
13211295
visit::walk_generic_param(self, param);
13221296
}
13231297

compiler/rustc_ast_passes/src/errors.rs

Lines changed: 0 additions & 15 deletions
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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,9 @@ parse_invalid_dyn_keyword = invalid `dyn` keyword
390390
parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else`
391391
parse_invalid_identifier_with_leading_number = identifiers cannot start with a number
392392
393+
parse_invalid_label =
394+
invalid label name `{$name}`
395+
393396
parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid
394397
.label = invalid suffix `{$suffix}`
395398
.tuple_exception_line_1 = `{$suffix}` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases
@@ -416,6 +419,9 @@ parse_invalid_unicode_escape = invalid unicode character escape
416419
parse_invalid_variable_declaration =
417420
invalid variable declaration
418421
422+
parse_keyword_lifetime =
423+
lifetimes cannot use keyword names
424+
419425
parse_kw_bad_case = keyword `{$kw}` is written in the wrong case
420426
.suggestion = write it in the correct case
421427

compiler/rustc_parse/src/errors.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1907,6 +1907,21 @@ pub struct CannotBeRawIdent {
19071907
pub ident: Symbol,
19081908
}
19091909

1910+
#[derive(Diagnostic)]
1911+
#[diag(parse_keyword_lifetime)]
1912+
pub struct KeywordLifetime {
1913+
#[primary_span]
1914+
pub span: Span,
1915+
}
1916+
1917+
#[derive(Diagnostic)]
1918+
#[diag(parse_invalid_label)]
1919+
pub struct InvalidLabel {
1920+
#[primary_span]
1921+
pub span: Span,
1922+
pub name: Symbol,
1923+
}
1924+
19101925
#[derive(Diagnostic)]
19111926
#[diag(parse_cr_doc_comment)]
19121927
pub struct CrDocComment {

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2931,10 +2931,25 @@ impl<'a> Parser<'a> {
29312931
}
29322932

29332933
pub(crate) fn eat_label(&mut self) -> Option<Label> {
2934-
self.token.lifetime().map(|(ident, _)| {
2934+
if let Some((ident, is_raw)) = self.token.lifetime() {
2935+
// Disallow `'fn`, but with a better error message than `expect_lifetime`.
2936+
if matches!(is_raw, IdentIsRaw::No)
2937+
&& ident.without_first_quote().is_reserved()
2938+
&& ![kw::UnderscoreLifetime, kw::StaticLifetime].contains(&ident.name)
2939+
{
2940+
self.dcx().emit_err(errors::InvalidLabel { span: ident.span, name: ident.name });
2941+
}
2942+
2943+
// Disallow `'_: loop {}`
2944+
if [kw::StaticLifetime, kw::UnderscoreLifetime].contains(&ident.name) {
2945+
self.dcx().emit_err(errors::InvalidLabel { span: ident.span, name: ident.name });
2946+
}
2947+
29352948
self.bump();
2936-
Label { ident }
2937-
})
2949+
Some(Label { ident })
2950+
} else {
2951+
None
2952+
}
29382953
}
29392954

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

compiler/rustc_parse/src/parser/pat.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -539,12 +539,12 @@ impl<'a> Parser<'a> {
539539
None => PatKind::Path(qself, path),
540540
}
541541
}
542-
} else if let token::Lifetime(lt, IdentIsRaw::No) = self.token.kind
542+
} else if let Some((lt, IdentIsRaw::No)) = self.token.lifetime()
543543
// In pattern position, we're totally fine with using "next token isn't colon"
544544
// as a heuristic. We could probably just always try to recover if it's a lifetime,
545545
// because we never have `'a: label {}` in a pattern position anyways, but it does
546546
// keep us from suggesting something like `let 'a: Ty = ..` => `let 'a': Ty = ..`
547-
&& could_be_unclosed_char_literal(Ident::with_dummy_span(lt))
547+
&& could_be_unclosed_char_literal(lt)
548548
&& !self.look_ahead(1, |token| matches!(token.kind, token::Colon))
549549
{
550550
// Recover a `'a` as a `'a'` literal
@@ -672,11 +672,13 @@ impl<'a> Parser<'a> {
672672
/// Parse `&pat` / `&mut pat`.
673673
fn parse_pat_deref(&mut self, expected: Option<Expected>) -> PResult<'a, PatKind> {
674674
self.expect_and()?;
675-
if let token::Lifetime(name, _) = self.token.kind {
675+
if let Some((lifetime, _)) = self.token.lifetime() {
676676
self.bump(); // `'a`
677677

678-
self.dcx()
679-
.emit_err(UnexpectedLifetimeInPattern { span: self.prev_token.span, symbol: name });
678+
self.dcx().emit_err(UnexpectedLifetimeInPattern {
679+
span: self.prev_token.span,
680+
symbol: lifetime.name,
681+
});
680682
}
681683

682684
let mutbl = self.parse_mutability();

compiler/rustc_parse/src/parser/ty.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::errors::{
99
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
1010

1111
use rustc_ast::ptr::P;
12-
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
12+
use rustc_ast::token::{self, Delimiter, IdentIsRaw, Token, TokenKind};
1313
use rustc_ast::util::case::Case;
1414
use rustc_ast::{
1515
self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, FnRetTy, GenericBound,
@@ -1201,7 +1201,14 @@ impl<'a> Parser<'a> {
12011201

12021202
/// Parses a single lifetime `'a` or panics.
12031203
pub(super) fn expect_lifetime(&mut self) -> Lifetime {
1204-
if let Some((ident, _)) = self.token.lifetime() {
1204+
if let Some((ident, is_raw)) = self.token.lifetime() {
1205+
if matches!(is_raw, IdentIsRaw::No)
1206+
&& ident.without_first_quote().is_reserved()
1207+
&& ![kw::UnderscoreLifetime, kw::StaticLifetime].contains(&ident.name)
1208+
{
1209+
self.dcx().emit_err(errors::KeywordLifetime { span: ident.span });
1210+
}
1211+
12051212
self.bump();
12061213
Lifetime { ident, id: ast::DUMMY_NODE_ID }
12071214
} else {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//@ check-pass
2+
3+
macro_rules! t {
4+
($lt:lifetime) => {};
5+
}
6+
7+
t!('fn);
8+
9+
fn main() {}

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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 16 additions & 4 deletions
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

Lines changed: 6 additions & 6 deletions
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
|
@@ -48,12 +54,6 @@ error: expected identifier, found keyword `Self`
4854
LL | trait Self {}
4955
| ^^^^ expected identifier, found keyword
5056

51-
error: lifetimes cannot use keyword names
52-
--> $DIR/self_type_keyword.rs:6:12
53-
|
54-
LL | struct Bar<'Self>;
55-
| ^^^^^
56-
5757
error: cannot find macro `Self` in this scope
5858
--> $DIR/self_type_keyword.rs:21:9
5959
|

0 commit comments

Comments
 (0)