Skip to content

Commit

Permalink
Fix const items not being allowed to be called r#move or r#static
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Noratrieb committed Feb 16, 2025
1 parent 23032f3 commit d4d2941
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 3 deletions.
6 changes: 3 additions & 3 deletions compiler/rustc_parse/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -754,9 +754,9 @@ impl<'a> Parser<'a> {
self.is_keyword_ahead(0, &[kw::Const])
&& self.look_ahead(1, |t| match &t.kind {
// async closures do not work with const closures, so we do not parse that here.
token::Ident(kw::Move | kw::Static, _) | token::OrOr | token::BinOp(token::Or) => {
true
}
token::Ident(kw::Move | kw::Static, IdentIsRaw::No)
| token::OrOr
| token::BinOp(token::Or) => true,
_ => false,
})
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ symbols! {
Gen: "gen", // >= 2024 Edition only
Try: "try", // >= 2018 Edition only

// NOTE: When adding new keywords, consider adding them to the ui/parser/raw/raw-idents.rs test.

// "Lifetime keywords": regular keywords with a leading `'`.
// Matching predicates: `is_any_keyword`
UnderscoreLifetime: "'_",
Expand Down
157 changes: 157 additions & 0 deletions tests/ui/parser/raw/raw-idents.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//@ check-pass
// To make everything a keyword:
//@ edition:2024

// Ensure that all (usable as identifier) keywords work as raw identifiers in all positions.
// This was motivated by issue #137128, where `r#move`/`r#static`` did not work as `const` names
// due to a parser check not acounting for raw identifiers.

#![crate_type = "lib"]
#![allow(dead_code, nonstandard_style)]

macro_rules! with_keywords {
($macro:ident) => {
$macro!(r#break);
$macro!(r#const);
$macro!(r#continue);
$macro!(r#else);
$macro!(r#enum);
$macro!(r#extern);
$macro!(r#false);
$macro!(r#fn);
$macro!(r#for);
$macro!(r#if);
$macro!(r#impl);
$macro!(r#in);
$macro!(r#let);
$macro!(r#loop);
$macro!(r#match);
$macro!(r#mod);
$macro!(r#move);
$macro!(r#mut);
$macro!(r#pub);
$macro!(r#ref);
$macro!(r#return);
$macro!(r#static);
$macro!(r#struct);
$macro!(r#trait);
$macro!(r#true);
$macro!(r#type);
$macro!(r#unsafe);
$macro!(r#use);
$macro!(r#where);
$macro!(r#while);
$macro!(r#abstract);
$macro!(r#become);
$macro!(r#box);
$macro!(r#do);
$macro!(r#final);
$macro!(r#macro);
$macro!(r#override);
$macro!(r#priv);
$macro!(r#typeof);
$macro!(r#unsized);
$macro!(r#virtual);
$macro!(r#yield);
$macro!(r#async);
$macro!(r#await);
$macro!(r#dyn);
$macro!(r#gen);
$macro!(r#try);

// Weak keywords:
$macro!(auto);
$macro!(builtin);
$macro!(catch);
$macro!(default);
$macro!(macro_rules);
$macro!(raw);
$macro!(reuse);
$macro!(contract_ensures);
$macro!(contract_requires);
$macro!(safe);
$macro!(union);
$macro!(yeet);
};
}

// NOTE: It is vital to only use a `tt` fragment to avoid confusing
// the parser with nonterminals that can mask bugs.

macro_rules! callback {
($kw:tt) => {
mod $kw {
mod const_item {
const $kw: () = ();
}
mod static_item {
static $kw: () = ();
}
mod fn_item {
fn $kw() {}
}
mod mod_and_use_item {
mod $kw {
use super::$kw;
}
}
mod ty_alias_item {
type $kw = ();
}
mod struct_item {
struct $kw { $kw: () }
}
mod enum_item {
enum $kw { $kw }
}
mod union_item {
union $kw { $kw: () }
}
mod trait_item {
trait $kw {
fn $kw() {}
}
}
mod generics_and_impl {
struct A<$kw>($kw);
enum B<$kw> { A($kw) }
trait Tr<$kw> {
type $kw;
}

impl<$kw> Tr<$kw> for A<$kw> {
type $kw = ();
}
impl<$kw> B<$kw> {}
}
mod extern_crate {
#[cfg(any())]
extern crate $kw;
}
mod body {
fn expr() {
let $kw = 0;
assert_eq!($kw, 0);
}
fn pat_const() {
const $kw: u8 = 0;

// Ensure that $kw actually matches the constant.
#[forbid(unreachable_patterns)]
match 1 {
$kw => {}
_ => {}
}
}
fn pat_binding() {
match 1 {
$kw => {}
_ => {}
}
}
}
}
};
}

with_keywords!(callback);

0 comments on commit d4d2941

Please sign in to comment.