Skip to content

Commit 1047c0f

Browse files
Fix invalid handling of field followed by negated macro call
1 parent 61cc47e commit 1047c0f

File tree

2 files changed

+77
-35
lines changed

2 files changed

+77
-35
lines changed

src/librustdoc/html/highlight.rs

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -823,15 +823,31 @@ impl<'a> PeekIter<'a> {
823823
self.peek_pos += 1;
824824
if self.peek_pos - 1 < self.stored.len() {
825825
self.stored.get(self.peek_pos - 1)
826-
} else if let Some(next) = self.iter.next() {
827-
self.stored.push_back(next);
828-
self.stored.back()
829826
} else {
830-
None
827+
if let Some(next) = self.iter.next() {
828+
self.stored.push_back(next);
829+
self.stored.back()
830+
} else {
831+
None
832+
}
831833
}
832834
.copied()
833835
}
834836

837+
fn peek_next_if<F: Fn((TokenKind, &'a str)) -> bool>(
838+
&mut self,
839+
f: F,
840+
) -> Option<(TokenKind, &'a str)> {
841+
let next = self.peek_next()?;
842+
if f(next) {
843+
Some(next)
844+
} else {
845+
// We go one step back.
846+
self.peek_pos -= 1;
847+
None
848+
}
849+
}
850+
835851
fn stop_peeking(&mut self) {
836852
self.peek_pos = 0;
837853
}
@@ -903,18 +919,19 @@ fn classify<'src>(
903919
}
904920
}
905921

906-
if let Some((TokenKind::Colon | TokenKind::Ident, _)) = classifier.tokens.peek() {
907-
let tokens = classifier.get_full_ident_path();
908-
for &(token, start, end) in &tokens {
909-
let text = &classifier.src[start..end];
910-
classifier.advance(token, text, sink, start as u32);
911-
classifier.byte_pos += text.len() as u32;
912-
}
913-
if !tokens.is_empty() {
914-
continue;
922+
if let Some((TokenKind::Colon | TokenKind::Ident, _)) = classifier.tokens.peek()
923+
&& let Some(nb_items) = classifier.get_full_ident_path()
924+
{
925+
let start = classifier.byte_pos as usize;
926+
let mut len = 0;
927+
for _ in 0..nb_items {
928+
if let Some((_, text, _)) = classifier.next() {
929+
len += text.len();
930+
}
915931
}
916-
}
917-
if let Some((token, text, before)) = classifier.next() {
932+
let text = &classifier.src[start..start + len];
933+
classifier.advance(TokenKind::Ident, text, sink, start as u32);
934+
} else if let Some((token, text, before)) = classifier.next() {
918935
classifier.advance(token, text, sink, before);
919936
} else {
920937
break;
@@ -957,47 +974,47 @@ impl<'src> Classifier<'src> {
957974
}
958975

959976
/// Concatenate colons and idents as one when possible.
960-
fn get_full_ident_path(&mut self) -> Vec<(TokenKind, usize, usize)> {
961-
let start = self.byte_pos as usize;
962-
let mut pos = start;
977+
fn get_full_ident_path(&mut self) -> Option<usize> {
963978
let mut has_ident = false;
979+
let mut nb_items = 0;
964980

965-
loop {
981+
let ret = loop {
966982
let mut nb = 0;
967-
while let Some((TokenKind::Colon, _)) = self.tokens.peek() {
968-
self.tokens.next();
983+
while self.tokens.peek_next_if(|(token, _)| token == TokenKind::Colon).is_some() {
969984
nb += 1;
985+
nb_items += 1;
970986
}
971987
// Ident path can start with "::" but if we already have content in the ident path,
972988
// the "::" is mandatory.
973989
if has_ident && nb == 0 {
974-
return vec![(TokenKind::Ident, start, pos)];
990+
break Some(nb_items);
975991
} else if nb != 0 && nb != 2 {
976992
if has_ident {
977-
return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)];
993+
// Following `;` will be handled on its own.
994+
break Some(nb_items - 1);
978995
} else {
979-
return vec![(TokenKind::Colon, start, pos + nb)];
996+
break None;
980997
}
981998
}
982999

983-
if let Some((TokenKind::Ident, text)) = self.tokens.peek()
1000+
if let Some((TokenKind::Ident, text)) =
1001+
self.tokens.peek_next_if(|(token, _)| token == TokenKind::Ident)
9841002
&& let symbol = Symbol::intern(text)
9851003
&& (symbol.is_path_segment_keyword() || !is_keyword(symbol))
9861004
{
987-
// We only "add" the colon if there is an ident behind.
988-
pos += text.len() + nb;
9891005
has_ident = true;
990-
self.tokens.next();
1006+
nb_items += 1;
9911007
} else if nb > 0 && has_ident {
992-
return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)];
993-
} else if nb > 0 {
994-
return vec![(TokenKind::Colon, start, start + nb)];
1008+
// Following `;` will be handled on its own.
1009+
break Some(nb_items - 1);
9951010
} else if has_ident {
996-
return vec![(TokenKind::Ident, start, pos)];
1011+
break Some(nb_items);
9971012
} else {
998-
return Vec::new();
1013+
break None;
9991014
}
1000-
}
1015+
};
1016+
self.tokens.stop_peeking();
1017+
ret
10011018
}
10021019

10031020
/// Wraps the tokens iteration to ensure that the `byte_pos` is always correct.
@@ -1243,7 +1260,6 @@ impl<'src> Classifier<'src> {
12431260
Class::MacroNonTerminal
12441261
}
12451262
TokenKind::Ident => {
1246-
let file_span = self.file_span;
12471263
let span = || new_span(before, text, file_span);
12481264

12491265
match text {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// This test ensures that the macro expansion is correctly handled in cases like:
2+
// `field: !f!`, because the `:` was simply not considered because of how paths
3+
// are handled.
4+
5+
//@ compile-flags: -Zunstable-options --generate-macro-expansion
6+
7+
#![crate_name = "foo"]
8+
9+
//@ has 'src/foo/field-followed-by-exclamation.rs.html'
10+
11+
struct Bar {
12+
bla: bool,
13+
}
14+
15+
macro_rules! f {
16+
() => {{ false }}
17+
}
18+
19+
const X: Bar = Bar {
20+
//@ has - '//*[@class="expansion"]/*[@class="original"]/*[@class="macro"]' 'f!'
21+
//@ has - '//*[@class="expansion"]/*[@class="original"]' 'f!()'
22+
//@ has - '//*[@class="expansion"]/*[@class="expanded"]' '{ false }'
23+
// It includes both original and expanded code.
24+
//@ has - '//*[@class="expansion"]' ' bla: !{ false }f!()'
25+
bla: !f!(),
26+
};

0 commit comments

Comments
 (0)