diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 39912a136ab22..401e2ed2884a0 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -251,6 +251,7 @@ struct ResolutionInfo { extra_fragment: Option, } +#[derive(Clone)] struct DiagnosticInfo<'a> { item: &'a Item, dox: &'a str, @@ -916,19 +917,19 @@ impl LinkCollector<'_, '_> { return None; } + let diag_info = DiagnosticInfo { + item, + dox, + ori_link: &ori_link.link, + link_range: ori_link.range.clone(), + }; + let link = ori_link.link.replace("`", ""); let no_backticks_range = range_between_backticks(&ori_link); let parts = link.split('#').collect::>(); let (link, extra_fragment) = if parts.len() > 2 { // A valid link can't have multiple #'s - anchor_failure( - self.cx, - &item, - &link, - dox, - ori_link.range, - AnchorFailure::MultipleAnchors, - ); + anchor_failure(self.cx, diag_info, AnchorFailure::MultipleAnchors); return None; } else if parts.len() == 2 { if parts[0].trim().is_empty() { @@ -950,7 +951,7 @@ impl LinkCollector<'_, '_> { // See issue #83859. let disambiguator_range = (no_backticks_range.start + relative_range.start) ..(no_backticks_range.start + relative_range.end); - disambiguator_error(self.cx, &item, dox, disambiguator_range, &err_msg); + disambiguator_error(self.cx, diag_info, disambiguator_range, &err_msg); } return None; } @@ -989,11 +990,9 @@ impl LinkCollector<'_, '_> { debug!("attempting to resolve item without parent module: {}", path_str); resolution_failure( self, - &item, + diag_info, path_str, disambiguator, - dox, - ori_link.range, smallvec![ResolutionFailure::NoParentItem], ); return None; @@ -1039,11 +1038,9 @@ impl LinkCollector<'_, '_> { debug!("link has malformed generics: {}", path_str); resolution_failure( self, - &item, + diag_info, path_str, disambiguator, - dox, - ori_link.range, smallvec![err_kind], ); return None; @@ -1059,12 +1056,6 @@ impl LinkCollector<'_, '_> { return None; } - let diag_info = DiagnosticInfo { - item, - dox, - ori_link: &ori_link.link, - link_range: ori_link.range.clone(), - }; let (mut res, mut fragment) = self.resolve_with_disambiguator_cached( ResolutionInfo { module_id, @@ -1072,7 +1063,7 @@ impl LinkCollector<'_, '_> { path_str: path_str.to_owned(), extra_fragment, }, - diag_info, + diag_info.clone(), // this struct should really be Copy, but Range is not :( matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut), )?; @@ -1090,10 +1081,7 @@ impl LinkCollector<'_, '_> { if fragment.is_some() { anchor_failure( self.cx, - &item, - path_str, - dox, - ori_link.range, + diag_info, AnchorFailure::RustdocAnchorConflict(prim), ); return None; @@ -1103,7 +1091,7 @@ impl LinkCollector<'_, '_> { } else { // `[char]` when a `char` module is in scope let candidates = vec![res, prim]; - ambiguity_error(self.cx, &item, path_str, dox, ori_link.range, candidates); + ambiguity_error(self.cx, diag_info, path_str, candidates); return None; } } @@ -1123,15 +1111,7 @@ impl LinkCollector<'_, '_> { diag.note(¬e); suggest_disambiguator(resolved, diag, path_str, dox, sp, &ori_link.range); }; - report_diagnostic( - self.cx.tcx, - BROKEN_INTRA_DOC_LINKS, - &msg, - &item, - dox, - &ori_link.range, - callback, - ); + report_diagnostic(self.cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, &diag_info, callback); }; let verify = |kind: DefKind, id: DefId| { @@ -1171,7 +1151,7 @@ impl LinkCollector<'_, '_> { if self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_src) && !self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_dst) { - privacy_error(self.cx, &item, &path_str, dox, &ori_link); + privacy_error(self.cx, &diag_info, &path_str); } } @@ -1312,29 +1292,14 @@ impl LinkCollector<'_, '_> { } } } - resolution_failure( - self, - diag.item, - path_str, - disambiguator, - diag.dox, - diag.link_range, - smallvec![kind], - ); + resolution_failure(self, diag, path_str, disambiguator, smallvec![kind]); // This could just be a normal link or a broken link // we could potentially check if something is // "intra-doc-link-like" and warn in that case. None } Err(ErrorKind::AnchorFailure(msg)) => { - anchor_failure( - self.cx, - diag.item, - diag.ori_link, - diag.dox, - diag.link_range, - msg, - ); + anchor_failure(self.cx, diag, msg); None } } @@ -1351,14 +1316,7 @@ impl LinkCollector<'_, '_> { Ok(res) } Err(ErrorKind::AnchorFailure(msg)) => { - anchor_failure( - self.cx, - diag.item, - diag.ori_link, - diag.dox, - diag.link_range, - msg, - ); + anchor_failure(self.cx, diag, msg); return None; } Err(ErrorKind::Resolve(box kind)) => Err(kind), @@ -1366,14 +1324,7 @@ impl LinkCollector<'_, '_> { value_ns: match self.resolve(path_str, ValueNS, base_node, extra_fragment) { Ok(res) => Ok(res), Err(ErrorKind::AnchorFailure(msg)) => { - anchor_failure( - self.cx, - diag.item, - diag.ori_link, - diag.dox, - diag.link_range, - msg, - ); + anchor_failure(self.cx, diag, msg); return None; } Err(ErrorKind::Resolve(box kind)) => Err(kind), @@ -1402,11 +1353,9 @@ impl LinkCollector<'_, '_> { if len == 0 { resolution_failure( self, - diag.item, + diag, path_str, disambiguator, - diag.dox, - diag.link_range, candidates.into_iter().filter_map(|res| res.err()).collect(), ); // this could just be a normal link @@ -1423,14 +1372,7 @@ impl LinkCollector<'_, '_> { } // If we're reporting an ambiguity, don't mention the namespaces that failed let candidates = candidates.map(|candidate| candidate.ok().map(|(res, _)| res)); - ambiguity_error( - self.cx, - diag.item, - path_str, - diag.dox, - diag.link_range, - candidates.present_items().collect(), - ); + ambiguity_error(self.cx, diag, path_str, candidates.present_items().collect()); None } } @@ -1448,15 +1390,7 @@ impl LinkCollector<'_, '_> { break; } } - resolution_failure( - self, - diag.item, - path_str, - disambiguator, - diag.dox, - diag.link_range, - smallvec![kind], - ); + resolution_failure(self, diag, path_str, disambiguator, smallvec![kind]); None } } @@ -1690,9 +1624,7 @@ fn report_diagnostic( tcx: TyCtxt<'_>, lint: &'static Lint, msg: &str, - item: &Item, - dox: &str, - link_range: &Range, + DiagnosticInfo { item, ori_link: _, dox, link_range }: &DiagnosticInfo<'_>, decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option), ) { let hir_id = match DocContext::as_local_hir_id(tcx, item.def_id) { @@ -1746,11 +1678,9 @@ fn report_diagnostic( /// `std::io::Error::x`, this will resolve `std::io::Error`. fn resolution_failure( collector: &mut LinkCollector<'_, '_>, - item: &Item, + diag_info: DiagnosticInfo<'_>, path_str: &str, disambiguator: Option, - dox: &str, - link_range: Range, kinds: SmallVec<[ResolutionFailure<'_>; 3]>, ) { let tcx = collector.cx.tcx; @@ -1758,9 +1688,7 @@ fn resolution_failure( tcx, BROKEN_INTRA_DOC_LINKS, &format!("unresolved link to `{}`", path_str), - item, - dox, - &link_range, + &diag_info, |diag, sp| { let item = |res: Res| format!("the {} `{}`", res.descr(), res.name(tcx),); let assoc_item_not_allowed = |res: Res| { @@ -1920,9 +1848,9 @@ fn resolution_failure( disambiguator, diag, path_str, - dox, + diag_info.dox, sp, - &link_range, + &diag_info.link_range, ) } @@ -1969,24 +1897,19 @@ fn resolution_failure( } /// Report an anchor failure. -fn anchor_failure( - cx: &DocContext<'_>, - item: &Item, - path_str: &str, - dox: &str, - link_range: Range, - failure: AnchorFailure, -) { +fn anchor_failure(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>, failure: AnchorFailure) { let msg = match failure { - AnchorFailure::MultipleAnchors => format!("`{}` contains multiple anchors", path_str), + AnchorFailure::MultipleAnchors => { + format!("`{}` contains multiple anchors", diag_info.ori_link) + } AnchorFailure::RustdocAnchorConflict(res) => format!( "`{}` contains an anchor, but links to {kind}s are already anchored", - path_str, + diag_info.ori_link, kind = res.descr(), ), }; - report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, item, dox, &link_range, |diag, sp| { + report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, &diag_info, |diag, sp| { if let Some(sp) = sp { diag.span_label(sp, "contains invalid anchor"); } @@ -1996,21 +1919,19 @@ fn anchor_failure( /// Report an error in the link disambiguator. fn disambiguator_error( cx: &DocContext<'_>, - item: &Item, - dox: &str, - link_range: Range, + mut diag_info: DiagnosticInfo<'_>, + disambiguator_range: Range, msg: &str, ) { - report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, msg, item, dox, &link_range, |_diag, _sp| {}); + diag_info.link_range = disambiguator_range; + report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, msg, &diag_info, |_diag, _sp| {}); } /// Report an ambiguity error, where there were multiple possible resolutions. fn ambiguity_error( cx: &DocContext<'_>, - item: &Item, + diag_info: DiagnosticInfo<'_>, path_str: &str, - dox: &str, - link_range: Range, candidates: Vec, ) { let mut msg = format!("`{}` is ", path_str); @@ -2037,7 +1958,7 @@ fn ambiguity_error( } } - report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, item, dox, &link_range, |diag, sp| { + report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, &diag_info, |diag, sp| { if let Some(sp) = sp { diag.span_label(sp, "ambiguous link"); } else { @@ -2046,7 +1967,14 @@ fn ambiguity_error( for res in candidates { let disambiguator = Disambiguator::from_res(res); - suggest_disambiguator(disambiguator, diag, path_str, dox, sp, &link_range); + suggest_disambiguator( + disambiguator, + diag, + path_str, + diag_info.dox, + sp, + &diag_info.link_range, + ); } }); } @@ -2078,9 +2006,9 @@ fn suggest_disambiguator( } /// Report a link from a public item to a private one. -fn privacy_error(cx: &DocContext<'_>, item: &Item, path_str: &str, dox: &str, link: &MarkdownLink) { +fn privacy_error(cx: &DocContext<'_>, diag_info: &DiagnosticInfo<'_>, path_str: &str) { let sym; - let item_name = match item.name { + let item_name = match diag_info.item.name { Some(name) => { sym = name.as_str(); &*sym @@ -2090,7 +2018,7 @@ fn privacy_error(cx: &DocContext<'_>, item: &Item, path_str: &str, dox: &str, li let msg = format!("public documentation for `{}` links to private item `{}`", item_name, path_str); - report_diagnostic(cx.tcx, PRIVATE_INTRA_DOC_LINKS, &msg, item, dox, &link.range, |diag, sp| { + report_diagnostic(cx.tcx, PRIVATE_INTRA_DOC_LINKS, &msg, diag_info, |diag, sp| { if let Some(sp) = sp { diag.span_label(sp, "this item is private"); } diff --git a/src/test/rustdoc-ui/intra-doc/anchors.rs b/src/test/rustdoc-ui/intra-doc/anchors.rs index 009b291be1f08..6785cb7abeaee 100644 --- a/src/test/rustdoc-ui/intra-doc/anchors.rs +++ b/src/test/rustdoc-ui/intra-doc/anchors.rs @@ -43,3 +43,7 @@ pub fn enum_link() {} /// [u32#hello] //~^ ERROR `u32#hello` contains an anchor pub fn x() {} + +/// [prim@usize#x] +//~^ ERROR `prim@usize#x` contains an anchor +pub mod usize {} diff --git a/src/test/rustdoc-ui/intra-doc/anchors.stderr b/src/test/rustdoc-ui/intra-doc/anchors.stderr index 97b0cea0c1e4d..787a68ed969ef 100644 --- a/src/test/rustdoc-ui/intra-doc/anchors.stderr +++ b/src/test/rustdoc-ui/intra-doc/anchors.stderr @@ -1,8 +1,8 @@ -error: `Foo::f#hola` contains an anchor, but links to fields are already anchored - --> $DIR/anchors.rs:25:15 +error: `prim@usize#x` contains an anchor, but links to builtin types are already anchored + --> $DIR/anchors.rs:47:6 | -LL | /// Or maybe [Foo::f#hola]. - | ^^^^^^^^^^^ contains invalid anchor +LL | /// [prim@usize#x] + | ^^^^^^^^^^^^ contains invalid anchor | note: the lint level is defined here --> $DIR/anchors.rs:1:9 @@ -10,6 +10,12 @@ note: the lint level is defined here LL | #![deny(rustdoc::broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: `Foo::f#hola` contains an anchor, but links to fields are already anchored + --> $DIR/anchors.rs:25:15 + | +LL | /// Or maybe [Foo::f#hola]. + | ^^^^^^^^^^^ contains invalid anchor + error: `hello#people#!` contains multiple anchors --> $DIR/anchors.rs:31:28 | @@ -28,5 +34,5 @@ error: `u32#hello` contains an anchor, but links to builtin types are already an LL | /// [u32#hello] | ^^^^^^^^^ contains invalid anchor -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors