Refactor lint from rustc to rustdoc

This commit is contained in:
Kyle Lin 2023-06-30 18:10:01 +08:00
parent e583318aa8
commit c7369891ba
6 changed files with 519 additions and 102 deletions

View File

@ -1041,7 +1041,6 @@ fn resolve_link(
)?;
self.check_redundant_explicit_link(
&res,
path_str,
ResolutionInfo {
item_id,
@ -1388,7 +1387,6 @@ fn resolve_with_disambiguator(
/// Check if resolution of inline link's display text and explicit link are same.
fn check_redundant_explicit_link(
&mut self,
explicit_res: &Res,
explicit_link: &Box<str>,
display_res_info: ResolutionInfo,
ori_link: &MarkdownLink,
@ -1415,38 +1413,14 @@ fn check_redundant_explicit_link(
if explicit_len >= display_len
&& &explicit_link[(explicit_len - display_len)..] == display_text
{
let Some((display_res, _)) = self.resolve_with_disambiguator_cached(
self.resolve_with_disambiguator_cached(
display_res_info,
diag_info.clone(), // this struct should really be Copy, but Range is not :(
// For reference-style links we want to report only one error so unsuccessful
// resolutions are cached, for other links we want to report an error every
// time so they are not cached.
matches!(ori_link.kind, LinkType::Reference),
) else {
return;
};
if &display_res == explicit_res {
use crate::lint::REDUNDANT_EXPLICIT_LINKS;
report_diagnostic(
self.cx.tcx,
REDUNDANT_EXPLICIT_LINKS,
"redundant explicit rustdoc link",
&diag_info,
|diag, sp, _link_range| {
if let Some(sp) = sp {
diag.note("Explicit link does not affect the original link")
.span_suggestion_hidden(
sp,
"Remove explicit link instead",
format!(""),
Applicability::MachineApplicable,
);
}
},
);
}
);
}
}
}

View File

@ -4,6 +4,7 @@
mod bare_urls;
mod check_code_block_syntax;
mod html_tags;
mod redundant_explicit_links;
mod unescaped_backticks;
use super::Pass;
@ -29,6 +30,7 @@ fn visit_item(&mut self, item: &Item) {
check_code_block_syntax::visit_item(self.cx, item);
html_tags::visit_item(self.cx, item);
unescaped_backticks::visit_item(self.cx, item);
redundant_explicit_links::visit_item(self.cx, item);
self.visit_item_recur(item)
}

View File

@ -0,0 +1,188 @@
use std::ops::Range;
use pulldown_cmark::{Parser, BrokenLink, Event, Tag, LinkType, OffsetIter};
use rustc_ast::NodeId;
use rustc_errors::SuggestionStyle;
use rustc_hir::HirId;
use rustc_hir::def::{Namespace, DefKind, DocLinkResMap, Res};
use rustc_lint_defs::Applicability;
use rustc_span::Symbol;
use crate::clean::Item;
use crate::clean::utils::find_nearest_parent_module;
use crate::core::DocContext;
use crate::html::markdown::main_body_opts;
use crate::passes::source_span_for_markdown_range;
struct LinkData {
resolvable_link: Option<String>,
resolvable_link_range: Option<Range<usize>>,
display_link: String,
}
pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
let Some(hir_id) = DocContext::as_local_hir_id(cx.tcx, item.item_id) else {
// If non-local, no need to check anything.
return;
};
let doc = item.doc_value();
if doc.is_empty() {
return;
}
check_redundant_explicit_link(cx, item, hir_id, &doc);
}
fn check_redundant_explicit_link<'md>(cx: &DocContext<'_>, item: &Item, hir_id: HirId, doc: &'md str) {
let mut broken_line_callback = |link: BrokenLink<'md>| Some((link.reference, "".into()));
let mut offset_iter = Parser::new_with_broken_link_callback(&doc, main_body_opts(), Some(&mut broken_line_callback)).into_offset_iter();
while let Some((event, link_range)) = offset_iter.next() {
match event {
Event::Start(Tag::Link(link_type, dest, _)) => {
let link_data = collect_link_data(&mut offset_iter);
let dest = dest.to_string();
if link_type == LinkType::Inline {
check_inline_link_redundancy(cx, item, hir_id, doc, link_range, dest, link_data);
}
}
_ => {}
}
}
}
fn check_inline_link_redundancy(cx: &DocContext<'_>, item: &Item, hir_id: HirId, doc: &str, link_range: Range<usize>, dest: String, link_data: LinkData) -> Option<()> {
let item_id = item.def_id()?;
let module_id = match cx.tcx.def_kind(item_id) {
DefKind::Mod if item.inner_docs(cx.tcx) => item_id,
_ => find_nearest_parent_module(cx.tcx, item_id).unwrap(),
};
let resolutions = cx.tcx.doc_link_resolutions(module_id);
let (resolvable_link, resolvable_link_range) = (&link_data.resolvable_link?, &link_data.resolvable_link_range?);
let (dest_res, display_res) = (find_resolution(resolutions, &dest)?, find_resolution(resolutions, resolvable_link)?);
if dest_res == display_res {
let link_span = source_span_for_markdown_range(
cx.tcx,
&doc,
&link_range,
&item.attrs,
).unwrap_or(item.attr_span(cx.tcx));
let explicit_span = source_span_for_markdown_range(
cx.tcx,
&doc,
&offset_explicit_range(doc, &link_range, b'(', b')'),
&item.attrs
)?;
let display_span = source_span_for_markdown_range(
cx.tcx,
&doc,
&resolvable_link_range,
&item.attrs
)?;
cx.tcx.struct_span_lint_hir(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, "redundant explicit link target", |lint| {
lint.span_label(explicit_span, "explicit target is redundant")
.span_label(display_span, "because label contains path that resolves to same destination")
.note("when a link's destination is not specified,\nthe label is used to resolve intra-doc links")
.span_suggestion_with_style(link_span, "remove explicit link target", format!("[{}]", link_data.display_link), Applicability::MaybeIncorrect, SuggestionStyle::ShowAlways);
lint
});
}
None
}
fn find_resolution<'tcx>(resolutions: &'tcx DocLinkResMap, path: &str) -> Option<&'tcx Res<NodeId>> {
for ns in [Namespace::TypeNS, Namespace::ValueNS, Namespace::MacroNS] {
let Some(Some(res)) = resolutions.get(&(Symbol::intern(path), ns))
else {
continue;
};
return Some(res);
}
None
}
/// Collects all neccessary data of link.
fn collect_link_data(offset_iter: &mut OffsetIter<'_, '_>) -> LinkData {
let mut resolvable_link = None;
let mut resolvable_link_range = None;
let mut display_link = String::new();
while let Some((event, range)) = offset_iter.next() {
match event {
Event::Text(code) => {
let code = code.to_string();
display_link.push_str(&code);
resolvable_link = Some(code);
resolvable_link_range = Some(range);
}
Event::Code(code) => {
let code = code.to_string();
display_link.push('`');
display_link.push_str(&code);
display_link.push('`');
resolvable_link = Some(code);
resolvable_link_range = Some(range);
}
Event::End(_) => {
break;
}
_ => {}
}
}
LinkData {
resolvable_link,
resolvable_link_range,
display_link,
}
}
fn offset_explicit_range(md: &str, link_range: &Range<usize>, open: u8, close: u8) -> Range<usize> {
let mut open_brace = !0;
let mut close_brace = !0;
for (i, b) in md.as_bytes()[link_range.clone()].iter().copied().enumerate().rev() {
let i = i + link_range.start;
if b == close {
close_brace = i;
break;
}
}
if close_brace < link_range.start || close_brace >= link_range.end {
return link_range.clone();
}
let mut nesting = 1;
for (i, b) in md.as_bytes()[link_range.start..close_brace].iter().copied().enumerate().rev() {
let i = i + link_range.start;
if b == close {
nesting += 1;
}
if b == open {
nesting -= 1;
}
if nesting == 0 {
open_brace = i;
break;
}
}
assert!(open_brace != close_brace);
if open_brace < link_range.start || open_brace >= link_range.end {
return link_range.clone();
}
// do not actually include braces in the span
(open_brace + 1)..close_brace
}

View File

@ -0,0 +1,54 @@
// run-rustfix
#![deny(rustdoc::redundant_explicit_links)]
pub fn dummy_target() {}
/// [dummy_target]
//~^ ERROR redundant explicit link target
/// [`dummy_target`]
//~^ ERROR redundant explicit link target
///
/// [Vec]
//~^ ERROR redundant explicit link target
/// [`Vec`]
//~^ ERROR redundant explicit link target
/// [Vec]
//~^ ERROR redundant explicit link target
/// [`Vec`]
//~^ ERROR redundant explicit link target
/// [std::vec::Vec]
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`]
//~^ ERROR redundant explicit link target
/// [std::vec::Vec]
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`]
//~^ ERROR redundant explicit link target
///
/// [usize]
//~^ ERROR redundant explicit link target
/// [`usize`]
//~^ ERROR redundant explicit link target
/// [usize]
//~^ ERROR redundant explicit link target
/// [`usize`]
//~^ ERROR redundant explicit link target
/// [std::primitive::usize]
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`]
//~^ ERROR redundant explicit link target
/// [std::primitive::usize]
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`]
//~^ ERROR redundant explicit link target
///
/// [dummy_target] TEXT
//~^ ERROR redundant explicit link target
/// [`dummy_target`] TEXT
//~^ ERROR redundant explicit link target
pub fn should_warn_inline() {}
/// [`Vec<T>`](Vec)
/// [`Vec<T>`](std::vec::Vec)
pub fn should_not_warn_inline() {}

View File

@ -1,29 +1,54 @@
// run-rustfix
#![deny(rustdoc::redundant_explicit_links)]
pub fn dummy_target() {}
/// [dummy_target](dummy_target)
//~^ ERROR redundant explicit link target
/// [`dummy_target`](dummy_target)
//~^ ERROR redundant explicit link target
///
/// [Vec](Vec)
//~^ ERROR redundant explicit link target
/// [`Vec`](Vec)
//~^ ERROR redundant explicit link target
/// [Vec](std::vec::Vec)
//~^ ERROR redundant explicit link target
/// [`Vec`](std::vec::Vec)
//~^ ERROR redundant explicit link target
/// [std::vec::Vec](Vec)
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`](Vec)
//~^ ERROR redundant explicit link target
/// [std::vec::Vec](std::vec::Vec)
//~^ ERROR redundant explicit link target
/// [`std::vec::Vec`](std::vec::Vec)
//~^ ERROR redundant explicit link target
///
/// [usize](usize)
//~^ ERROR redundant explicit link target
/// [`usize`](usize)
//~^ ERROR redundant explicit link target
/// [usize](std::primitive::usize)
//~^ ERROR redundant explicit link target
/// [`usize`](std::primitive::usize)
//~^ ERROR redundant explicit link target
/// [std::primitive::usize](usize)
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`](usize)
//~^ ERROR redundant explicit link target
/// [std::primitive::usize](std::primitive::usize)
//~^ ERROR redundant explicit link target
/// [`std::primitive::usize`](std::primitive::usize)
pub fn should_warn() {}
//~^ ERROR redundant explicit link target
///
/// [dummy_target](dummy_target) TEXT
//~^ ERROR redundant explicit link target
/// [`dummy_target`](dummy_target) TEXT
//~^ ERROR redundant explicit link target
pub fn should_warn_inline() {}
/// [`Vec<T>`](Vec)
/// [`Vec<T>`](std::vec::Vec)
pub fn should_not_warn() {}
pub fn should_not_warn_inline() {}

View File

