diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index d433391f272..083d16d3b04 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -392,16 +392,57 @@ pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec(doc: &'md str) -> Vec> { + let mut broken_link_callback = |link: BrokenLink<'md>| Some((link.reference, "".into())); + let mut event_iter = Parser::new_with_broken_link_callback( &doc, main_body_opts(), - Some(&mut |link: BrokenLink<'_>| Some((link.reference, "".into()))), + Some(&mut broken_link_callback), ) - .filter_map(|event| match event { - Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => { - Some(preprocess_link(&dest)) + .into_iter(); + let mut links = Vec::new(); + + while let Some(event) = event_iter.next() { + match event { + Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => { + if let Some(display_text) = collect_link_data(&mut event_iter) { + links.push(display_text); + } + + links.push(preprocess_link(&dest)); + } + _ => {} } - _ => None, - }) - .collect() + } + + links +} + +/// Collects additional data of link. +fn collect_link_data<'input, 'callback>( + event_iter: &mut Parser<'input, 'callback>, +) -> Option> { + let mut display_text = None; + + while let Some(event) = event_iter.next() { + match event { + Event::Text(code) => { + display_text = Some(code.to_string().into_boxed_str()); + } + Event::Code(code) => { + display_text = Some(code.to_string().into_boxed_str()); + } + Event::End(_) => { + break; + } + _ => {} + } + } + + display_text } diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index fe06b303e72..f15e6e451e7 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -420,17 +420,29 @@ as computed automatic links. This usually means the explicit links is removeable. For example: ```rust -#![warn(rustdoc::redundant_explicit_links)] +#![warn(rustdoc::redundant_explicit_links)] // note: unnecessary - warns by default. -pub fn dummy_target() {} // note: unnecessary - warns by default. - -/// [`dummy_target`](dummy_target) -/// [dummy_target](dummy_target) -pub fn c() {} +/// add takes 2 [`usize`](usize) and performs addition +/// on them, then returns result. +pub fn add(left: usize, right: usize) -> usize { + left + right +} ``` Which will give: ```text - +error: redundant explicit rustdoc link + --> src/lib.rs:3:27 + | +3 | /// add takes 2 [`usize`](usize) and performs addition + | ^^^^^ + | + = note: Explicit link does not affect the original link +note: the lint level is defined here + --> src/lib.rs:1:9 + | +1 | #![deny(rustdoc::redundant_explicit_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: Remove explicit link instead ``` diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index b5ab8a7b12a..2fe052283a9 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -1421,6 +1421,7 @@ pub(crate) fn markdown_links<'md, R>( links } +/// Collects additional data of link. fn collect_link_data<'input, 'callback>( event_iter: &mut OffsetIter<'input, 'callback>, ) -> Option> { diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 78b86cdfa82..2ed0077c3d5 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -994,15 +994,7 @@ impl LinkCollector<'_, '_> { _ => find_nearest_parent_module(self.cx.tcx, item_id).unwrap(), }; for md_link in preprocessed_markdown_links(&doc) { - let PreprocessedMarkdownLink(_pp_link, ori_link) = &md_link; - let diag_info = DiagnosticInfo { - item, - dox: &doc, - ori_link: &ori_link.link, - link_range: ori_link.range.clone(), - }; - - let link = self.resolve_link(item, item_id, module_id, &md_link, &diag_info); + let link = self.resolve_link(&doc, item, item_id, module_id, &md_link); if let Some(link) = link { self.cx.cache.intra_doc_links.entry(item.item_id).or_default().insert(link); } @@ -1015,14 +1007,20 @@ impl LinkCollector<'_, '_> { /// FIXME(jynelson): this is way too many arguments fn resolve_link( &mut self, + dox: &String, item: &Item, item_id: DefId, module_id: DefId, PreprocessedMarkdownLink(pp_link, ori_link): &PreprocessedMarkdownLink, - diag_info: &DiagnosticInfo<'_>, ) -> Option { trace!("considering link '{}'", ori_link.link); + let diag_info = DiagnosticInfo { + item, + dox, + ori_link: &ori_link.link, + link_range: ori_link.range.clone(), + }; let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text } = pp_link.as_ref().map_err(|err| err.report(self.cx, diag_info.clone())).ok()?; let disambiguator = *disambiguator; @@ -1045,11 +1043,14 @@ impl LinkCollector<'_, '_> { self.check_redundant_explicit_link( &res, path_str, - item_id, - module_id, - disambiguator, + ResolutionInfo { + item_id, + module_id, + dis: disambiguator, + path_str: ori_link.display_text.clone().into_boxed_str(), + extra_fragment: extra_fragment.clone(), + }, &ori_link, - extra_fragment, &diag_info, ); @@ -1384,15 +1385,13 @@ impl LinkCollector<'_, '_> { } } + /// Check if resolution of inline link's display text and explicit link are same. fn check_redundant_explicit_link( &mut self, - ex_res: &Res, - ex: &Box, - item_id: DefId, - module_id: DefId, - dis: Option, + explicit_res: &Res, + explicit_link: &Box, + display_res_info: ResolutionInfo, ori_link: &MarkdownLink, - extra_fragment: &Option, diag_info: &DiagnosticInfo<'_>, ) { // Check if explicit resolution's path is same as resolution of original link's display text path, e.g. @@ -1409,21 +1408,15 @@ impl LinkCollector<'_, '_> { return; } - let di_text = &ori_link.display_text; - let di_len = di_text.len(); - let ex_len = ex.len(); + let display_text = &ori_link.display_text; + let display_len = display_text.len(); + let explicit_len = explicit_link.len(); - let intra_doc_links = std::mem::take(&mut self.cx.cache.intra_doc_links); - - if ex_len >= di_len && &ex[(ex_len - di_len)..] == di_text { - let Some((di_res, _)) = self.resolve_with_disambiguator_cached( - ResolutionInfo { - item_id, - module_id, - dis, - path_str: di_text.clone().into_boxed_str(), - extra_fragment: extra_fragment.clone(), - }, + if explicit_len >= display_len + && &explicit_link[(explicit_len - display_len)..] == display_text + { + let Some((display_res, _)) = 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 @@ -1433,7 +1426,7 @@ impl LinkCollector<'_, '_> { return; }; - if &di_res == ex_res { + if &display_res == explicit_res { use crate::lint::REDUNDANT_EXPLICIT_LINKS; report_diagnostic( @@ -1444,9 +1437,14 @@ impl LinkCollector<'_, '_> { |diag, sp, _link_range| { if let Some(sp) = sp { diag.note("Explicit link does not affect the original link") - .span_suggestion(sp, "Remove explicit link instead", format!("[{}]", ori_link.link), Applicability::MachineApplicable); + .span_suggestion_hidden( + sp, + "Remove explicit link instead", + format!(""), + Applicability::MachineApplicable, + ); } - } + }, ); } } diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links.rs b/tests/rustdoc-ui/lints/redundant_explicit_links.rs index e794388476b..d33396f6810 100644 --- a/tests/rustdoc-ui/lints/redundant_explicit_links.rs +++ b/tests/rustdoc-ui/lints/redundant_explicit_links.rs @@ -2,5 +2,20 @@ pub fn dummy_target() {} +/// [dummy_target](dummy_target) +/// [`dummy_target`](dummy_target) +/// [Vec](Vec) +/// [`Vec`](Vec) +/// [Vec](std::vec::Vec) /// [`Vec`](std::vec::Vec) -pub fn c() {} +/// [std::vec::Vec](std::vec::Vec) +/// [`std::vec::Vec`](std::vec::Vec) +/// [usize](usize) +/// [`usize`](usize) +/// [std::primitive::usize](usize) +/// [`std::primitive::usize`](usize) +pub fn should_warn() {} + +/// [`Vec`](Vec) +/// [`Vec`](std::vec::Vec) +pub fn should_not_warn() {} diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links.stderr b/tests/rustdoc-ui/lints/redundant_explicit_links.stderr new file mode 100644 index 00000000000..4ca427d62ce --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links.stderr @@ -0,0 +1,97 @@ +error: redundant explicit rustdoc link + --> $DIR/redundant_explicit_links.rs:5:20 + | +LL | /// [dummy_target](dummy_target) + | ^^^^^^^^^^^^ + | + = note: Explicit link does not affect the original link +note: the lint level is defined here + --> $DIR/redundant_explicit_links.rs:1:9 + | +LL | #![deny(rustdoc::redundant_explicit_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: Remove explicit link instead + +error: redundant explicit rustdoc link + --> $DIR/redundant_explicit_links.rs:6:22 + | +LL | /// [`dummy_target`](dummy_target) + | ^^^^^^^^^^^^ + | + = note: Explicit link does not affect the original link + = help: Remove explicit link instead + +error: redundant explicit rustdoc link + --> $DIR/redundant_explicit_links.rs:7:11 + | +LL | /// [Vec](Vec) + | ^^^ + | + = note: Explicit link does not affect the original link + = help: Remove explicit link instead + +error: redundant explicit rustdoc link + --> $DIR/redundant_explicit_links.rs:8:13 + | +LL | /// [`Vec`](Vec) + | ^^^ + | + = note: Explicit link does not affect the original link + = help: Remove explicit link instead + +error: redundant explicit rustdoc link + --> $DIR/redundant_explicit_links.rs:9:11 + | +LL | /// [Vec](std::vec::Vec) + | ^^^^^^^^^^^^^ + | + = note: Explicit link does not affect the original link + = help: Remove explicit link instead + +error: redundant explicit rustdoc link + --> $DIR/redundant_explicit_links.rs:10:13 + | +LL | /// [`Vec`](std::vec::Vec) + | ^^^^^^^^^^^^^ + | + = note: Explicit link does not affect the original link + = help: Remove explicit link instead + +error: redundant explicit rustdoc link + --> $DIR/redundant_explicit_links.rs:11:21 + | +LL | /// [std::vec::Vec](std::vec::Vec) + | ^^^^^^^^^^^^^ + | + = note: Explicit link does not affect the original link + = help: Remove explicit link instead + +error: redundant explicit rustdoc link + --> $DIR/redundant_explicit_links.rs:12:23 + | +LL | /// [`std::vec::Vec`](std::vec::Vec) + | ^^^^^^^^^^^^^ + | + = note: Explicit link does not affect the original link + = help: Remove explicit link instead + +error: redundant explicit rustdoc link + --> $DIR/redundant_explicit_links.rs:13:13 + | +LL | /// [usize](usize) + | ^^^^^ + | + = note: Explicit link does not affect the original link + = help: Remove explicit link instead + +error: redundant explicit rustdoc link + --> $DIR/redundant_explicit_links.rs:14:15 + | +LL | /// [`usize`](usize) + | ^^^^^ + | + = note: Explicit link does not affect the original link + = help: Remove explicit link instead + +error: aborting due to 10 previous errors +