Skip to content

Commit 36b8e4a

Browse files
committed
Auto merge of rust-lang#115689 - Alexendoo:clippy-doc-comments, r=notriddle,Manishearth,flip1995
Reuse rustdoc's doc comment handling in Clippy Moves `source_span_for_markdown_range` and `span_of_attrs` (renamed to `span_of_fragments`) to `rustc_resolve::rustdoc` so it can be used in Clippy Fixes rust-lang/rust-clippy#10277 Fixes rust-lang/rust-clippy#5593 Fixes rust-lang/rust-clippy#10263 Fixes rust-lang/rust-clippy#2581
2 parents 725afd2 + caaf1eb commit 36b8e4a

24 files changed

+325
-303
lines changed

Cargo.lock

-1
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,6 @@ dependencies = [
558558
"declare_clippy_lint",
559559
"if_chain",
560560
"itertools",
561-
"pulldown-cmark",
562561
"quine-mc_cluskey",
563562
"regex",
564563
"regex-syntax 0.7.2",

compiler/rustc_resolve/src/rustdoc.rs

+88-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ use pulldown_cmark::{BrokenLink, CowStr, Event, LinkType, Options, Parser, Tag};
22
use rustc_ast as ast;
33
use rustc_ast::util::comments::beautify_doc_string;
44
use rustc_data_structures::fx::FxHashMap;
5+
use rustc_middle::ty::TyCtxt;
56
use rustc_span::def_id::DefId;
67
use rustc_span::symbol::{kw, sym, Symbol};
7-
use rustc_span::Span;
8+
use rustc_span::{InnerSpan, Span, DUMMY_SP};
9+
use std::ops::Range;
810
use std::{cmp, mem};
911

1012
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
@@ -462,3 +464,88 @@ fn collect_link_data<'input, 'callback>(
462464

463465
display_text.map(String::into_boxed_str)
464466
}
467+
468+
/// Returns a span encompassing all the document fragments.
469+
pub fn span_of_fragments(fragments: &[DocFragment]) -> Option<Span> {
470+
if fragments.is_empty() {
471+
return None;
472+
}
473+
let start = fragments[0].span;
474+
if start == DUMMY_SP {
475+
return None;
476+
}
477+
let end = fragments.last().expect("no doc strings provided").span;
478+
Some(start.to(end))
479+
}
480+
481+
/// Attempts to match a range of bytes from parsed markdown to a `Span` in the source code.
482+
///
483+
/// This method will return `None` if we cannot construct a span from the source map or if the
484+
/// fragments are not all sugared doc comments. It's difficult to calculate the correct span in
485+
/// that case due to escaping and other source features.
486+
pub fn source_span_for_markdown_range(
487+
tcx: TyCtxt<'_>,
488+
markdown: &str,
489+
md_range: &Range<usize>,
490+
fragments: &[DocFragment],
491+
) -> Option<Span> {
492+
let is_all_sugared_doc = fragments.iter().all(|frag| frag.kind == DocFragmentKind::SugaredDoc);
493+
494+
if !is_all_sugared_doc {
495+
return None;
496+
}
497+
498+
let snippet = tcx.sess.source_map().span_to_snippet(span_of_fragments(fragments)?).ok()?;
499+
500+
let starting_line = markdown[..md_range.start].matches('\n').count();
501+
let ending_line = starting_line + markdown[md_range.start..md_range.end].matches('\n').count();
502+
503+
// We use `split_terminator('\n')` instead of `lines()` when counting bytes so that we treat
504+
// CRLF and LF line endings the same way.
505+
let mut src_lines = snippet.split_terminator('\n');
506+
let md_lines = markdown.split_terminator('\n');
507+
508+
// The number of bytes from the source span to the markdown span that are not part
509+
// of the markdown, like comment markers.
510+
let mut start_bytes = 0;
511+
let mut end_bytes = 0;
512+
513+
'outer: for (line_no, md_line) in md_lines.enumerate() {
514+
loop {
515+
let source_line = src_lines.next()?;
516+
match source_line.find(md_line) {
517+
Some(offset) => {
518+
if line_no == starting_line {
519+
start_bytes += offset;
520+
521+
if starting_line == ending_line {
522+
break 'outer;
523+
}
524+
} else if line_no == ending_line {
525+
end_bytes += offset;
526+
break 'outer;
527+
} else if line_no < starting_line {
528+
start_bytes += source_line.len() - md_line.len();
529+
} else {
530+
end_bytes += source_line.len() - md_line.len();
531+
}
532+
break;
533+
}
534+
None => {
535+
// Since this is a source line that doesn't include a markdown line,
536+
// we have to count the newline that we split from earlier.
537+
if line_no <= starting_line {
538+
start_bytes += source_line.len() + 1;
539+
} else {
540+
end_bytes += source_line.len() + 1;
541+
}
542+
}
543+
}
544+
}
545+
}
546+
547+
Some(span_of_fragments(fragments)?.from_inner(InnerSpan::new(
548+
md_range.start + start_bytes,
549+
md_range.end + start_bytes + end_bytes,
550+
)))
551+
}

