Skip to content

Commit d1dda8d

Browse files
Fix invalid macro tag generation for keywords which can be followed by values
1 parent acda5e9 commit d1dda8d

File tree

1 file changed

+54
-29
lines changed

1 file changed

+54
-29
lines changed

src/librustdoc/html/highlight.rs

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,9 @@ impl<'a> Iterator for TokenIter<'a> {
789789
}
790790
}
791791

792+
/// Used to know if a keyword followed by a `!` should never be treated as a macro.
793+
const NON_MACRO_KEYWORDS: &[&str] = &["if", "while", "match", "break", "return", "impl"];
794+
792795
/// This iterator comes from the same idea than "Peekable" except that it allows to "peek" more than
793796
/// just the next item by using `peek_next`. The `peek` method always returns the next item after
794797
/// the current one whereas `peek_next` will return the next item after the last one peeked.
@@ -1010,6 +1013,19 @@ impl<'src> Classifier<'src> {
10101013
}
10111014
}
10121015

1016+
fn new_macro_span(
1017+
&mut self,
1018+
text: &'src str,
1019+
sink: &mut dyn FnMut(Span, Highlight<'src>),
1020+
before: u32,
1021+
file_span: Span,
1022+
) {
1023+
self.in_macro = true;
1024+
let span = new_span(before, text, file_span);
1025+
sink(DUMMY_SP, Highlight::EnterSpan { class: Class::Macro(span) });
1026+
sink(span, Highlight::Token { text, class: None });
1027+
}
1028+
10131029
/// Single step of highlighting. This will classify `token`, but maybe also a couple of
10141030
/// following ones as well.
10151031
///
@@ -1216,16 +1232,46 @@ impl<'src> Classifier<'src> {
12161232
LiteralKind::Float { .. } | LiteralKind::Int { .. } => Class::Number,
12171233
},
12181234
TokenKind::GuardedStrPrefix => return no_highlight(sink),
1219-
TokenKind::Ident | TokenKind::RawIdent
1220-
if let Some((TokenKind::Bang, _)) = self.peek_non_trivia() =>
1221-
{
1222-
self.in_macro = true;
1223-
let span = new_span(before, text, file_span);
1224-
sink(DUMMY_SP, Highlight::EnterSpan { class: Class::Macro(span) });
1225-
sink(span, Highlight::Token { text, class: None });
1235+
TokenKind::RawIdent if let Some((TokenKind::Bang, _)) = self.peek_non_trivia() => {
1236+
self.new_macro_span(text, sink, before, file_span);
12261237
return;
12271238
}
1228-
TokenKind::Ident => self.classify_ident(before, text),
1239+
// Macro non-terminals (meta vars) take precedence.
1240+
TokenKind::Ident if self.in_macro_nonterminal => {
1241+
self.in_macro_nonterminal = false;
1242+
Class::MacroNonTerminal
1243+
}
1244+
TokenKind::Ident => {
1245+
let file_span = self.file_span;
1246+
let span = || new_span(before, text, file_span);
1247+
1248+
match text {
1249+
"ref" | "mut" => Class::RefKeyWord,
1250+
"false" | "true" => Class::Bool,
1251+
"self" | "Self" => Class::Self_(span()),
1252+
"Option" | "Result" => Class::PreludeTy(span()),
1253+
"Some" | "None" | "Ok" | "Err" => Class::PreludeVal(span()),
1254+
_ if self.is_weak_keyword(text) || is_keyword(Symbol::intern(text)) => {
1255+
// So if it's not a keyword which can be followed by a value (like `if` or
1256+
// `return`) and the next non-whitespace token is a `!`, then we consider
1257+
// it's a macro.
1258+
if !NON_MACRO_KEYWORDS.contains(&text)
1259+
&& matches!(self.peek_non_trivia(), Some((TokenKind::Bang, _)))
1260+
{
1261+
self.new_macro_span(text, sink, before, file_span);
1262+
return;
1263+
}
1264+
Class::KeyWord
1265+
}
1266+
// If it's not a keyword and the next non whitespace token is a `!`, then
1267+
// we consider it's a macro.
1268+
_ if matches!(self.peek_non_trivia(), Some((TokenKind::Bang, _))) => {
1269+
self.new_macro_span(text, sink, before, file_span);
1270+
return;
1271+
}
1272+
_ => Class::Ident(span()),
1273+
}
1274+
}
12291275
TokenKind::RawIdent | TokenKind::UnknownPrefix | TokenKind::InvalidIdent => {
12301276
Class::Ident(new_span(before, text, file_span))
12311277
}
@@ -1246,27 +1292,6 @@ impl<'src> Classifier<'src> {
12461292
}
12471293
}
12481294

1249-
fn classify_ident(&mut self, before: u32, text: &'src str) -> Class {
1250-
// Macro non-terminals (meta vars) take precedence.
1251-
if self.in_macro_nonterminal {
1252-
self.in_macro_nonterminal = false;
1253-
return Class::MacroNonTerminal;
1254-
}
1255-
1256-
let file_span = self.file_span;
1257-
let span = || new_span(before, text, file_span);
1258-
1259-
match text {
1260-
"ref" | "mut" => Class::RefKeyWord,
1261-
"false" | "true" => Class::Bool,
1262-
"self" | "Self" => Class::Self_(span()),
1263-
"Option" | "Result" => Class::PreludeTy(span()),
1264-
"Some" | "None" | "Ok" | "Err" => Class::PreludeVal(span()),
1265-
_ if self.is_weak_keyword(text) || is_keyword(Symbol::intern(text)) => Class::KeyWord,
1266-
_ => Class::Ident(span()),
1267-
}
1268-
}
1269-
12701295
fn is_weak_keyword(&mut self, text: &str) -> bool {
12711296
// NOTE: `yeet` (`do yeet $expr`), `catch` (`do catch $block`), `default` (specialization),
12721297
// `contract_{ensures,requires}`, `builtin` (builtin_syntax) & `reuse` (fn_delegation) are

0 commit comments

Comments
 (0)