Rollup merge of #141648 - GuillaumeGomez:redundant_explicit_links-expansion, r=lolbinarycat

[rustdoc] Do not emit redundant_explicit_links lint if the doc comment comes from expansion

Fixes https://github.com/rust-lang/rust/issues/141553.

The problem was that we change the context for the attributes in some cases to get better error output, preventing us to detect if the attribute comes from expansion. Most of the changes are about keeping track of the "does this span comes from expansion" information.

r? ```@Manishearth```
This commit is contained in:
Matthias Krüger 2025-06-26 15:47:18 +02:00 committed by GitHub
commit cbcf183711
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 212 additions and 65 deletions

View file

@ -10,6 +10,7 @@ fn create_doc_fragment(s: &str) -> Vec<DocFragment> {
doc: Symbol::intern(s),
kind: DocFragmentKind::SugaredDoc,
indent: 0,
from_expansion: false,
}]
}

View file

@ -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(),
),
};

View file

@ -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)

View file

@ -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),
};

View file

@ -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:

View file

@ -161,20 +161,36 @@ 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, false) = 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(
)?
else {
// This `span` comes from macro expansion so skipping it.
return None;
};
let (display_span, false) = source_span_for_markdown_range(
cx.tcx,
doc,
resolvable_link_range,
&item.attrs.doc_strings,
)?;
)?
else {
// This `span` comes from macro expansion so skipping it.
return None;
};
cx.tcx.node_span_lint(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, |lint| {
lint.primary_message("redundant explicit link target")
@ -206,21 +222,37 @@ 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, false) = 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(
)?
else {
// This `span` comes from macro expansion so skipping it.
return None;
};
let (display_span, false) = source_span_for_markdown_range(
cx.tcx,
doc,
resolvable_link_range,
&item.attrs.doc_strings,
)?;
let def_span = source_span_for_markdown_range(
)?
else {
// This `span` comes from macro expansion so skipping it.
return None;
};
let (def_span, _) = source_span_for_markdown_range(
cx.tcx,
doc,
&offset_reference_def_range(doc, dest, link_range),

View file

@ -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),

View file

@ -765,8 +765,8 @@ impl Fragments<'_> {
/// get the span for the markdown range. Note that this function is not cheap, use it with
/// caution.
#[must_use]
fn span(&self, cx: &LateContext<'_>, range: Range<usize>) -> Option<Span> {
source_span_for_markdown_range(cx.tcx, self.doc, &range, self.fragments)
fn span(self, cx: &LateContext<'_>, range: Range<usize>) -> Option<Span> {
source_span_for_markdown_range(cx.tcx, self.doc, &range, self.fragments).map(|(sp, _)| sp)
}
}