rustdoc: do not include extra stuff in span#157561
Conversation
|
rustbot has assigned @lolbinarycat. Use Why was this reviewer chosen?The reviewer was selected based on:
|
|
p.s. this change is a lot easier to understand if you use the hide whitespace setting on the diff. |
This comment has been minimized.
This comment has been minimized.
d2784cd to
9af61c5
Compare
This comment has been minimized.
This comment has been minimized.
9af61c5 to
7db2845
Compare
This comment has been minimized.
This comment has been minimized.
7db2845 to
9d216d1
Compare
This comment has been minimized.
This comment has been minimized.
|
The Clippy subtree was changed cc @rust-lang/clippy |
31c9e0e to
d13270c
Compare
This comment has been minimized.
This comment has been minimized.
This change prevents our lints from returning a span with stuff in it that isn't actually part of the doc comment. When that happens, it returns `None` instead.
d13270c to
33fb30e
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
Do we need to be optimizing our diagnostic code (cold path) to the extent of using binary search to find an item in a usually small list? this only speeds up doc generation if a crate has a ton of warnings. I guess I'll see if it shows up at all in a perf run. @bors try @rust-timer queue profiles=doc |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
rustdoc: do not include extra stuff in span
This comment has been minimized.
This comment has been minimized.
I knew it would be O(n2) if just did a linear scan. Even in the cold path, I'd prefer to avoid that. |
This comment has been minimized.
This comment has been minimized.
It requires an allocation, and doesn't seem to help in practice. Fixes a nit found during review.
93b65a3 to
ae2edb4
Compare
|
@bors try @rust-timer queue profiles=doc |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
rustdoc: do not include extra stuff in span
This comment has been minimized.
This comment has been minimized.
|
Finished benchmarking commit (1a5fffe): comparison URL. Overall result: no relevant changes - no action neededBenchmarking means the PR may be perf-sensitive. Consider adding rollup=never if this change is not fit for rolling up. @rustbot label: -S-waiting-on-perf -perf-regression Instruction countThis perf run didn't have relevant results for this metric. Max RSS (memory usage)This perf run didn't have relevant results for this metric. CyclesThis perf run didn't have relevant results for this metric. Binary sizeThis perf run didn't have relevant results for this metric. Bootstrap: 521.088s -> 525.76s (0.90%) |
| // Right now, redundant_explicit_links won't produce a warning at all if | ||
| // rustdoc isn't able to calculate an accurate span for the link. |
There was a problem hiding this comment.
Shouldn't we produce a warning, but not a suggestion? fine with addressing this in a followup PR.
| let mut start_bytes = 0; | ||
| let mut end_bytes = 0; | ||
|
|
||
| let span = span_of_fragments(fragments)?; |
There was a problem hiding this comment.
| let span = span_of_fragments(fragments)?; | |
| let span_of_all_fragments = span_of_fragments(fragments)?; |
|
|
||
| let span = span_of_fragments(fragments)?; | ||
|
|
||
| let mut line_bytes = 0; |
There was a problem hiding this comment.
| let mut line_bytes = 0; | |
| let mut prev_lines_bytes = 0; |
| let has_fragment = fragments.iter().any(|fragment| { | ||
| fragment.span.hi().0 >= span.lo().0 + line_bytes | ||
| && fragment.span.lo().0 <= span.lo().0 + line_bytes + source_line_len | ||
| }); |
There was a problem hiding this comment.
| let has_fragment = fragments.iter().any(|fragment| { | |
| fragment.span.hi().0 >= span.lo().0 + line_bytes | |
| && fragment.span.lo().0 <= span.lo().0 + line_bytes + source_line_len | |
| }); | |
| let source_line_span = span_of_all_fragments.split_at(prev_line_bytes).1.split_at(source_line_len).0; | |
| let has_fragment = fragments.iter().any(|fragment| { | |
| fragment.span.contains(source_line_span) | |
| }); |
I think an intermediate span or at least some intermediate variables would make this more readable (unfortunately the functions for manipulating spans are a bit lacking in utilities that would be more intuitive to read).
There was a problem hiding this comment.
This doesn't work as-is, because span_of_fragment contains the intermediate indentation and fragment.span doesn't.
pub struct Foo {
/// doc comment for struct property
/// intermediate line
/// another intermediate line
pub property: Bar,
}Consider that source_line_span and fragment.span for the middle line won't be the same:
/// intermediate line
-------------------------
| ^^^^^^^^^^^^^^^^^^^^^
| | fragment.span
|
| source_line_span
There was a problem hiding this comment.
doesn't that also mean your code is also wrong? unless i messed up and changed behavior in my refactor
it should be source_line_span.contains(fragment.span())
this whole discussion makes me think we need more test coverage.
There was a problem hiding this comment.
My code was actually just really weird and redundant. I thought it was equivalent to fragment.span.contains(source_line_span), but lucked into a version that actually worked instead.
The new version is just fragment.span.contains(source_line_span.shrink_to_hi()), which should be easier to understand.
There was a problem hiding this comment.
that also seems strange. why not Span::overlaps?
| // Since this is a source line that doesn't include a markdown line, | ||
| // we have to count the newline that we split from earlier. |
There was a problem hiding this comment.
| // Since this is a source line that doesn't include a markdown line, | |
| // we have to count the newline that we split from earlier. | |
| // Since this is a source line that doesn't include a markdown line, | |
| // we have to count it and its newline as non-markdown bytes |
| } else if source_line.chars().any(|c| !c.is_whitespace()) { | ||
| // If we're past the first line, but haven't found the last line, | ||
| // we can only return a contiguous span if every line is either | ||
| // part of the doc comment or blank. | ||
| return None; |
There was a problem hiding this comment.
| } else if source_line.chars().any(|c| !c.is_whitespace()) { | |
| // If we're past the first line, but haven't found the last line, | |
| // we can only return a contiguous span if every line is either | |
| // part of the doc comment or blank. | |
| return None; | |
| } else if source_line.chars().any(|c| !c.is_whitespace()) { | |
| // We're past the first line, but haven't found the last line, | |
| // but we found a non-empty non-markdown line. | |
| // This could be an attribute, and we don't want a diagnostic | |
| // suggesting to delete that attribute, so we return None to be safe. | |
| return None; |
Co-authored-by: binarycat <binarycat@envs.net>
Co-authored-by: binarycat <binarycat@envs.net>
|
@rustbot review Okay, I've pushed a bunch of commits that should resolve all of these requests. |
Co-authored-by: binarycat <binarycat@envs.net>
2965c54 to
1180a93
Compare
This comment has been minimized.
This comment has been minimized.
3b59e6a to
9c0a7d7
Compare
| let has_fragment = fragments | ||
| .iter() | ||
| // `source_line_span` might contain indentation that `fragment.span` doesn't contain, | ||
| // so `shrink_to_hi()` removes it |
There was a problem hiding this comment.
lines with doc comments can have trailing whitespace that isn't part of the doc comment, though.
there's nothing asymmetrical about this problem as far as i can tell, so i don't see why the solution would be asymmetrical
consider:
/** Here's some function docs with an unusual style */ // whoops some trailing whitespace here!
pub fn f() { ... }or even:
/** Everything on one line */ pub fn g() { ... }i think overlaps is the correct thing to use, or if that's incorrect, then we need something more nuanced than shrink_to_hi.
There was a problem hiding this comment.
lines with doc comments can have trailing whitespace that isn't part of the doc comment, though.
Neither of the snippets you gave are examples of this, because source_line_span is created by splitting up span_of_all_fragments, which doesn't include anything after the last block comment.
1| /** some doc */ fn foo() {}
^^^^^^^^^^^^^^^
| span_of_all_fragments doesn't include the `fn`,
so neither does `source_line_span`
You can wind up with an attribute that's included in the span_of_all_fragments without it being in the fragment.span, by putting two block comments back-to-back:
/** [Clone]( */ #[inline]
/** std::clone::Clone) */
pub fn split_blockcomment_twoline_attr() {
}Or do the same thing with an attribute at the start of the second line:
/** [Clone]( */
#[inline] /** std::clone::Clone) */
pub fn split_blockcomment_twoline_attr() {
}I've pushed a new commit with the change to overlaps, a bug fix for these two cases, and a bunch of new tests.
|
☔ The latest upstream changes (presumably #158403) made this pull request unmergeable. Please resolve the merge conflicts by rebasing. |


View all comments
This change prevents our lints from returning a span with stuff in it that isn't actually part of the doc comment. When that happens, it returns
Noneinstead.Fixes rust-lang/rust-clippy#16169, since the bug was reported against the
clippy::doc_paragraphs_missing_punctuationlint but is actually a bug insource_span_for_markdown_range_inner.