Skip to content

Commit d4d2941

Browse files
committed
Fix const items not being allowed to be called r#move or r#static
Because of an ambiguity with const closures, the parser needs to ensure that for a const item, the `const` keyword isn't followed by a `move` or `static` keyword, as that would indicate a const closure: ```rust fn main() { const move // ... } ``` This check did not take raw identifiers into account, therefore being unable to distinguish between `const move` and `const r#move`. The latter is obviously not a const closure, so it should be allowed as a const item. This fixes the check in the parser to only treat `const ...` as a const closure if it's followed by the *proper keyword*, and not a raw identifier. Additionally, this adds a large test that tests for all raw identifiers in all kinds of positions, including `const`, to prevent issues like this one from occurring again.
1 parent 23032f3 commit d4d2941

File tree

3 files changed

+162
-3
lines changed

3 files changed

+162
-3
lines changed

compiler/rustc_parse/src/parser/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -754,9 +754,9 @@ impl<'a> Parser<'a> {
754754
self.is_keyword_ahead(0, &[kw::Const])
755755
&& self.look_ahead(1, |t| match &t.kind {
756756
// async closures do not work with const closures, so we do not parse that here.
757-
token::Ident(kw::Move | kw::Static, _) | token::OrOr | token::BinOp(token::Or) => {
758-
true
759-
}
757+
token::Ident(kw::Move | kw::Static, IdentIsRaw::No)
758+
| token::OrOr
759+
| token::BinOp(token::Or) => true,
760760
_ => false,
761761
})
762762
}

compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ symbols! {
104104
Gen: "gen", // >= 2024 Edition only
105105
Try: "try", // >= 2018 Edition only
106106

107+
// NOTE: When adding new keywords, consider adding them to the ui/parser/raw/raw-idents.rs test.
108+
107109
// "Lifetime keywords": regular keywords with a leading `'`.
108110
// Matching predicates: `is_any_keyword`
109111
UnderscoreLifetime: "'_",

tests/ui/parser/raw/raw-idents.rs

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
//@ check-pass
2+
// To make everything a keyword:
3+
//@ edition:2024
4+
5+
// Ensure that all (usable as identifier) keywords work as raw identifiers in all positions.
6+
// This was motivated by issue #137128, where `r#move`/`r#static`` did not work as `const` names
7+
// due to a parser check not acounting for raw identifiers.
8+
9+
#![crate_type = "lib"]
10+
#![allow(dead_code, nonstandard_style)]
11+
12+
macro_rules! with_keywords {
13+
($macro:ident) => {
14+
$macro!(r#break);
15+
$macro!(r#const);
16+
$macro!(r#continue);
17+
$macro!(r#else);
18+
$macro!(r#enum);
19+
$macro!(r#extern);
20+
$macro!(r#false);
21+
$macro!(r#fn);
22+
$macro!(r#for);
23+
$macro!(r#if);
24+
$macro!(r#impl);
25+
$macro!(r#in);
26+
$macro!(r#let);
27+
$macro!(r#loop);
28+
$macro!(r#match);
29+
$macro!(r#mod);
30+
$macro!(r#move);
31+
$macro!(r#mut);
32+
$macro!(r#pub);
33+
$macro!(r#ref);
34+
$macro!(r#return);
35+
$macro!(r#static);
36+
$macro!(r#struct);
37+
$macro!(r#trait);
38+
$macro!(r#true);
39+
$macro!(r#type);
40+
$macro!(r#unsafe);
41+
$macro!(r#use);
42+
$macro!(r#where);
43+
$macro!(r#while);
44+
$macro!(r#abstract);
45+
$macro!(r#become);
46+
$macro!(r#box);
47+
$macro!(r#do);
48+
$macro!(r#final);
49+
$macro!(r#macro);
50+
$macro!(r#override);
51+
$macro!(r#priv);
52+
$macro!(r#typeof);
53+
$macro!(r#unsized);
54+
$macro!(r#virtual);
55+
$macro!(r#yield);
56+
$macro!(r#async);
57+
$macro!(r#await);
58+
$macro!(r#dyn);
59+
$macro!(r#gen);
60+
$macro!(r#try);
61+
62+
// Weak keywords:
63+
$macro!(auto);
64+
$macro!(builtin);
65+
$macro!(catch);
66+
$macro!(default);
67+
$macro!(macro_rules);
68+
$macro!(raw);
69+
$macro!(reuse);
70+
$macro!(contract_ensures);
71+
$macro!(contract_requires);
72+
$macro!(safe);
73+
$macro!(union);
74+
$macro!(yeet);
75+
};
76+
}
77+
78+
// NOTE: It is vital to only use a `tt` fragment to avoid confusing
79+
// the parser with nonterminals that can mask bugs.
80+
81+
macro_rules! callback {
82+
($kw:tt) => {
83+
mod $kw {
84+
mod const_item {
85+
const $kw: () = ();
86+
}
87+
mod static_item {
88+
static $kw: () = ();
89+
}
90+
mod fn_item {
91+
fn $kw() {}
92+
}
93+
mod mod_and_use_item {
94+
mod $kw {
95+
use super::$kw;
96+
}
97+
}
98+
mod ty_alias_item {
99+
type $kw = ();
100+
}
101+
mod struct_item {
102+
struct $kw { $kw: () }
103+
}
104+
mod enum_item {
105+
enum $kw { $kw }
106+
}
107+
mod union_item {
108+
union $kw { $kw: () }
109+
}
110+
mod trait_item {
111+
trait $kw {
112+
fn $kw() {}
113+
}
114+
}
115+
mod generics_and_impl {
116+
struct A<$kw>($kw);
117+
enum B<$kw> { A($kw) }
118+
trait Tr<$kw> {
119+
type $kw;
120+
}
121+
122+
impl<$kw> Tr<$kw> for A<$kw> {
123+
type $kw = ();
124+
}
125+
impl<$kw> B<$kw> {}
126+
}
127+
mod extern_crate {
128+
#[cfg(any())]
129+
extern crate $kw;
130+
}
131+
mod body {
132+
fn expr() {
133+
let $kw = 0;
134+
assert_eq!($kw, 0);
135+
}
136+
fn pat_const() {
137+
const $kw: u8 = 0;
138+
139+
// Ensure that $kw actually matches the constant.
140+
#[forbid(unreachable_patterns)]
141+
match 1 {
142+
$kw => {}
143+
_ => {}
144+
}
145+
}
146+
fn pat_binding() {
147+
match 1 {
148+
$kw => {}
149+
_ => {}
150+
}
151+
}
152+
}
153+
}
154+
};
155+
}
156+
157+
with_keywords!(callback);

0 commit comments

Comments
 (0)