diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index fad15573cde0c..7b74daf83a92b 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -10,6 +10,7 @@ use std::collections::VecDeque;
use std::fmt::{self, Display, Write};
use std::iter;
+use arrayvec::ArrayVec;
use rustc_data_structures::fx::FxIndexMap;
use rustc_lexer::{Cursor, FrontmatterAllowed, LiteralKind, TokenKind};
use rustc_span::edition::Edition;
@@ -818,7 +819,7 @@ impl<'src> Classifier<'src> {
}
/// Concatenate colons and idents as one when possible.
- fn get_full_ident_path(&mut self) -> Vec<(TokenKind, usize, usize)> {
+ fn get_full_ident_path(&mut self) -> ArrayVec<(TokenKind, usize, usize), 2> {
let start = self.byte_pos as usize;
let mut pos = start;
let mut has_ident = false;
@@ -832,13 +833,16 @@ impl<'src> Classifier<'src> {
// Ident path can start with "::" but if we already have content in the ident path,
// the "::" is mandatory.
if has_ident && nb == 0 {
- return vec![(TokenKind::Ident, start, pos)];
+ return ArrayVec::from_iter([(TokenKind::Ident, start, pos)]);
} else if nb != 0 && nb != 2 {
- if has_ident {
- return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)];
+ return if has_ident {
+ ArrayVec::from_iter([
+ (TokenKind::Ident, start, pos),
+ (TokenKind::Colon, pos, pos + nb),
+ ])
} else {
- return vec![(TokenKind::Colon, start, pos + nb)];
- }
+ ArrayVec::from_iter([(TokenKind::Colon, start, pos + nb)])
+ };
}
if let Some((None, text)) = self.tokens.peek().map(|(token, text)| {
@@ -854,15 +858,21 @@ impl<'src> Classifier<'src> {
pos += text.len() + nb;
has_ident = true;
self.tokens.next();
- } else if nb > 0 && has_ident {
- return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)];
+ continue;
+ }
+
+ return if nb > 0 && has_ident {
+ ArrayVec::from_iter([
+ (TokenKind::Ident, start, pos),
+ (TokenKind::Colon, pos, pos + nb),
+ ])
} else if nb > 0 {
- return vec![(TokenKind::Colon, start, start + nb)];
+ ArrayVec::from_iter([(TokenKind::Colon, start, start + nb)])
} else if has_ident {
- return vec![(TokenKind::Ident, start, pos)];
+ ArrayVec::from_iter([(TokenKind::Ident, start, pos)])
} else {
- return Vec::new();
- }
+ ArrayVec::new()
+ };
}
}
@@ -885,7 +895,7 @@ impl<'src> Classifier<'src> {
/// The general structure for this method is to iterate over each token,
/// possibly giving it an HTML span with a class specifying what flavor of
/// token is used.
- fn highlight(mut self, sink: &mut dyn FnMut(Span, Highlight<'src>)) {
+ fn highlight(mut self, mut sink: impl FnMut(Span, Highlight<'src>)) {
loop {
if let Some(decs) = self.decorations.as_mut() {
let byte_pos = self.byte_pos;
@@ -903,13 +913,12 @@ impl<'src> Classifier<'src> {
if self
.tokens
.peek()
- .map(|t| matches!(t.0, TokenKind::Colon | TokenKind::Ident))
- .unwrap_or(false)
+ .is_some_and(|(kind, _)| matches!(kind, TokenKind::Colon | TokenKind::Ident))
{
let tokens = self.get_full_ident_path();
for (token, start, end) in &tokens {
let text = &self.src[*start..*end];
- self.advance(*token, text, sink, *start as u32);
+ self.advance(*token, text, &mut sink, *start as u32);
self.byte_pos += text.len() as u32;
}
if !tokens.is_empty() {
@@ -917,7 +926,7 @@ impl<'src> Classifier<'src> {
}
}
if let Some((token, text, before)) = self.next() {
- self.advance(token, text, sink, before);
+ self.advance(token, text, &mut sink, before);
} else {
break;
}
@@ -934,26 +943,28 @@ impl<'src> Classifier<'src> {
&mut self,
token: TokenKind,
text: &'src str,
- sink: &mut dyn FnMut(Span, Highlight<'src>),
+ mut sink: impl FnMut(Span, Highlight<'src>),
before: u32,
) {
let lookahead = self.peek();
let file_span = self.file_span;
- let no_highlight = |sink: &mut dyn FnMut(_, _)| {
- sink(new_span(before, text, file_span), Highlight::Token { text, class: None })
- };
- let whitespace = |sink: &mut dyn FnMut(_, _)| {
+ let no_highlight =
+ || (new_span(before, text, file_span), Highlight::Token { text, class: None });
+ let mut whitespace = |class| {
let mut start = 0u32;
for part in text.split('\n').intersperse("\n").filter(|s| !s.is_empty()) {
sink(
new_span(before + start, part, file_span),
- Highlight::Token { text: part, class: None },
+ Highlight::Token { text: part, class },
);
start += part.len() as u32;
}
};
let class = match token {
- TokenKind::Whitespace => return whitespace(sink),
+ TokenKind::Whitespace => {
+ whitespace(None);
+ return;
+ }
TokenKind::LineComment { doc_style } | TokenKind::BlockComment { doc_style, .. } => {
if doc_style.is_some() {
Class::DocComment
@@ -974,7 +985,10 @@ impl<'src> Classifier<'src> {
// or a reference or pointer type. Unless, of course, it looks like
// a logical and or a multiplication operator: `&&` or `* `.
TokenKind::Star => match self.tokens.peek() {
- Some((TokenKind::Whitespace, _)) => return whitespace(sink),
+ Some((TokenKind::Whitespace, _)) => {
+ whitespace(None);
+ return;
+ }
Some((TokenKind::Ident, "mut")) => {
self.next();
sink(
@@ -1004,7 +1018,10 @@ impl<'src> Classifier<'src> {
sink(DUMMY_SP, Highlight::Token { text: "&=", class: None });
return;
}
- Some((TokenKind::Whitespace, _)) => return whitespace(sink),
+ Some((TokenKind::Whitespace, _)) => {
+ whitespace(None);
+ return;
+ }
Some((TokenKind::Ident, "mut")) => {
self.next();
sink(
@@ -1028,7 +1045,11 @@ impl<'src> Classifier<'src> {
sink(DUMMY_SP, Highlight::Token { text: "=>", class: None });
return;
}
- _ => return no_highlight(sink),
+ _ => {
+ let (span, highlight) = no_highlight();
+ sink(span, highlight);
+ return;
+ }
},
TokenKind::Minus if lookahead == Some(TokenKind::Gt) => {
self.next();
@@ -1045,7 +1066,11 @@ impl<'src> Classifier<'src> {
| TokenKind::Percent
| TokenKind::Bang
| TokenKind::Lt
- | TokenKind::Gt => return no_highlight(sink),
+ | TokenKind::Gt => {
+ let (span, highlight) = no_highlight();
+ sink(span, highlight);
+ return;
+ }
// Miscellaneous, no highlighting.
TokenKind::Dot
@@ -1060,7 +1085,11 @@ impl<'src> Classifier<'src> {
| TokenKind::Tilde
| TokenKind::Colon
| TokenKind::Frontmatter { .. }
- | TokenKind::Unknown => return no_highlight(sink),
+ | TokenKind::Unknown => {
+ let (span, highlight) = no_highlight();
+ sink(span, highlight);
+ return;
+ }
TokenKind::Question => Class::QuestionMark,
@@ -1069,7 +1098,11 @@ impl<'src> Classifier<'src> {
self.in_macro_nonterminal = true;
Class::MacroNonTerminal
}
- _ => return no_highlight(sink),
+ _ => {
+ let (span, highlight) = no_highlight();
+ sink(span, highlight);
+ return;
+ }
},
// This might be the start of an attribute. We're going to want to
@@ -1100,9 +1133,11 @@ impl<'src> Classifier<'src> {
Highlight::EnterSpan { class: Class::Attribute },
);
}
- _ => (),
+ _ => {}
}
- return no_highlight(sink);
+ let (span, highlight) = no_highlight();
+ sink(span, highlight);
+ return;
}
TokenKind::CloseBracket => {
if self.in_attribute {
@@ -1114,7 +1149,9 @@ impl<'src> Classifier<'src> {
sink(DUMMY_SP, Highlight::ExitSpan);
return;
}
- return no_highlight(sink);
+ let (span, highlight) = no_highlight();
+ sink(span, highlight);
+ return;
}
TokenKind::Literal { kind, .. } => match kind {
// Text literals.
@@ -1129,7 +1166,11 @@ impl<'src> Classifier<'src> {
// Number literals.
LiteralKind::Float { .. } | LiteralKind::Int { .. } => Class::Number,
},
- TokenKind::GuardedStrPrefix => return no_highlight(sink),
+ TokenKind::GuardedStrPrefix => {
+ let (span, highlight) = no_highlight();
+ sink(span, highlight);
+ return;
+ }
TokenKind::Ident | TokenKind::RawIdent if lookahead == Some(TokenKind::Bang) => {
self.in_macro = true;
let span = new_span(before, text, file_span);
@@ -1165,14 +1206,7 @@ impl<'src> Classifier<'src> {
};
// Anything that didn't return above is the simple case where we the
// class just spans a single token, so we can use the `string` method.
- let mut start = 0u32;
- for part in text.split('\n').intersperse("\n").filter(|s| !s.is_empty()) {
- sink(
- new_span(before + start, part, file_span),
- Highlight::Token { text: part, class: Some(class) },
- );
- start += part.len() as u32;
- }
+ whitespace(Some(class));
}
fn peek(&mut self) -> Option {