Skip to content

Commit 47075ae

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

File tree

1 file changed

+53
-25
lines changed

1 file changed

+53
-25
lines changed

src/librustdoc/html/highlight.rs

Lines changed: 53 additions & 25 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 KEYWORDS_FOLLOWABLE_BY_VALUE: &[&str] = &["if", "while", "match", "break", "return"];
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,49 @@ 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-
{
1235+
TokenKind::RawIdent if let Some((TokenKind::Bang, _)) = self.peek_non_trivia() => {
12221236
self.in_macro = true;
12231237
let span = new_span(before, text, file_span);
12241238
sink(DUMMY_SP, Highlight::EnterSpan { class: Class::Macro(span) });
12251239
sink(span, Highlight::Token { text, class: None });
12261240
return;
12271241
}
1228-
TokenKind::Ident => self.classify_ident(before, text),
1242+
// Macro non-terminals (meta vars) take precedence.
1243+
TokenKind::Ident if self.in_macro_nonterminal => {
1244+
self.in_macro_nonterminal = false;
1245+
Class::MacroNonTerminal
1246+
}
1247+
TokenKind::Ident => {
1248+
let file_span = self.file_span;
1249+
let span = || new_span(before, text, file_span);
1250+
1251+
match text {
1252+
"ref" | "mut" => Class::RefKeyWord,
1253+
"false" | "true" => Class::Bool,
1254+
"self" | "Self" => Class::Self_(span()),
1255+
"Option" | "Result" => Class::PreludeTy(span()),
1256+
"Some" | "None" | "Ok" | "Err" => Class::PreludeVal(span()),
1257+
_ if self.is_weak_keyword(text) || is_keyword(Symbol::intern(text)) => {
1258+
// So if it's not a keyword which can be followed by a value (like `if` or
1259+
// `return`) and the next non-whitespace token is a `!`, then we consider
1260+
// it's a macro.
1261+
if !KEYWORDS_FOLLOWABLE_BY_VALUE.contains(&text)
1262+
&& matches!(self.peek_non_trivia(), Some((TokenKind::Bang, _)))
1263+
{
1264+
self.new_macro_span(text, sink, before, file_span);
1265+
return;
1266+
}
1267+
Class::KeyWord
1268+
}
1269+
// If it's not a keyword and the next non whitespace token is a `!`, then
1270+
// we consider it's a macro.
1271+
_ if matches!(self.peek_non_trivia(), Some((TokenKind::Bang, _))) => {
1272+
self.new_macro_span(text, sink, before, file_span);
1273+
return;
1274+
}
1275+
_ => Class::Ident(span()),
1276+
}
1277+
}
12291278
TokenKind::RawIdent | TokenKind::UnknownPrefix | TokenKind::InvalidIdent => {
12301279
Class::Ident(new_span(before, text, file_span))
12311280
}
@@ -1246,27 +1295,6 @@ impl<'src> Classifier<'src> {
12461295
}
12471296
}
12481297

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-
12701298
fn is_weak_keyword(&mut self, text: &str) -> bool {
12711299
// NOTE: `yeet` (`do yeet $expr`), `catch` (`do catch $block`), `default` (specialization),
12721300
// `contract_{ensures,requires}`, `builtin` (builtin_syntax) & `reuse` (fn_delegation) are

0 commit comments

Comments
 (0)