@ -1,133 +1,307 @@
error: redundant explicit rustdoc link
--> $DIR/redundant_explicit_links.rs:5:20
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:7:20
|
LL | /// [dummy_target](dummy_target)
| ^^^^^^^^^^^^
| ------------ ^^^^^^^^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: Explicit link does not affect the original link
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
note: the lint level is defined here
--> $DIR/redundant_explicit_links.rs:1:9
--> $DIR/redundant_explicit_links.rs:3:9
|
LL | #![deny(rustdoc::redundant_explicit_links)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: Remove explicit link instead
help: remove explicit link target
|
LL | /// [dummy_target]
| ~~~~~~~~~~~~~~
error: redundant explicit rustdoc link
--> $DIR/redundant_explicit_links.rs:6:22
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:9:22
|
LL | /// [`dummy_target`](dummy_target)
| ^^^^^^^^^^^^
| -------------- ^^^^^^^^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: Explicit link does not affect the original link
= help: Remove explicit link instead
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [`dummy_target`]
| ~~~~~~~~~~~~~~~~
error: redundant explicit rustdoc link
--> $DIR/redundant_explicit_links.rs:8:11
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:12:11
|
LL | /// [Vec](Vec)
| ^^^
| --- ^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: Explicit link does not affect the original link
= help: Remove explicit link instead
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [Vec]
| ~~~~~
error: redundant explicit rustdoc link
--> $DIR/redundant_explicit_links.rs:9:13
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:14:13
|
LL | /// [`Vec`](Vec)
| ^^^
| ----- ^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: Explicit link does not affect the original link
= help: Remove explicit link instead
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [`Vec`]
| ~~~~~~~
error: redundant explicit rustdoc link
--> $DIR/redundant_explicit_links.rs:10:11
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:16:11
|
LL | /// [Vec](std::vec::Vec)
| ^^^^^^^^^^^^^
| --- ^^^^^^^^^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: Explicit link does not affect the original link
= help: Remove explicit link instead
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [Vec]
| ~~~~~
error: redundant explicit rustdoc link
--> $DIR/redundant_explicit_links.rs:11:13
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:18:13
|
LL | /// [`Vec`](std::vec::Vec)
| ^^^^^^^^^^^^^
| ----- ^^^^^^^^^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: Explicit link does not affect the original link
= help: Remove explicit link instead
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [`Vec`]
| ~~~~~~~
error: redundant explicit rustdoc link
--> $DIR/redundant_explicit_links.rs:14:21
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:20:21
|
LL | /// [std::vec::Vec](Vec)
| ------------- ^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [std::vec::Vec]
| ~~~~~~~~~~~~~~~
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:22:23
|
LL | /// [`std::vec::Vec`](Vec)
| --------------- ^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [`std::vec::Vec`]
| ~~~~~~~~~~~~~~~~~
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:24:21
|
LL | /// [std::vec::Vec](std::vec::Vec)
| ^^^^^^^^^^^^^
| ------------- ^^^^^^^^^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: Explicit link does not affect the original link
= help: Remove explicit link instead
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [std::vec::Vec]
| ~~~~~~~~~~~~~~~
error: redundant explicit rustdoc link
--> $DIR/redundant_explicit_links.rs:15:23
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:26:23
|
LL | /// [`std::vec::Vec`](std::vec::Vec)
| ^^^^^^^^^^^^^
| --------------- ^^^^^^^^^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: Explicit link does not affect the original link
= help: Remove explicit link instead
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [`std::vec::Vec`]
| ~~~~~~~~~~~~~~~~~
error: redundant explicit rustdoc link
--> $DIR/redundant_explicit_links.rs:17:13
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:29:13
|
LL | /// [usize](usize)
| ^^^^^
| ----- ^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: Explicit link does not affect the original link
= help: Remove explicit link instead
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [usize]
| ~~~~~~~
error: redundant explicit rustdoc link
--> $DIR/redundant_explicit_links.rs:18:15
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:31:15
|
LL | /// [`usize`](usize)
| ^^^^^
| ------- ^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: Explicit link does not affect the original link
= help: Remove explicit link instead
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [`usize`]
| ~~~~~~~~~
error: redundant explicit rustdoc link
--> $DIR/redundant_explicit_links.rs:19:13
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:33:13
|
LL | /// [usize](std::primitive::usize)
| ^^^^^^^^^^^^^^^^^^^^^
| ----- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: Explicit link does not affect the original link
= help: Remove explicit link instead
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [usize]
| ~~~~~~~
error: redundant explicit rustdoc link
--> $DIR/redundant_explicit_links.rs:20:15
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:35:15
|
LL | /// [`usize`](std::primitive::usize)
| ^^^^^^^^^^^^^^^^^^^^^
| ------- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: Explicit link does not affect the original link
= help: Remove explicit link instead
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [`usize`]
| ~~~~~~~~~
error: redundant explicit rustdoc link
--> $DIR/redundant_explicit_links.rs:23:29
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:37:29
|
LL | /// [std::primitive::usize](usize)
| --------------------- ^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [std::primitive::usize]
| ~~~~~~~~~~~~~~~~~~~~~~~
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:39:31
|
LL | /// [`std::primitive::usize`](usize)
| ----------------------- ^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [`std::primitive::usize`]
| ~~~~~~~~~~~~~~~~~~~~~~~~~
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:41:29
|
LL | /// [std::primitive::usize](std::primitive::usize)
| ^^^^^^^^^^^^^^^^^^^^^
| --------------------- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: Explicit link does not affect the original link
= help: Remove explicit link instead
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [std::primitive::usize]
| ~~~~~~~~~~~~~~~~~~~~~~~
error: redundant explicit rustdoc link
--> $DIR/redundant_explicit_links.rs:24:31
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:43:31
|
LL | /// [`std::primitive::usize`](std::primitive::usize)
| ^^^^^^^^^^^^^^^^^^^^^
| ----------------------- ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: Explicit link does not affect the original link
= help: Remove explicit link instead
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [`std::primitive::usize`]
| ~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 14 previous errors
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:46:20
|
LL | /// [dummy_target](dummy_target) TEXT
| ------------ ^^^^^^^^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [dummy_target] TEXT
| ~~~~~~~~~~~~~~
error: redundant explicit link target
--> $DIR/redundant_explicit_links.rs:48:22
|
LL | /// [`dummy_target`](dummy_target) TEXT
| -------------- ^^^^^^^^^^^^ explicit target is redundant
| |
| because label contains path that resolves to same destination
|
= note: when a link's destination is not specified,
the label is used to resolve intra-doc links
help: remove explicit link target
|
LL | /// [`dummy_target`] TEXT
| ~~~~~~~~~~~~~~~~
error: aborting due to 20 previous errors