Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 69 additions & 57 deletions compiler/rustc_ast/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,9 @@ impl Lit {
/// `Parser::eat_token_lit` (excluding unary negation).
pub fn from_token(token: &Token) -> Option<Lit> {
match token.uninterpolate().kind {
Ident(name, IdentIsRaw::No) if name.is_bool_lit() => Some(Lit::new(Bool, name, None)),
Ident(name, IdentKind::Normal | IdentKind::ForcedKeyword) if name.is_bool_lit() => {
Some(Lit::new(Bool, name, None))
}
Literal(token_lit) => Some(token_lit),
OpenInvisible(InvisibleOrigin::MetaVar(
MetaVarKind::Literal | MetaVarKind::Expr { .. },
Expand Down Expand Up @@ -307,11 +309,11 @@ impl LitKind {
}
}

pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: IdentIsRaw) -> bool {
pub fn ident_can_begin_expr(name: Symbol, span: Span, kind: IdentKind) -> bool {
// WARNING: Take care when modifying this function! It will change the stable(!) set of
// tokens that are allowed to match an `expr` nonterminal which is user observable.

let ident_token = Token::new(Ident(name, is_raw), span);
let ident_token = Token::new(Ident(name, kind), span);

// FIXME: Remove `box` from this list given we officially no longer support box expressions
// (#108471) (needs lang FCP as it affects stable macro matching behavior).
Expand Down Expand Up @@ -344,11 +346,11 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: IdentIsRaw) -> boo
.contains(&name)
}

fn ident_can_begin_type(name: Symbol, span: Span, is_raw: IdentIsRaw) -> bool {
fn ident_can_begin_type(name: Symbol, span: Span, kind: IdentKind) -> bool {
// WARNING: Take care when modifying this function! It will change the stable(!) set of
// tokens that are allowed to match an `ty` nonterminal which is user observable.

let ident_token = Token::new(Ident(name, is_raw), span);
let ident_token = Token::new(Ident(name, kind), span);

!ident_token.is_reserved_ident()
|| ident_token.is_path_segment_keyword()
Expand All @@ -357,32 +359,30 @@ fn ident_can_begin_type(name: Symbol, span: Span, is_raw: IdentIsRaw) -> bool {
}

#[derive(PartialEq, Eq, Encodable, Decodable, Hash, Debug, Copy, Clone, StableHash)]
pub enum IdentIsRaw {
No,
Yes,
pub enum IdentKind {
Normal,
Raw,
ForcedKeyword,
}

impl IdentIsRaw {
impl IdentKind {
pub fn to_print_mode_ident(self) -> IdentPrintMode {
match self {
IdentIsRaw::No => IdentPrintMode::Normal,
IdentIsRaw::Yes => IdentPrintMode::RawIdent,
IdentKind::Normal => IdentPrintMode::Normal,
IdentKind::Raw => IdentPrintMode::RawIdent,
IdentKind::ForcedKeyword => IdentPrintMode::ForcedKeywordIdent,
}
}

pub fn to_print_mode_lifetime(self) -> IdentPrintMode {
match self {
IdentIsRaw::No => IdentPrintMode::Normal,
IdentIsRaw::Yes => IdentPrintMode::RawLifetime,
IdentKind::Normal => IdentPrintMode::Normal,
IdentKind::Raw => IdentPrintMode::RawLifetime,
IdentKind::ForcedKeyword => unreachable!(),
}
}
}

impl From<bool> for IdentIsRaw {
fn from(b: bool) -> Self {
if b { Self::Yes } else { Self::No }
}
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, Debug, StableHash)]
pub enum TokenKind {
/* Expression-operator symbols. */
Expand Down Expand Up @@ -507,22 +507,22 @@ pub enum TokenKind {
/// It's recommended to use `Token::{ident,uninterpolate}` and
/// `Parser::token_uninterpolated_span` to treat regular and interpolated
/// identifiers in the same way.
Ident(Symbol, IdentIsRaw),
Ident(Symbol, IdentKind),
/// This identifier (and its span) is the identifier passed to the
/// declarative macro. The span in the surrounding `Token` is the span of
/// the `ident` metavariable in the macro's RHS.
NtIdent(sp::Ident, IdentIsRaw),
NtIdent(sp::Ident, IdentKind),

/// Lifetime identifier token.
/// Do not forget about `NtLifetime` when you want to match on lifetime identifiers.
/// It's recommended to use `Token::{ident,uninterpolate}` and
/// `Parser::token_uninterpolated_span` to treat regular and interpolated
/// identifiers in the same way.
Lifetime(Symbol, IdentIsRaw),
Lifetime(Symbol, IdentKind),
/// This identifier (and its span) is the lifetime passed to the
/// declarative macro. The span in the surrounding `Token` is the span of
/// the `lifetime` metavariable in the macro's RHS.
NtLifetime(sp::Ident, IdentIsRaw),
NtLifetime(sp::Ident, IdentKind),

/// A doc comment token.
/// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc)
Expand Down Expand Up @@ -641,9 +641,13 @@ impl Token {
Token::new(TokenKind::Question, DUMMY_SP)
}

/// Recovers a `Token` from an `Ident`. This creates a raw identifier if necessary.
/// Recovers a `Token` from an `Ident`.
///
/// This creates a raw identifier if necessary.
/// It will never create a forced keyword.
pub fn from_ast_ident(ident: sp::Ident) -> Self {
Token::new(Ident(ident.name, ident.is_raw_guess().into()), ident.span)
let kind = if ident.is_raw_guess() { IdentKind::Raw } else { IdentKind::Normal };
Token::new(Ident(ident.name, kind), ident.span)
}

pub fn is_range_separator(&self) -> bool {
Expand Down Expand Up @@ -674,8 +678,8 @@ impl Token {
// tokens that are allowed to match an `expr` nonterminal which is user observable.

match self.uninterpolate().kind {
Ident(name, is_raw) =>
ident_can_begin_expr(name, self.span, is_raw), // value name or keyword
Ident(name, kind) =>
ident_can_begin_expr(name, self.span, kind), // value name or keyword
OpenParen | // tuple
OpenBrace | // block
OpenBracket | // array
Expand Down Expand Up @@ -743,8 +747,8 @@ impl Token {
// object types (consider `use<>+` and `use<T> + Trait` for example).

match self.uninterpolate().kind {
Ident(name, is_raw) =>
ident_can_begin_type(name, self.span, is_raw), // type name or keyword
Ident(name, kind) =>
ident_can_begin_type(name, self.span, kind), // type name or keyword
OpenParen // tuple
| OpenBracket // array
| Bang // never
Expand All @@ -766,7 +770,7 @@ impl Token {
pub fn can_begin_const_arg(&self) -> bool {
match self.kind {
OpenBrace | Literal(..) | Minus => true,
Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true,
Ident(name, IdentKind::Normal | IdentKind::ForcedKeyword) if name.is_bool_lit() => true,
OpenInvisible(InvisibleOrigin::MetaVar(
MetaVarKind::Expr { .. } | MetaVarKind::Block | MetaVarKind::Literal,
)) => true,
Expand Down Expand Up @@ -815,7 +819,7 @@ impl Token {
pub fn can_begin_literal_maybe_minus(&self) -> bool {
match self.uninterpolate().kind {
Literal(..) | Minus => true,
Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true,
Ident(name, IdentKind::Normal | IdentKind::ForcedKeyword) if name.is_bool_lit() => true,
OpenInvisible(InvisibleOrigin::MetaVar(mv_kind)) => match mv_kind {
MetaVarKind::Literal => true,
MetaVarKind::Expr { can_begin_literal_maybe_minus, .. } => {
Expand Down Expand Up @@ -845,32 +849,32 @@ impl Token {
/// otherwise returns the original token.
pub fn uninterpolate(&self) -> Cow<'_, Token> {
match self.kind {
NtIdent(ident, is_raw) => Cow::Owned(Token::new(Ident(ident.name, is_raw), ident.span)),
NtLifetime(ident, is_raw) => {
Cow::Owned(Token::new(Lifetime(ident.name, is_raw), ident.span))
NtIdent(ident, kind) => Cow::Owned(Token::new(Ident(ident.name, kind), ident.span)),
NtLifetime(ident, kind) => {
Cow::Owned(Token::new(Lifetime(ident.name, kind), ident.span))
}
_ => Cow::Borrowed(self),
}
}

/// Returns an identifier if this token is an identifier.
#[inline]
pub fn ident(&self) -> Option<(sp::Ident, IdentIsRaw)> {
pub fn ident(&self) -> Option<(sp::Ident, IdentKind)> {
// We avoid using `Token::uninterpolate` here because it's slow.
match self.kind {
Ident(name, is_raw) => Some((sp::Ident::new(name, self.span), is_raw)),
NtIdent(ident, is_raw) => Some((ident, is_raw)),
Ident(name, kind) => Some((sp::Ident::new(name, self.span), kind)),
NtIdent(ident, kind) => Some((ident, kind)),
_ => None,
}
}

/// Returns a lifetime identifier if this token is a lifetime.
#[inline]
pub fn lifetime(&self) -> Option<(sp::Ident, IdentIsRaw)> {
pub fn lifetime(&self) -> Option<(sp::Ident, IdentKind)> {
// We avoid using `Token::uninterpolate` here because it's slow.
match self.kind {
Lifetime(name, is_raw) => Some((sp::Ident::new(name, self.span), is_raw)),
NtLifetime(ident, is_raw) => Some((ident, is_raw)),
Lifetime(name, kind) => Some((sp::Ident::new(name, self.span), kind)),
NtLifetime(ident, kind) => Some((ident, kind)),
_ => None,
}
}
Expand Down Expand Up @@ -929,58 +933,56 @@ impl Token {

/// Returns `true` if the token is a given keyword, `kw`.
pub fn is_keyword(&self, kw: Symbol) -> bool {
self.is_non_raw_ident_where(|id| id.name == kw)
self.non_raw_ident().is_some_and(|id| id.name == kw)
}

/// Returns `true` if the token is a given keyword, `kw` or if `case` is `Insensitive` and this
/// token is an identifier equal to `kw` ignoring the case.
pub fn is_keyword_case(&self, kw: Symbol, case: Case) -> bool {
self.is_keyword(kw)
|| (case == Case::Insensitive
&& self.is_non_raw_ident_where(|id| {
&& self.non_raw_ident().is_some_and(|id| {
// Do an ASCII case-insensitive match, because all keywords are ASCII.
id.name.as_str().eq_ignore_ascii_case(kw.as_str())
}))
}

pub fn is_path_segment_keyword(&self) -> bool {
self.is_non_raw_ident_where(sp::Ident::is_path_segment_keyword)
self.non_raw_ident().is_some_and(sp::Ident::is_path_segment_keyword)
}

/// Returns true for reserved identifiers used internally for elided lifetimes,
/// unnamed method parameters, crate root module, error recovery etc.
pub fn is_special_ident(&self) -> bool {
self.is_non_raw_ident_where(sp::Ident::is_special)
self.non_raw_ident().is_some_and(sp::Ident::is_special)
}

/// Returns `true` if the token is a keyword used in the language.
pub fn is_used_keyword(&self) -> bool {
self.is_non_raw_ident_where(sp::Ident::is_used_keyword)
self.non_raw_ident().is_some_and(sp::Ident::is_used_keyword)
}

/// Returns `true` if the token is a keyword reserved for possible future use.
pub fn is_unused_keyword(&self) -> bool {
self.is_non_raw_ident_where(sp::Ident::is_unused_keyword)
self.non_raw_ident().is_some_and(sp::Ident::is_unused_keyword)
}

/// Returns `true` if the token is either a special identifier or a keyword.
pub fn is_reserved_ident(&self) -> bool {
self.is_non_raw_ident_where(sp::Ident::is_reserved)
self.ident().is_some_and(|(id, kind)| ident_of_kind_is_reserved(id, kind))
}

pub fn is_non_reserved_ident(&self) -> bool {
self.non_reserved_ident().is_some()
}

pub fn non_reserved_ident(&self) -> Option<sp::Ident> {
self.ident()
.filter(|&(id, raw)| raw == IdentIsRaw::Yes || !sp::Ident::is_reserved(id))
.map(|(id, _)| id)
self.ident().filter(|&(id, kind)| !ident_of_kind_is_reserved(id, kind)).map(|(id, _)| id)
}

/// Returns `true` if the token is the identifier `true` or `false`.
pub fn is_bool_lit(&self) -> bool {
self.is_non_raw_ident_where(|id| id.name.is_bool_lit())
self.non_raw_ident().is_some_and(|id| id.name.is_bool_lit())
}

pub fn is_numeric_lit(&self) -> bool {
Expand All @@ -995,11 +997,11 @@ impl Token {
matches!(self.kind, Literal(Lit { kind: LitKind::Integer, .. }))
}

/// Returns `true` if the token is a non-raw identifier for which `pred` holds.
pub fn is_non_raw_ident_where(&self, pred: impl FnOnce(sp::Ident) -> bool) -> bool {
/// Returns an identifier if this token is a non-raw identifier.
pub fn non_raw_ident(&self) -> Option<sp::Ident> {
match self.ident() {
Some((id, IdentIsRaw::No)) => pred(id),
_ => false,
Some((id, IdentKind::Normal | IdentKind::ForcedKeyword)) => Some(id),
_ => None,
}
}

Expand Down Expand Up @@ -1076,8 +1078,8 @@ impl Token {
(Colon, Colon) => PathSep,
(Colon, _) => return None,

(SingleQuote, Ident(name, is_raw)) => {
Lifetime(Symbol::intern(&format!("'{name}")), *is_raw)
(SingleQuote, Ident(name, kind)) => {
Lifetime(Symbol::intern(&format!("'{name}")), *kind)
}
(SingleQuote, _) => return None,

Expand Down Expand Up @@ -1105,6 +1107,16 @@ impl PartialEq<TokenKind> for Token {
}
}

pub fn ident_of_kind_is_reserved(id: sp::Ident, kind: IdentKind) -> bool {
match kind {
IdentKind::Normal => id.is_reserved(),
IdentKind::Raw => false,
// Purely for better diagnostics we return true here even if the identifier isn't actually
// reserved. That's because clearly the user has requested the identifier to be reserved.
IdentKind::ForcedKeyword => true,
}
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, StableHash)]
pub enum NtPatKind {
// Matches or-patterns. Was written using `pat` in edition 2021 or later.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/tokenstream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,7 @@ impl TokenStream {
DelimSpacing::new(Spacing::JointHidden, Spacing::Alone),
Delimiter::Bracket,
[
TokenTree::token_alone(token::Ident(sym::doc, token::IdentIsRaw::No), span),
TokenTree::token_alone(token::Ident(sym::doc, token::IdentKind::Normal), span),
TokenTree::token_alone(token::Eq, span),
TokenTree::token_alone(
TokenKind::lit(token::StrRaw(num_of_hashes), data, None),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(explicit_tail_calls, "`become` expression is experimental");
gate_all!(final_associated_functions, "`final` on trait functions is experimental");
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
gate_all!(forced_keywords, "forced keywords are experimental");
gate_all!(frontmatter, "frontmatters are experimental");
gate_all!(gen_blocks, "gen blocks are experimental");
gate_all!(generic_const_items, "generic const items are experimental");
Expand Down
28 changes: 12 additions & 16 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,20 +364,16 @@ fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {

// IDENT + `!`: `println!()`, but `if !x { ... }` needs a space after the `if`
(
Tok(tk::Token { kind: tk::Ident(sym, is_raw), span }, _),
Tok(tk::Token { kind: tk::Ident(sym, kind), span }, _),
Tok(tk::Token { kind: tk::Bang, .. }, _),
) if !Ident::new(*sym, *span).is_reserved() || matches!(is_raw, tk::IdentIsRaw::Yes) => {
false
}
) if !Ident::new(*sym, *span).is_reserved() || matches!(kind, tk::IdentKind::Raw) => false,

// IDENT|`fn`|`Self`|`pub` + `(`: `f(3)`, `fn(x: u8)`, `Self()`, `pub(crate)`,
// but `let (a, b) = (1, 2)` needs a space after the `let`
(Tok(tk::Token { kind: tk::Ident(sym, is_raw), span }, _), Del(_, _, Parenthesis, _))
if !Ident::new(*sym, *span).is_reserved()
|| *sym == kw::Fn
|| *sym == kw::SelfUpper
|| *sym == kw::Pub
|| matches!(is_raw, tk::IdentIsRaw::Yes) =>
(&Tok(tk::Token { kind: tk::Ident(sym, kind), span }, _), Del(_, _, Parenthesis, _))
if kind == tk::IdentKind::Raw
|| matches!(sym, kw::Fn | kw::SelfUpper | kw::Pub)
|| !Ident::new(sym, span).is_reserved() =>
{
false
}
Expand Down Expand Up @@ -1076,17 +1072,17 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
tk::Literal(lit) => literal_to_string(lit).into(),

/* Name components */
tk::Ident(name, is_raw) => {
IdentPrinter::new(name, is_raw.to_print_mode_ident(), convert_dollar_crate)
tk::Ident(name, kind) => {
IdentPrinter::new(name, kind.to_print_mode_ident(), convert_dollar_crate)
.to_string()
.into()
}
tk::NtIdent(ident, is_raw) => {
IdentPrinter::for_ast_ident(ident, is_raw.to_print_mode_ident()).to_string().into()
tk::NtIdent(ident, kind) => {
IdentPrinter::for_ast_ident(ident, kind.to_print_mode_ident()).to_string().into()
}

tk::Lifetime(name, is_raw) | tk::NtLifetime(Ident { name, .. }, is_raw) => {
IdentPrinter::new(name, is_raw.to_print_mode_lifetime(), None).to_string().into()
tk::Lifetime(name, kind) | tk::NtLifetime(Ident { name, .. }, kind) => {
IdentPrinter::new(name, kind.to_print_mode_lifetime(), None).to_string().into()
}

/* Other */
Expand Down
Loading
Loading