diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs index 13bc4c079aa7..8da21f100c6a 100644 --- a/src/librustdoc/passes/lint/redundant_explicit_links.rs +++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs @@ -90,12 +90,23 @@ fn check_redundant_explicit_link<'md>( .into_offset_iter(); while let Some((event, link_range)) = offset_iter.next() { - if let Event::Start(Tag::Link { link_type, dest_url, .. }) = event { + if let Event::Start(Tag::Link { link_type, dest_url, title, .. }) = event { + if !title.is_empty() { + // Skips if the link specifies a title, e.g. `[Option](Option "title")`, + // in which case the explicit link cannot be removed without also + // removing the title. + continue; + } + let link_data = collect_link_data(&mut offset_iter); - if let Some(resolvable_link) = link_data.resolvable_link.as_ref() - && &link_data.display_link.replace('`', "") != resolvable_link - { + let Some(resolvable_link) = link_data.resolvable_link.as_ref() else { + // collect_link_data didn't return a resolvable_link + // most likely due to the displayed link containing inline markup + continue; + }; + + if &link_data.display_link.replace('`', "") != resolvable_link { // Skips if display link does not match to actual // resolvable link, usually happens if display link // has several segments, e.g. @@ -103,10 +114,7 @@ fn check_redundant_explicit_link<'md>( continue; } - let explicit_link = dest_url.to_string(); - let display_link = link_data.resolvable_link.clone()?; - - if explicit_link.ends_with(&display_link) || display_link.ends_with(&explicit_link) { + if dest_url.ends_with(resolvable_link) || resolvable_link.ends_with(&*dest_url) { match link_type { LinkType::Inline | LinkType::ReferenceUnknown => { check_inline_or_reference_unknown_redundancy( diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.fixed b/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.fixed new file mode 100644 index 000000000000..75e2398e64c5 --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.fixed @@ -0,0 +1,17 @@ +//@ run-rustfix + +// There was a logic error in `redundant_explicit_links` that caused the lint +// to skip all remaining links once it skipped a link containing inline markups. +// This test asserts that the lint continues after skipping such links. + +#![deny(rustdoc::redundant_explicit_links)] + +/// [Option] +///~^ ERROR redundant explicit link target +/// +/// [**u8**](u8) +/// This link should not lint. +/// +/// [Result] +///~^ ERROR redundant explicit link target +pub fn func() {} diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.rs b/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.rs new file mode 100644 index 000000000000..0c39ad8f1801 --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.rs @@ -0,0 +1,17 @@ +//@ run-rustfix + +// There was a logic error in `redundant_explicit_links` that caused the lint +// to skip all remaining links once it skipped a link containing inline markups. +// This test asserts that the lint continues after skipping such links. + +#![deny(rustdoc::redundant_explicit_links)] + +/// [Option][Option] +///~^ ERROR redundant explicit link target +/// +/// [**u8**](u8) +/// This link should not lint. +/// +/// [Result][Result] +///~^ ERROR redundant explicit link target +pub fn func() {} diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.stderr b/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.stderr new file mode 100644 index 000000000000..61f4ee584da8 --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.stderr @@ -0,0 +1,39 @@ +error: redundant explicit link target + --> $DIR/redundant_explicit_links-some-skipped.rs:9:14 + | +LL | /// [Option][Option] + | ------ ^^^^^^ 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 +note: the lint level is defined here + --> $DIR/redundant_explicit_links-some-skipped.rs:7:9 + | +LL | #![deny(rustdoc::redundant_explicit_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: remove explicit link target + | +LL - /// [Option][Option] +LL + /// [Option] + | + +error: redundant explicit link target + --> $DIR/redundant_explicit_links-some-skipped.rs:15:14 + | +LL | /// [Result][Result] + | ------ ^^^^^^ 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 - /// [Result][Result] +LL + /// [Result] + | + +error: aborting due to 2 previous errors + diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links-with-title.rs b/tests/rustdoc-ui/lints/redundant_explicit_links-with-title.rs new file mode 100644 index 000000000000..01b84563ce55 --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links-with-title.rs @@ -0,0 +1,15 @@ +//@ check-pass + +#![deny(rustdoc::redundant_explicit_links)] + +/// [drop](drop "This function is not magic") +/// +/// This link should not lint, because it specifies a link title, and it is +/// not possible to remove the explicit link without also removing the title. +/// +/// [Vec][vec] +/// +/// [vec]: std::vec::Vec "A contiguous growable array type" +/// +/// This also applies to reference-style links. +pub fn func() {}