Do not emit redundant_explicit_links rustdoc lint if the doc comment comes from expansion
This commit is contained in:
parent
22be76b7e2
commit
7e683cc4d1
7 changed files with 101 additions and 55 deletions
|
|
@ -49,6 +49,9 @@ pub struct DocFragment {
|
|||
pub doc: Symbol,
|
||||
pub kind: DocFragmentKind,
|
||||
pub indent: usize,
|
||||
/// Because we temper with the spans context, this information cannot be correctly retrieved
|
||||
/// later on. So instead, we compute it and store it here.
|
||||
pub from_expansion: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
|
@ -208,17 +211,18 @@ pub fn attrs_to_doc_fragments<'a, A: AttributeExt + Clone + 'a>(
|
|||
for (attr, item_id) in attrs {
|
||||
if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() {
|
||||
let doc = beautify_doc_string(doc_str, comment_kind);
|
||||
let (span, kind) = if attr.is_doc_comment() {
|
||||
(attr.span(), DocFragmentKind::SugaredDoc)
|
||||
let (span, kind, from_expansion) = if attr.is_doc_comment() {
|
||||
let span = attr.span();
|
||||
(span, DocFragmentKind::SugaredDoc, span.from_expansion())
|
||||
} else {
|
||||
(
|
||||
attr.value_span()
|
||||
.map(|i| i.with_ctxt(attr.span().ctxt()))
|
||||
.unwrap_or(attr.span()),
|
||||
DocFragmentKind::RawDoc,
|
||||
)
|
||||
let attr_span = attr.span();
|
||||
let (span, from_expansion) = match attr.value_span() {
|
||||
Some(sp) => (sp.with_ctxt(attr_span.ctxt()), sp.from_expansion()),
|
||||
None => (attr_span, attr_span.from_expansion()),
|
||||
};
|
||||
(span, DocFragmentKind::RawDoc, from_expansion)
|
||||
};
|
||||
let fragment = DocFragment { span, doc, kind, item_id, indent: 0 };
|
||||
let fragment = DocFragment { span, doc, kind, item_id, indent: 0, from_expansion };
|
||||
doc_fragments.push(fragment);
|
||||
} else if !doc_only {
|
||||
other_attrs.push(attr.clone());
|
||||
|
|
@ -505,17 +509,22 @@ fn collect_link_data<'input, F: BrokenLinkCallback<'input>>(
|
|||
display_text.map(String::into_boxed_str)
|
||||
}
|
||||
|
||||
/// Returns a span encompassing all the document fragments.
|
||||
pub fn span_of_fragments_with_expansion(fragments: &[DocFragment]) -> Option<(Span, bool)> {
|
||||
let Some(first_fragment) = fragments.first() else { return None };
|
||||
if first_fragment.span == DUMMY_SP {
|
||||
return None;
|
||||
}
|
||||
let last_fragment = fragments.last().expect("no doc strings provided");
|
||||
Some((
|
||||
first_fragment.span.to(last_fragment.span),
|
||||
first_fragment.from_expansion || last_fragment.from_expansion,
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns a span encompassing all the document fragments.
|
||||
pub fn span_of_fragments(fragments: &[DocFragment]) -> Option<Span> {
|
||||
if fragments.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let start = fragments[0].span;
|
||||
if start == DUMMY_SP {
|
||||
return None;
|
||||
}
|
||||
let end = fragments.last().expect("no doc strings provided").span;
|
||||
Some(start.to(end))
|
||||
span_of_fragments_with_expansion(fragments).map(|(sp, _)| sp)
|
||||
}
|
||||
|
||||
/// Attempts to match a range of bytes from parsed markdown to a `Span` in the source code.
|
||||
|
|
@ -540,7 +549,7 @@ pub fn source_span_for_markdown_range(
|
|||
markdown: &str,
|
||||
md_range: &Range<usize>,
|
||||
fragments: &[DocFragment],
|
||||
) -> Option<Span> {
|
||||
) -> Option<(Span, bool)> {
|
||||
let map = tcx.sess.source_map();
|
||||
source_span_for_markdown_range_inner(map, markdown, md_range, fragments)
|
||||
}
|
||||
|
|
@ -551,7 +560,7 @@ pub fn source_span_for_markdown_range_inner(
|
|||
markdown: &str,
|
||||
md_range: &Range<usize>,
|
||||
fragments: &[DocFragment],
|
||||
) -> Option<Span> {
|
||||
) -> Option<(Span, bool)> {
|
||||
use rustc_span::BytePos;
|
||||
|
||||
if let &[fragment] = &fragments
|
||||
|
|
@ -562,11 +571,14 @@ pub fn source_span_for_markdown_range_inner(
|
|||
&& let Ok(md_range_hi) = u32::try_from(md_range.end)
|
||||
{
|
||||
// Single fragment with string that contains same bytes as doc.
|
||||
return Some(Span::new(
|
||||
fragment.span.lo() + rustc_span::BytePos(md_range_lo),
|
||||
fragment.span.lo() + rustc_span::BytePos(md_range_hi),
|
||||
fragment.span.ctxt(),
|
||||
fragment.span.parent(),
|
||||
return Some((
|
||||
Span::new(
|
||||
fragment.span.lo() + rustc_span::BytePos(md_range_lo),
|
||||
fragment.span.lo() + rustc_span::BytePos(md_range_hi),
|
||||
fragment.span.ctxt(),
|
||||
fragment.span.parent(),
|
||||
),
|
||||
fragment.from_expansion,
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -598,19 +610,21 @@ pub fn source_span_for_markdown_range_inner(
|
|||
{
|
||||
match_data = Some((i, match_start));
|
||||
} else {
|
||||
// Heirustic produced ambiguity, return nothing.
|
||||
// Heuristic produced ambiguity, return nothing.
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((i, match_start)) = match_data {
|
||||
let sp = fragments[i].span;
|
||||
let fragment = &fragments[i];
|
||||
let sp = fragment.span;
|
||||
// we need to calculate the span start,
|
||||
// then use that in our calulations for the span end
|
||||
let lo = sp.lo() + BytePos(match_start as u32);
|
||||
return Some(
|
||||
return Some((
|
||||
sp.with_lo(lo).with_hi(lo + BytePos((md_range.end - md_range.start) as u32)),
|
||||
);
|
||||
fragment.from_expansion,
|
||||
));
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
|
@ -664,8 +678,12 @@ pub fn source_span_for_markdown_range_inner(
|
|||
}
|
||||
}
|
||||
|
||||
Some(span_of_fragments(fragments)?.from_inner(InnerSpan::new(
|
||||
md_range.start + start_bytes,
|
||||
md_range.end + start_bytes + end_bytes,
|
||||
)))
|
||||
let (span, from_expansion) = span_of_fragments_with_expansion(fragments)?;
|
||||
Some((
|
||||
span.from_inner(InnerSpan::new(
|
||||
md_range.start + start_bytes,
|
||||
md_range.end + start_bytes + end_bytes,
|
||||
)),
|
||||
from_expansion,
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1387,13 +1387,15 @@ impl LinkCollector<'_, '_> {
|
|||
ori_link: &MarkdownLinkRange,
|
||||
item: &Item,
|
||||
) {
|
||||
let span = source_span_for_markdown_range(
|
||||
let span = match source_span_for_markdown_range(
|
||||
self.cx.tcx,
|
||||
dox,
|
||||
ori_link.inner_range(),
|
||||
&item.attrs.doc_strings,
|
||||
)
|
||||
.unwrap_or_else(|| item.attr_span(self.cx.tcx));
|
||||
) {
|
||||
Some((sp, _)) => sp,
|
||||
None => item.attr_span(self.cx.tcx),
|
||||
};
|
||||
rustc_session::parse::feature_err(
|
||||
self.cx.tcx.sess,
|
||||
sym::intra_doc_pointers,
|
||||
|
|
@ -1836,7 +1838,7 @@ fn report_diagnostic(
|
|||
let mut md_range = md_range.clone();
|
||||
let sp =
|
||||
source_span_for_markdown_range(tcx, dox, &md_range, &item.attrs.doc_strings)
|
||||
.map(|mut sp| {
|
||||
.map(|(mut sp, _)| {
|
||||
while dox.as_bytes().get(md_range.start) == Some(&b' ')
|
||||
|| dox.as_bytes().get(md_range.start) == Some(&b'`')
|
||||
{
|
||||
|
|
@ -1854,7 +1856,8 @@ fn report_diagnostic(
|
|||
(sp, MarkdownLinkRange::Destination(md_range))
|
||||
}
|
||||
MarkdownLinkRange::WholeLink(md_range) => (
|
||||
source_span_for_markdown_range(tcx, dox, md_range, &item.attrs.doc_strings),
|
||||
source_span_for_markdown_range(tcx, dox, md_range, &item.attrs.doc_strings)
|
||||
.map(|(sp, _)| sp),
|
||||
link_range.clone(),
|
||||
),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ use crate::html::markdown::main_body_opts;
|
|||
|
||||
pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &str) {
|
||||
let report_diag = |cx: &DocContext<'_>, msg: &'static str, range: Range<usize>| {
|
||||
let maybe_sp = source_span_for_markdown_range(cx.tcx, dox, &range, &item.attrs.doc_strings);
|
||||
let maybe_sp = source_span_for_markdown_range(cx.tcx, dox, &range, &item.attrs.doc_strings)
|
||||
.map(|(sp, _)| sp);
|
||||
let sp = maybe_sp.unwrap_or_else(|| item.attr_span(cx.tcx));
|
||||
cx.tcx.node_span_lint(crate::lint::BARE_URLS, hir_id, sp, |lint| {
|
||||
lint.primary_message(msg)
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ fn check_rust_syntax(
|
|||
&code_block.range,
|
||||
&item.attrs.doc_strings,
|
||||
) {
|
||||
Some(sp) => (sp, true),
|
||||
Some((sp, _)) => (sp, true),
|
||||
None => (item.attr_span(cx.tcx), false),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &
|
|||
let tcx = cx.tcx;
|
||||
let report_diag = |msg: String, range: &Range<usize>, is_open_tag: bool| {
|
||||
let sp = match source_span_for_markdown_range(tcx, dox, range, &item.attrs.doc_strings) {
|
||||
Some(sp) => sp,
|
||||
Some((sp, _)) => sp,
|
||||
None => item.attr_span(tcx),
|
||||
};
|
||||
tcx.node_span_lint(crate::lint::INVALID_HTML_TAGS, hir_id, sp, |lint| {
|
||||
|
|
@ -55,7 +55,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &
|
|||
&(generics_start..generics_end),
|
||||
&item.attrs.doc_strings,
|
||||
) {
|
||||
Some(sp) => sp,
|
||||
Some((sp, _)) => sp,
|
||||
None => item.attr_span(tcx),
|
||||
};
|
||||
// Sometimes, we only extract part of a path. For example, consider this:
|
||||
|
|
|
|||
|
|
@ -161,15 +161,26 @@ fn check_inline_or_reference_unknown_redundancy(
|
|||
|
||||
if dest_res == display_res {
|
||||
let link_span =
|
||||
source_span_for_markdown_range(cx.tcx, doc, &link_range, &item.attrs.doc_strings)
|
||||
.unwrap_or(item.attr_span(cx.tcx));
|
||||
let explicit_span = source_span_for_markdown_range(
|
||||
match source_span_for_markdown_range(cx.tcx, doc, &link_range, &item.attrs.doc_strings)
|
||||
{
|
||||
Some((sp, from_expansion)) => {
|
||||
if from_expansion {
|
||||
return None;
|
||||
}
|
||||
sp
|
||||
}
|
||||
None => item.attr_span(cx.tcx),
|
||||
};
|
||||
let (explicit_span, from_expansion) = source_span_for_markdown_range(
|
||||
cx.tcx,
|
||||
doc,
|
||||
&offset_explicit_range(doc, link_range, open, close),
|
||||
&item.attrs.doc_strings,
|
||||
)?;
|
||||
let display_span = source_span_for_markdown_range(
|
||||
if from_expansion {
|
||||
return None;
|
||||
}
|
||||
let (display_span, _) = source_span_for_markdown_range(
|
||||
cx.tcx,
|
||||
doc,
|
||||
resolvable_link_range,
|
||||
|
|
@ -206,21 +217,32 @@ fn check_reference_redundancy(
|
|||
|
||||
if dest_res == display_res {
|
||||
let link_span =
|
||||
source_span_for_markdown_range(cx.tcx, doc, &link_range, &item.attrs.doc_strings)
|
||||
.unwrap_or(item.attr_span(cx.tcx));
|
||||
let explicit_span = source_span_for_markdown_range(
|
||||
match source_span_for_markdown_range(cx.tcx, doc, &link_range, &item.attrs.doc_strings)
|
||||
{
|
||||
Some((sp, from_expansion)) => {
|
||||
if from_expansion {
|
||||
return None;
|
||||
}
|
||||
sp
|
||||
}
|
||||
None => item.attr_span(cx.tcx),
|
||||
};
|
||||
let (explicit_span, from_expansion) = source_span_for_markdown_range(
|
||||
cx.tcx,
|
||||
doc,
|
||||
&offset_explicit_range(doc, link_range.clone(), b'[', b']'),
|
||||
&item.attrs.doc_strings,
|
||||
)?;
|
||||
let display_span = source_span_for_markdown_range(
|
||||
if from_expansion {
|
||||
return None;
|
||||
}
|
||||
let (display_span, _) = source_span_for_markdown_range(
|
||||
cx.tcx,
|
||||
doc,
|
||||
resolvable_link_range,
|
||||
&item.attrs.doc_strings,
|
||||
)?;
|
||||
let def_span = source_span_for_markdown_range(
|
||||
let (def_span, _) = source_span_for_markdown_range(
|
||||
cx.tcx,
|
||||
doc,
|
||||
&offset_reference_def_range(doc, dest, link_range),
|
||||
|
|
|
|||
|
|
@ -42,13 +42,15 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &
|
|||
|
||||
// If we can't get a span of the backtick, because it is in a `#[doc = ""]` attribute,
|
||||
// use the span of the entire attribute as a fallback.
|
||||
let span = source_span_for_markdown_range(
|
||||
let span = match source_span_for_markdown_range(
|
||||
tcx,
|
||||
dox,
|
||||
&(backtick_index..backtick_index + 1),
|
||||
&item.attrs.doc_strings,
|
||||
)
|
||||
.unwrap_or_else(|| item.attr_span(tcx));
|
||||
) {
|
||||
Some((sp, _)) => sp,
|
||||
None => item.attr_span(tcx),
|
||||
};
|
||||
|
||||
tcx.node_span_lint(crate::lint::UNESCAPED_BACKTICKS, hir_id, span, |lint| {
|
||||
lint.primary_message("unescaped backtick");
|
||||
|
|
@ -419,7 +421,7 @@ fn suggest_insertion(
|
|||
/// Maximum bytes of context to show around the insertion.
|
||||
const CONTEXT_MAX_LEN: usize = 80;
|
||||
|
||||
if let Some(span) = source_span_for_markdown_range(
|
||||
if let Some((span, _)) = source_span_for_markdown_range(
|
||||
cx.tcx,
|
||||
dox,
|
||||
&(insert_index..insert_index),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue