Rollup merge of #137140 - Noratrieb:const-move, r=jieyouxu,compiler-errors

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.

fixes #137128
This commit is contained in:
Matthias Krüger 2025-02-17 06:37:39 +01:00 committed by GitHub
commit f0710999a9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 163 additions and 3 deletions

View file

@ -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,
})
}

View file

@ -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: "'_",

View file

@ -0,0 +1,158 @@
//@ check-pass
//@ revisions:e2015 e2018 e2021 e2024
//@[e2015] edition:2015
//@[e2018] edition:2018
//@[e2021] edition:2021
//@[e2024] 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)]
// NOTE: It is vital to only use a `tt` fragment to avoid confusing
// the parser with nonterminals that can mask bugs.
macro_rules! tests {
($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;
let b = $kw;
assert_eq!($kw, b);
type $kw = ();
let $kw: $kw = ();
let _ = $kw as $kw;
}
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 => {}
_ => {}
}
}
}
}
};
}
tests!(r#break);
tests!(r#const);
tests!(r#continue);
tests!(r#else);
tests!(r#enum);
tests!(r#extern);
tests!(r#false);
tests!(r#fn);
tests!(r#for);
tests!(r#if);
tests!(r#impl);
tests!(r#in);
tests!(r#let);
tests!(r#loop);
tests!(r#match);
tests!(r#mod);
tests!(r#move);
tests!(r#mut);
tests!(r#pub);
tests!(r#ref);
tests!(r#return);
tests!(r#static);
tests!(r#struct);
tests!(r#trait);
tests!(r#true);
tests!(r#type);
tests!(r#unsafe);
tests!(r#use);
tests!(r#where);
tests!(r#while);
tests!(r#abstract);
tests!(r#become);
tests!(r#box);
tests!(r#do);
tests!(r#final);
tests!(r#macro);
tests!(r#override);
tests!(r#priv);
tests!(r#typeof);
tests!(r#unsized);
tests!(r#virtual);
tests!(r#yield);
tests!(r#async);
tests!(r#await);
tests!(r#dyn);
tests!(r#gen);
tests!(r#try);
// Weak keywords:
tests!(auto);
tests!(builtin);
tests!(catch);
tests!(default);
tests!(macro_rules);
tests!(raw);
tests!(reuse);
tests!(contract_ensures);
tests!(contract_requires);
tests!(safe);
tests!(union);
tests!(yeet);