1
Fork 0

Only have one source of truth for keywords.

`rustc_symbol` is the source of truth for keywords.

rustdoc has its own implicit definition of keywords, via the
`is_doc_keyword`. It (presumably) intends to include all keywords, but
it omits `yeet`.

rustfmt has its own explicit list of Rust keywords. It also (presumably)
intends to include all keywords, but it omits `await`, `builtin`, `gen`,
`macro_rules`, `raw`, `reuse`, `safe`, and `yeet`. Also, it does linear
searches through this list, which is inefficient.

This commit fixes all of the above problems by introducing a new
predicate `is_any_keyword` in rustc and using it in rustdoc and rustfmt.
It documents that it's not the right predicate in most cases.
This commit is contained in:
Nicholas Nethercote 2024-12-13 21:07:58 +11:00
parent 64abe8be33
commit 1564318482
4 changed files with 31 additions and 78 deletions

View file

@ -903,7 +903,8 @@ impl Token {
self.is_non_raw_ident_where(|id| id.name == kw) self.is_non_raw_ident_where(|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. /// 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 { pub fn is_keyword_case(&self, kw: Symbol, case: Case) -> bool {
self.is_keyword(kw) self.is_keyword(kw)
|| (case == Case::Insensitive || (case == Case::Insensitive
@ -916,6 +917,11 @@ impl Token {
self.is_non_raw_ident_where(Ident::is_path_segment_keyword) self.is_non_raw_ident_where(Ident::is_path_segment_keyword)
} }
/// Don't use this unless you're doing something very loose and heuristic-y.
pub fn is_any_keyword(&self) -> bool {
self.is_non_raw_ident_where(Ident::is_any_keyword)
}
/// Returns true for reserved identifiers used internally for elided lifetimes, /// Returns true for reserved identifiers used internally for elided lifetimes,
/// unnamed method parameters, crate root module, error recovery etc. /// unnamed method parameters, crate root module, error recovery etc.
pub fn is_special_ident(&self) -> bool { pub fn is_special_ident(&self) -> bool {

View file

@ -815,8 +815,8 @@ impl<'a> Parser<'a> {
// Otherwise, check the previous token with all the keywords as possible candidates. // Otherwise, check the previous token with all the keywords as possible candidates.
// This handles code like `Struct Human;` and `While a < b {}`. // This handles code like `Struct Human;` and `While a < b {}`.
// We check the previous token only when the current token is an identifier to avoid false // We check the previous token only when the current token is an identifier to avoid
// positives like suggesting keyword `for` for `extern crate foo {}`. // false positives like suggesting keyword `for` for `extern crate foo {}`.
if let Some(misspelled_kw) = find_similar_kw(prev_ident, &all_keywords) { if let Some(misspelled_kw) = find_similar_kw(prev_ident, &all_keywords) {
err.subdiagnostic(misspelled_kw); err.subdiagnostic(misspelled_kw);
// We don't want other suggestions to be added as they are most likely meaningless // We don't want other suggestions to be added as they are most likely meaningless

View file

@ -2589,6 +2589,11 @@ pub mod sym {
} }
impl Symbol { impl Symbol {
/// Don't use this unless you're doing something very loose and heuristic-y.
pub fn is_any_keyword(self) -> bool {
self >= kw::As && self <= kw::Yeet
}
fn is_special(self) -> bool { fn is_special(self) -> bool {
self <= kw::Underscore self <= kw::Underscore
} }
@ -2645,6 +2650,11 @@ impl Symbol {
} }
impl Ident { impl Ident {
/// Don't use this unless you're doing something very loose and heuristic-y.
pub fn is_any_keyword(self) -> bool {
self.name.is_any_keyword()
}
/// Returns `true` for reserved identifiers used internally for elided lifetimes, /// Returns `true` for reserved identifiers used internally for elided lifetimes,
/// unnamed method parameters, crate root module, error recovery etc. /// unnamed method parameters, crate root module, error recovery etc.
pub fn is_special(self) -> bool { pub fn is_special(self) -> bool {

View file

@ -4,8 +4,7 @@ use rustc_ast::{ast, ptr};
use rustc_parse::MACRO_ARGUMENTS; use rustc_parse::MACRO_ARGUMENTS;
use rustc_parse::parser::{ForceCollect, Parser, Recovery}; use rustc_parse::parser::{ForceCollect, Parser, Recovery};
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
use rustc_span::Symbol; use rustc_span::symbol;
use rustc_span::symbol::{self, kw};
use crate::macros::MacroArg; use crate::macros::MacroArg;
use crate::rewrite::RewriteContext; use crate::rewrite::RewriteContext;
@ -82,18 +81,18 @@ pub(crate) struct ParsedMacroArgs {
} }
fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> { fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
for &keyword in RUST_KW.iter() { if parser.token.is_any_keyword()
if parser.token.is_keyword(keyword) && parser.look_ahead(1, |t| *t == TokenKind::Eof || *t == TokenKind::Comma)
&& parser.look_ahead(1, |t| *t == TokenKind::Eof || *t == TokenKind::Comma) {
{ let keyword = parser.token.ident().unwrap().0.name;
parser.bump(); parser.bump();
return Some(MacroArg::Keyword( Some(MacroArg::Keyword(
symbol::Ident::with_dummy_span(keyword), symbol::Ident::with_dummy_span(keyword),
parser.prev_token.span, parser.prev_token.span,
)); ))
} } else {
None
} }
None
} }
pub(crate) fn parse_macro_args( pub(crate) fn parse_macro_args(
@ -169,65 +168,3 @@ pub(crate) fn parse_expr(
let mut parser = build_parser(context, tokens); let mut parser = build_parser(context, tokens);
parser.parse_expr().ok() parser.parse_expr().ok()
} }
const RUST_KW: [Symbol; 59] = [
kw::PathRoot,
kw::DollarCrate,
kw::Underscore,
kw::As,
kw::Box,
kw::Break,
kw::Const,
kw::Continue,
kw::Crate,
kw::Else,
kw::Enum,
kw::Extern,
kw::False,
kw::Fn,
kw::For,
kw::If,
kw::Impl,
kw::In,
kw::Let,
kw::Loop,
kw::Match,
kw::Mod,
kw::Move,
kw::Mut,
kw::Pub,
kw::Ref,
kw::Return,
kw::SelfLower,
kw::SelfUpper,
kw::Static,
kw::Struct,
kw::Super,
kw::Trait,
kw::True,
kw::Type,
kw::Unsafe,
kw::Use,
kw::Where,
kw::While,
kw::Abstract,
kw::Become,
kw::Do,
kw::Final,
kw::Macro,
kw::Override,
kw::Priv,
kw::Typeof,
kw::Unsized,
kw::Virtual,
kw::Yield,
kw::Dyn,
kw::Async,
kw::Try,
kw::UnderscoreLifetime,
kw::StaticLifetime,
kw::Auto,
kw::Catch,
kw::Default,
kw::Union,
];