Skip to content

Commit cd8006e

Browse files
committed
rustdoc: Recognize more weak keywords when highlighting Rust code
1 parent d8d9993 commit cd8006e

File tree

1 file changed

+56
-58
lines changed

1 file changed

+56
-58
lines changed

src/librustdoc/html/highlight.rs

Lines changed: 56 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -754,21 +754,6 @@ impl<'a> Iterator for TokenIter<'a> {
754754
}
755755
}
756756

757-
/// Classifies into identifier class; returns `None` if this is a non-keyword identifier.
758-
fn get_real_ident_class(text: &str, allow_path_keywords: bool) -> Option<Class> {
759-
let ignore: &[&str] =
760-
if allow_path_keywords { &["self", "Self", "super", "crate"] } else { &["self", "Self"] };
761-
if ignore.contains(&text) {
762-
return None;
763-
}
764-
Some(match text {
765-
"ref" | "mut" => Class::RefKeyWord,
766-
"false" | "true" => Class::Bool,
767-
_ if Symbol::intern(text).is_reserved(|| Edition::Edition2021) => Class::KeyWord,
768-
_ => return None,
769-
})
770-
}
771-
772757
/// This iterator comes from the same idea than "Peekable" except that it allows to "peek" more than
773758
/// just the next item by using `peek_next`. The `peek` method always returns the next item after
774759
/// the current one whereas `peek_next` will return the next item after the last one peeked.
@@ -786,16 +771,16 @@ impl<'a> PeekIter<'a> {
786771
Self { stored: VecDeque::new(), peek_pos: 0, iter }
787772
}
788773
/// Returns the next item after the current one. It doesn't interfere with `peek_next` output.
789-
fn peek(&mut self) -> Option<&(TokenKind, &'a str)> {
774+
fn peek(&mut self) -> Option<(TokenKind, &'a str)> {
790775
if self.stored.is_empty()
791776
&& let Some(next) = self.iter.next()
792777
{
793778
self.stored.push_back(next);
794779
}
795-
self.stored.front()
780+
self.stored.front().copied()
796781
}
797782
/// Returns the next item after the last one peeked. It doesn't interfere with `peek` output.
798-
fn peek_next(&mut self) -> Option<&(TokenKind, &'a str)> {
783+
fn peek_next(&mut self) -> Option<(TokenKind, &'a str)> {
799784
self.peek_pos += 1;
800785
if self.peek_pos - 1 < self.stored.len() {
801786
self.stored.get(self.peek_pos - 1)
@@ -805,6 +790,7 @@ impl<'a> PeekIter<'a> {
805790
} else {
806791
None
807792
}
793+
.copied()
808794
}
809795

810796
fn stop_peeking(&mut self) {
@@ -951,15 +937,9 @@ impl<'src> Classifier<'src> {
951937
}
952938
}
953939

954-
if let Some((None, text)) = self.tokens.peek().map(|(token, text)| {
955-
if *token == TokenKind::Ident {
956-
let class = get_real_ident_class(text, true);
957-
(class, text)
958-
} else {
959-
// Doesn't matter which Class we put in here...
960-
(Some(Class::Comment), text)
961-
}
962-
}) {
940+
if let Some((TokenKind::Ident, text)) = self.tokens.peek()
941+
&& !ident_is_keyword(text)
942+
{
963943
// We only "add" the colon if there is an ident behind.
964944
pos += text.len() + nb;
965945
has_ident = true;
@@ -1197,34 +1177,15 @@ impl<'src> Classifier<'src> {
11971177
},
11981178
TokenKind::GuardedStrPrefix => return no_highlight(sink),
11991179
TokenKind::Ident | TokenKind::RawIdent
1200-
if self.peek_non_whitespace() == Some(TokenKind::Bang) =>
1180+
if let Some((TokenKind::Bang, _)) = self.peek_non_trivia() =>
12011181
{
12021182
self.in_macro = true;
12031183
let span = new_span(before, text, file_span);
12041184
sink(DUMMY_SP, Highlight::EnterSpan { class: Class::Macro(span) });
12051185
sink(span, Highlight::Token { text, class: None });
12061186
return;
12071187
}
1208-
TokenKind::Ident => {
1209-
match get_real_ident_class(text, false) {
1210-
None => match text {
1211-
"Option" | "Result" => Class::PreludeTy(new_span(before, text, file_span)),
1212-
"Some" | "None" | "Ok" | "Err" => {
1213-
Class::PreludeVal(new_span(before, text, file_span))
1214-
}
1215-
// "union" is a weak keyword and is only considered as a keyword when declaring
1216-
// a union type.
1217-
"union" if self.check_if_is_union_keyword() => Class::KeyWord,
1218-
_ if self.in_macro_nonterminal => {
1219-
self.in_macro_nonterminal = false;
1220-
Class::MacroNonTerminal
1221-
}
1222-
"self" | "Self" => Class::Self_(new_span(before, text, file_span)),
1223-
_ => Class::Ident(new_span(before, text, file_span)),
1224-
},
1225-
Some(c) => c,
1226-
}
1227-
}
1188+
TokenKind::Ident => self.classify_ident(before, text),
12281189
TokenKind::RawIdent | TokenKind::UnknownPrefix | TokenKind::InvalidIdent => {
12291190
Class::Ident(new_span(before, text, file_span))
12301191
}
@@ -1245,25 +1206,62 @@ impl<'src> Classifier<'src> {
12451206
}
12461207
}
12471208

1209+
fn classify_ident(&mut self, before: u32, text: &'src str) -> Class {
1210+
// Macro non-terminals (meta vars) take precedence.
1211+
if self.in_macro_nonterminal {
1212+
self.in_macro_nonterminal = false;
1213+
return Class::MacroNonTerminal;
1214+
}
1215+
1216+
let file_span = self.file_span;
1217+
let span = || new_span(before, text, file_span);
1218+
1219+
match text {
1220+
"ref" | "mut" => Class::RefKeyWord,
1221+
"false" | "true" => Class::Bool,
1222+
"self" | "Self" => Class::Self_(span()),
1223+
"Option" | "Result" => Class::PreludeTy(span()),
1224+
"Some" | "None" | "Ok" | "Err" => Class::PreludeVal(span()),
1225+
_ if ident_is_keyword(text) || self.ident_is_weak_keyword(text) => Class::KeyWord,
1226+
_ => Class::Ident(span()),
1227+
}
1228+
}
1229+
1230+
fn ident_is_weak_keyword(&mut self, text: &str) -> bool {
1231+
let matches = match text {
1232+
"auto" => |text| text == "trait", // `auto trait Trait {}` (`auto_traits`)
1233+
"pin" => |text| text == "const" || text == "mut", // `&pin mut Type` (`pin_ergonomics`)
1234+
"raw" => |text| text == "const" || text == "mut", // `&raw const local`
1235+
"safe" => |text| text == "fn" || text == "extern", // `unsafe extern { safe fn f(); }`
1236+
"union" => |_| true, // `union Untagged { field: () }`
1237+
_ => return false,
1238+
};
1239+
matches!(self.peek_non_trivia(), Some((TokenKind::Ident, text)) if matches(text))
1240+
}
1241+
12481242
fn peek(&mut self) -> Option<TokenKind> {
1249-
self.tokens.peek().map(|(token_kind, _text)| *token_kind)
1243+
self.tokens.peek().map(|(kind, _)| kind)
12501244
}
12511245

1252-
fn peek_non_whitespace(&mut self) -> Option<TokenKind> {
1253-
while let Some((token_kind, _)) = self.tokens.peek_next() {
1254-
if *token_kind != TokenKind::Whitespace {
1255-
let token_kind = *token_kind;
1256-
self.tokens.stop_peeking();
1257-
return Some(token_kind);
1246+
fn peek_non_trivia(&mut self) -> Option<(TokenKind, &str)> {
1247+
while let Some(token @ (kind, _)) = self.tokens.peek_next() {
1248+
if let TokenKind::Whitespace
1249+
| TokenKind::LineComment { doc_style: None }
1250+
| TokenKind::BlockComment { doc_style: None, .. } = kind
1251+
{
1252+
continue;
12581253
}
1254+
self.tokens.stop_peeking();
1255+
return Some(token);
12591256
}
12601257
self.tokens.stop_peeking();
12611258
None
12621259
}
1260+
}
12631261

1264-
fn check_if_is_union_keyword(&mut self) -> bool {
1265-
self.peek_non_whitespace().is_some_and(|kind| kind == TokenKind::Ident)
1266-
}
1262+
fn ident_is_keyword(text: &str) -> bool {
1263+
// FIXME(#148221): Don't hard-code the edition. The classifier should take it as an argument.
1264+
Symbol::intern(text).is_reserved(|| Edition::Edition2024)
12671265
}
12681266

12691267
fn generate_link_to_def(

0 commit comments

Comments
 (0)