src/librustdoc/clean/types.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ use rustc_index::IndexVec;
2525
use rustc_metadata::rendered_const;
2626
use rustc_middle::ty::fast_reject::SimplifiedType;
2727
use rustc_middle::ty::{self, TyCtxt, Visibility};
28-
use rustc_resolve::rustdoc::{add_doc_fragment, attrs_to_doc_fragments, inner_docs, DocFragment};
28+
use rustc_resolve::rustdoc::{
29+
add_doc_fragment, attrs_to_doc_fragments, inner_docs, span_of_fragments, DocFragment,
30+
};
2931
use rustc_session::Session;
3032
use rustc_span::hygiene::MacroKind;
3133
use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -397,7 +399,7 @@ impl Item {
397399
}
398400

399401
pub(crate) fn attr_span(&self, tcx: TyCtxt<'_>) -> rustc_span::Span {
400-
crate::passes::span_of_attrs(&self.attrs)
402+
span_of_fragments(&self.attrs.doc_strings)
401403
.unwrap_or_else(|| self.span(tcx).map_or(rustc_span::DUMMY_SP, |span| span.inner()))
402404
}
403405

src/librustdoc/doctest.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_middle::hir::nested_filter;
1010
use rustc_middle::ty::TyCtxt;
1111
use rustc_parse::maybe_new_parser_from_source_str;
1212
use rustc_parse::parser::attr::InnerAttrPolicy;
13+
use rustc_resolve::rustdoc::span_of_fragments;
1314
use rustc_session::config::{self, CrateType, ErrorOutputType};
1415
use rustc_session::parse::ParseSess;
1516
use rustc_session::{lint, EarlyErrorHandler, Session};
@@ -33,7 +34,6 @@ use crate::clean::{types::AttributesExt, Attributes};
3334
use crate::config::Options as RustdocOptions;
3435
use crate::html::markdown::{self, ErrorCodes, Ignore, LangString};
3536
use crate::lint::init_lints;
36-
use crate::passes::span_of_attrs;
3737

3838
/// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`).
3939
#[derive(Clone, Default)]
@@ -1241,7 +1241,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
12411241
Some(&crate::html::markdown::ExtraInfo::new(
12421242
self.tcx,
12431243
def_id.to_def_id(),
1244-
span_of_attrs(&attrs).unwrap_or(sp),
1244+
span_of_fragments(&attrs.doc_strings).unwrap_or(sp),
12451245
)),
12461246
);
12471247
}

src/librustdoc/passes/collect_intra_doc_links.rs

