-
-
Notifications
You must be signed in to change notification settings - Fork 14.2k
[rustdoc] Fix invalid handling of field followed by negated macro call #150099
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -832,6 +832,20 @@ impl<'a> PeekIter<'a> { | |||||||||||||||||||||
| .copied() | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| fn peek_next_if<F: Fn((TokenKind, &'a str)) -> bool>( | ||||||||||||||||||||||
| &mut self, | ||||||||||||||||||||||
| f: F, | ||||||||||||||||||||||
| ) -> Option<(TokenKind, &'a str)> { | ||||||||||||||||||||||
| let next = self.peek_next()?; | ||||||||||||||||||||||
| if f(next) { | ||||||||||||||||||||||
| Some(next) | ||||||||||||||||||||||
| } else { | ||||||||||||||||||||||
| // We go one step back. | ||||||||||||||||||||||
| self.peek_pos -= 1; | ||||||||||||||||||||||
| None | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| fn stop_peeking(&mut self) { | ||||||||||||||||||||||
| self.peek_pos = 0; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
@@ -903,18 +917,19 @@ fn classify<'src>( | |||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if let Some((TokenKind::Colon | TokenKind::Ident, _)) = classifier.tokens.peek() { | ||||||||||||||||||||||
| let tokens = classifier.get_full_ident_path(); | ||||||||||||||||||||||
| for &(token, start, end) in &tokens { | ||||||||||||||||||||||
| let text = &classifier.src[start..end]; | ||||||||||||||||||||||
| classifier.advance(token, text, sink, start as u32); | ||||||||||||||||||||||
| classifier.byte_pos += text.len() as u32; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| if !tokens.is_empty() { | ||||||||||||||||||||||
| continue; | ||||||||||||||||||||||
| if let Some((TokenKind::Colon | TokenKind::Ident, _)) = classifier.tokens.peek() | ||||||||||||||||||||||
| && let Some(nb_items) = classifier.get_full_ident_path() | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| let start = classifier.byte_pos as usize; | ||||||||||||||||||||||
| let mut len = 0; | ||||||||||||||||||||||
| for _ in 0..nb_items { | ||||||||||||||||||||||
| if let Some((_, text, _)) = classifier.next() { | ||||||||||||||||||||||
| len += text.len(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Code doesn't need this
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is much better 😁 |
||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
Comment on lines
+924
to
929
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Take it or leave it: I know you're not a big fan of iterator combinators and the functional style, but I think that apart from being more readable (IMHO, of course), this also has an additional benefit that it'll stop call |
||||||||||||||||||||||
| } | ||||||||||||||||||||||
| if let Some((token, text, before)) = classifier.next() { | ||||||||||||||||||||||
| let text = &classifier.src[start..start + len]; | ||||||||||||||||||||||
| classifier.advance(TokenKind::Ident, text, sink, start as u32); | ||||||||||||||||||||||
| } else if let Some((token, text, before)) = classifier.next() { | ||||||||||||||||||||||
| classifier.advance(token, text, sink, before); | ||||||||||||||||||||||
| } else { | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
|
|
@@ -957,47 +972,47 @@ impl<'src> Classifier<'src> { | |||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /// Concatenate colons and idents as one when possible. | ||||||||||||||||||||||
| fn get_full_ident_path(&mut self) -> Vec<(TokenKind, usize, usize)> { | ||||||||||||||||||||||
| let start = self.byte_pos as usize; | ||||||||||||||||||||||
| let mut pos = start; | ||||||||||||||||||||||
| fn get_full_ident_path(&mut self) -> Option<usize> { | ||||||||||||||||||||||
| let mut has_ident = false; | ||||||||||||||||||||||
| let mut nb_items = 0; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| loop { | ||||||||||||||||||||||
| let ret = loop { | ||||||||||||||||||||||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't return in this
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, would be nice to be able to just use |
||||||||||||||||||||||
| let mut nb = 0; | ||||||||||||||||||||||
| while let Some((TokenKind::Colon, _)) = self.tokens.peek() { | ||||||||||||||||||||||
| self.tokens.next(); | ||||||||||||||||||||||
| while self.tokens.peek_next_if(|(token, _)| token == TokenKind::Colon).is_some() { | ||||||||||||||||||||||
| nb += 1; | ||||||||||||||||||||||
| nb_items += 1; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| // 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)]; | ||||||||||||||||||||||
| break Some(nb_items); | ||||||||||||||||||||||
| } else if nb != 0 && nb != 2 { | ||||||||||||||||||||||
| if has_ident { | ||||||||||||||||||||||
| return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)]; | ||||||||||||||||||||||
| // Following `;` will be handled on its own. | ||||||||||||||||||||||
| break Some(nb_items - 1); | ||||||||||||||||||||||
| } else { | ||||||||||||||||||||||
| return vec![(TokenKind::Colon, start, pos + nb)]; | ||||||||||||||||||||||
| break None; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if let Some((TokenKind::Ident, text)) = self.tokens.peek() | ||||||||||||||||||||||
| if let Some((TokenKind::Ident, text)) = | ||||||||||||||||||||||
| self.tokens.peek_next_if(|(token, _)| token == TokenKind::Ident) | ||||||||||||||||||||||
| && let symbol = Symbol::intern(text) | ||||||||||||||||||||||
| && (symbol.is_path_segment_keyword() || !is_keyword(symbol)) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| // We only "add" the colon if there is an ident behind. | ||||||||||||||||||||||
| pos += text.len() + nb; | ||||||||||||||||||||||
| has_ident = true; | ||||||||||||||||||||||
| self.tokens.next(); | ||||||||||||||||||||||
| nb_items += 1; | ||||||||||||||||||||||
| } else if nb > 0 && has_ident { | ||||||||||||||||||||||
| return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)]; | ||||||||||||||||||||||
| } else if nb > 0 { | ||||||||||||||||||||||
| return vec![(TokenKind::Colon, start, start + nb)]; | ||||||||||||||||||||||
| // Following `;` will be handled on its own. | ||||||||||||||||||||||
| break Some(nb_items - 1); | ||||||||||||||||||||||
| } else if has_ident { | ||||||||||||||||||||||
| return vec![(TokenKind::Ident, start, pos)]; | ||||||||||||||||||||||
| break Some(nb_items); | ||||||||||||||||||||||
| } else { | ||||||||||||||||||||||
| return Vec::new(); | ||||||||||||||||||||||
| break None; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
| self.tokens.stop_peeking(); | ||||||||||||||||||||||
| ret | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /// Wraps the tokens iteration to ensure that the `byte_pos` is always correct. | ||||||||||||||||||||||
|
|
@@ -1243,7 +1258,6 @@ impl<'src> Classifier<'src> { | |||||||||||||||||||||
| Class::MacroNonTerminal | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| TokenKind::Ident => { | ||||||||||||||||||||||
| let file_span = self.file_span; | ||||||||||||||||||||||
| let span = || new_span(before, text, file_span); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| match text { | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| // This test ensures that the macro expansion is correctly handled in cases like: | ||
| // `field: !f!`, because the `:` was simply not considered because of how paths | ||
| // are handled. | ||
|
|
||
| //@ compile-flags: -Zunstable-options --generate-macro-expansion | ||
|
|
||
| #![crate_name = "foo"] | ||
|
|
||
| //@ has 'src/foo/field-followed-by-exclamation.rs.html' | ||
|
|
||
| struct Bar { | ||
| bla: bool, | ||
| } | ||
|
|
||
| macro_rules! f { | ||
| () => {{ false }} | ||
| } | ||
|
|
||
| const X: Bar = Bar { | ||
| //@ has - '//*[@class="expansion"]/*[@class="original"]/*[@class="macro"]' 'f!' | ||
| //@ has - '//*[@class="expansion"]/*[@class="original"]' 'f!()' | ||
| //@ has - '//*[@class="expansion"]/*[@class="expanded"]' '{ false }' | ||
| // It includes both original and expanded code. | ||
| //@ has - '//*[@class="expansion"]' ' bla: !{ false }f!()' | ||
| bla: !f!(), | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in case we don't find the item we're interested in, we don't want the item to impact the "peeked" items position, so I needed to write this function to "put back the item" in case it didn't match the condition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if it would make sense to switch the implementation of
peek_nextandpeek_next_ifaround, and then implementpeek_nextin terms ofpeek_next_if(|_| true).