+23-20
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use rustc_hir::Mutability;
1616
use rustc_middle::ty::{Ty, TyCtxt};
1717
use rustc_middle::{bug, span_bug, ty};
1818
use rustc_resolve::rustdoc::{has_primitive_or_keyword_docs, prepare_to_doc_link_resolution};
19-
use rustc_resolve::rustdoc::{strip_generics_from_path, MalformedGenerics};
19+
use rustc_resolve::rustdoc::{
20+
source_span_for_markdown_range, strip_generics_from_path, MalformedGenerics,
21+
};
2022
use rustc_session::lint::Lint;
2123
use rustc_span::hygiene::MacroKind;
2224
use rustc_span::symbol::{sym, Ident, Symbol};
@@ -1211,11 +1213,11 @@ impl LinkCollector<'_, '_> {
12111213
ori_link: &MarkdownLinkRange,
12121214
item: &Item,
12131215
) {
1214-
let span = super::source_span_for_markdown_range(
1216+
let span = source_span_for_markdown_range(
12151217
self.cx.tcx,
12161218
dox,
12171219
ori_link.inner_range(),
1218-
&item.attrs,
1220+
&item.attrs.doc_strings,
12191221
)
12201222
.unwrap_or_else(|| item.attr_span(self.cx.tcx));
12211223
rustc_session::parse::feature_err(
@@ -1702,26 +1704,27 @@ fn report_diagnostic(
17021704
let (span, link_range) = match link_range {
17031705
MarkdownLinkRange::Destination(md_range) => {
17041706
let mut md_range = md_range.clone();
1705-
let sp = super::source_span_for_markdown_range(tcx, dox, &md_range, &item.attrs)
1706-
.map(|mut sp| {
1707-
while dox.as_bytes().get(md_range.start) == Some(&b' ')
1708-
|| dox.as_bytes().get(md_range.start) == Some(&b'`')
1709-
{
1710-
md_range.start += 1;
1711-
sp = sp.with_lo(sp.lo() + BytePos(1));
1712-
}
1713-
while dox.as_bytes().get(md_range.end - 1) == Some(&b' ')
1714-
|| dox.as_bytes().get(md_range.end - 1) == Some(&b'`')
1715-
{
1716-
md_range.end -= 1;
1717-
sp = sp.with_hi(sp.hi() - BytePos(1));
1718-
}
1719-
sp
1720-
});
1707+
let sp =
1708+
source_span_for_markdown_range(tcx, dox, &md_range, &item.attrs.doc_strings)
1709+
.map(|mut sp| {
1710+
while dox.as_bytes().get(md_range.start) == Some(&b' ')
1711+
|| dox.as_bytes().get(md_range.start) == Some(&b'`')
1712+
{
1713+
md_range.start += 1;
1714+
sp = sp.with_lo(sp.lo() + BytePos(1));
1715+
}
1716+
while dox.as_bytes().get(md_range.end - 1) == Some(&b' ')
1717+
|| dox.as_bytes().get(md_range.end - 1) == Some(&b'`')
1718+
{
1719+
md_range.end -= 1;
1720+
sp = sp.with_hi(sp.hi() - BytePos(1));
1721+
}
1722+
sp
1723+
});
17211724
(sp, MarkdownLinkRange::Destination(md_range))
17221725
}
17231726
MarkdownLinkRange::WholeLink(md_range) => (
1724-
super::source_span_for_markdown_range(tcx, dox, &md_range, &item.attrs),
1727+
source_span_for_markdown_range(tcx, dox, &md_range, &item.attrs.doc_strings),
17251728
link_range.clone(),
17261729
),
17271730
};

src/librustdoc/passes/lint/bare_urls.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
use crate::clean::*;
55
use crate::core::DocContext;
66
use crate::html::markdown::main_body_opts;
7-
use crate::passes::source_span_for_markdown_range;
87
use core::ops::Range;
98
use pulldown_cmark::{Event, Parser, Tag};
109
use regex::Regex;
1110
use rustc_errors::Applicability;
11+
use rustc_resolve::rustdoc::source_span_for_markdown_range;
1212
use std::mem;
1313
use std::sync::LazyLock;
1414

@@ -21,8 +21,9 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item) {
2121
if !dox.is_empty() {
2222
let report_diag =
2323
|cx: &DocContext<'_>, msg: &'static str, url: &str, range: Range<usize>| {
24-
let sp = source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs)
25-
.unwrap_or_else(|| item.attr_span(cx.tcx));
24+
let sp =
25+
source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs.doc_strings)
26+
.unwrap_or_else(|| item.attr_span(cx.tcx));
2627
cx.tcx.struct_span_lint_hir(crate::lint::BARE_URLS, hir_id, sp, msg, |lint| {
2728
lint.note("bare URLs are not automatically turned into clickable links")
2829
.span_suggestion(

src/librustdoc/passes/lint/check_code_block_syntax.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use rustc_errors::{
66
Applicability, Diagnostic, Handler, LazyFallbackBundle,
77
};
88
use rustc_parse::parse_stream_from_source_str;
9+
use rustc_resolve::rustdoc::source_span_for_markdown_range;
910
use rustc_session::parse::ParseSess;
1011
use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId};
1112
use rustc_span::source_map::{FilePathMapping, SourceMap};
@@ -14,7 +15,6 @@ use rustc_span::{FileName, InnerSpan, DUMMY_SP};
1415
use crate::clean;
1516
use crate::core::DocContext;
1617
use crate::html::markdown::{self, RustCodeBlock};
17-
use crate::passes::source_span_for_markdown_range;
1818

1919
pub(crate) fn visit_item(cx: &DocContext<'_>, item: &clean::Item) {
2020
if let Some(dox) = &item.opt_doc_value() {
@@ -77,11 +77,15 @@ fn check_rust_syntax(
7777
let is_ignore = code_block.lang_string.ignore != markdown::Ignore::None;
7878

7979
// The span and whether it is precise or not.
80-
let (sp, precise_span) =
81-
match source_span_for_markdown_range(cx.tcx, dox, &code_block.range, &item.attrs) {
82-
Some(sp) => (sp, true),
83-
None => (item.attr_span(cx.tcx), false),
84-
};
80+
let (sp, precise_span) = match source_span_for_markdown_range(
81+
cx.tcx,
82+
dox,
83+
&code_block.range,
84+
&item.attrs.doc_strings,
85+
) {
86+
Some(sp) => (sp, true),
87+
None => (item.attr_span(cx.tcx), false),
88+
};
8589

8690
let msg = if buffer.has_errors {
8791
"could not parse code block as Rust code"

src/librustdoc/passes/lint/html_tags.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
use crate::clean::*;
33
use crate::core::DocContext;
44
use crate::html::markdown::main_body_opts;
5-
use crate::passes::source_span_for_markdown_range;
65

76
use pulldown_cmark::{BrokenLink, Event, LinkType, Parser, Tag};
7+
use rustc_resolve::rustdoc::source_span_for_markdown_range;
88

99
use std::iter::Peekable;
1010
use std::ops::Range;
@@ -20,7 +20,8 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
2020
let dox = item.doc_value();
2121
if !dox.is_empty() {
2222
let report_diag = |msg: String, range: &Range<usize>, is_open_tag: bool| {
23-
let sp = match source_span_for_markdown_range(tcx, &dox, range, &item.attrs) {
23+
let sp = match source_span_for_markdown_range(tcx, &dox, range, &item.attrs.doc_strings)
24+
{
2425
Some(sp) => sp,
2526
None => item.attr_span(tcx),
2627
};
@@ -60,7 +61,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
6061
tcx,
6162
&dox,
6263
&(generics_start..generics_end),
63-
&item.attrs,
64+
&item.attrs.doc_strings,
6465
) {
6566
Some(sp) => sp,
6667
None => item.attr_span(tcx),

src/librustdoc/passes/lint/redundant_explicit_links.rs

+22-12
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ use rustc_errors::SuggestionStyle;
66
use rustc_hir::def::{DefKind, DocLinkResMap, Namespace, Res};
77
use rustc_hir::HirId;
88
use rustc_lint_defs::Applicability;
9+
use rustc_resolve::rustdoc::source_span_for_markdown_range;
910
use rustc_span::Symbol;
1011

1112
use crate::clean::utils::find_nearest_parent_module;
1213
use crate::clean::utils::inherits_doc_hidden;
1314
use crate::clean::Item;
1415
use crate::core::DocContext;
1516
use crate::html::markdown::main_body_opts;
16-
use crate::passes::source_span_for_markdown_range;
1717

1818
#[derive(Debug)]
1919
struct LinkData {
@@ -160,16 +160,21 @@ fn check_inline_or_reference_unknown_redundancy(
160160
(find_resolution(resolutions, &dest)?, find_resolution(resolutions, resolvable_link)?);
161161

162162
if dest_res == display_res {
163-
let link_span = source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs)
164-
.unwrap_or(item.attr_span(cx.tcx));
163+
let link_span =
164+
source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs.doc_strings)
165+
.unwrap_or(item.attr_span(cx.tcx));
165166
let explicit_span = source_span_for_markdown_range(
166167
cx.tcx,
167168
&doc,
168169
&offset_explicit_range(doc, link_range, open, close),
169-
&item.attrs,
170+
&item.attrs.doc_strings,
171+
)?;
172+
let display_span = source_span_for_markdown_range(
173+
cx.tcx,
174+
&doc,
175+
&resolvable_link_range,
176+
&item.attrs.doc_strings,
170177
)?;
171-
let display_span =
172-
source_span_for_markdown_range(cx.tcx, &doc, &resolvable_link_range, &item.attrs)?;
173178

174179
cx.tcx.struct_span_lint_hir(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, "redundant explicit link target", |lint| {
175180
lint.span_label(explicit_span, "explicit target is redundant")
@@ -201,21 +206,26 @@ fn check_reference_redundancy(
201206
(find_resolution(resolutions, &dest)?, find_resolution(resolutions, resolvable_link)?);
202207

203208
if dest_res == display_res {
204-
let link_span = source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs)
205-
.unwrap_or(item.attr_span(cx.tcx));
209+
let link_span =
210+
source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs.doc_strings)
211+
.unwrap_or(item.attr_span(cx.tcx));
206212
let explicit_span = source_span_for_markdown_range(
207213
cx.tcx,
208214
&doc,
209215
&offset_explicit_range(doc, link_range.clone(), b'[', b']'),
210-
&item.attrs,
216+
&item.attrs.doc_strings,
217+
)?;
218+
let display_span = source_span_for_markdown_range(
219+
cx.tcx,
220+
&doc,
221+
&resolvable_link_range,
222+
&item.attrs.doc_strings,
211223
)?;
212-
let display_span =
213-
source_span_for_markdown_range(cx.tcx, &doc, &resolvable_link_range, &item.attrs)?;
214224
let def_span = source_span_for_markdown_range(
215225
cx.tcx,
216226
&doc,
217227
&offset_reference_def_range(doc, dest, link_range),
218-
&item.attrs,
228+
&item.attrs.doc_strings,
219229
)?;
220230

221231
cx.tcx.struct_span_lint_hir(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, "redundant explicit link target", |lint| {

0 commit comments

Comments
 (0)