diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 07d6efaa97e15..8a37f46b9a969 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -7,7 +7,6 @@ use std::fmt::Display; use std::mem; use std::ops::Range; -use rustc_ast::attr::AttributeExt; use rustc_ast::util::comments::may_have_doc_links; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; @@ -1038,10 +1037,11 @@ fn preprocessed_markdown_links(s: &str) -> Vec { impl LinkCollector<'_, '_> { #[instrument(level = "debug", skip_all)] fn resolve_links(&mut self, item: &Item) { + let tcx = self.cx.tcx; if !self.cx.document_private() && let Some(def_id) = item.item_id.as_def_id() && let Some(def_id) = def_id.as_local() - && !self.cx.tcx.effective_visibilities(()).is_exported(def_id) + && !tcx.effective_visibilities(()).is_exported(def_id) && !has_primitive_or_keyword_or_attribute_docs(&item.attrs.other_attrs) { // Skip link resolution for non-exported items. @@ -1049,9 +1049,9 @@ impl LinkCollector<'_, '_> { } let mut insert_links = |item_id, doc: &str| { - let module_id = match self.cx.tcx.def_kind(item_id) { - DefKind::Mod if item.inner_docs(self.cx.tcx) => item_id, - _ => find_nearest_parent_module(self.cx.tcx, item_id).unwrap(), + let module_id = match tcx.def_kind(item_id) { + DefKind::Mod if item.inner_docs(tcx) => item_id, + _ => find_nearest_parent_module(tcx, item_id).unwrap(), }; for md_link in preprocessed_markdown_links(&doc) { let link = self.resolve_link(&doc, item, item_id, module_id, &md_link); @@ -1084,7 +1084,14 @@ impl LinkCollector<'_, '_> { // Also resolve links in the note text of `#[deprecated]`. for attr in &item.attrs.other_attrs { - let Some(note_sym) = attr.deprecation_note() else { continue }; + let rustc_hir::Attribute::Parsed(rustc_hir::attrs::AttributeKind::Deprecation { + span, + deprecation, + }) = attr + else { + continue; + }; + let Some(note_sym) = deprecation.note else { continue }; let note = note_sym.as_str(); if !may_have_doc_links(note) { @@ -1092,7 +1099,18 @@ impl LinkCollector<'_, '_> { } debug!("deprecated_note={note}"); - insert_links(item.item_id.expect_def_id(), note) + // When resolving an intra-doc link inside a deprecation note that is on an inlined + // `use` statement, we need to use the `def_id` of the `use` statement, not the + // inlined item. + // + let item_id = if let Some(inline_stmt_id) = item.inline_stmt_id + && item.span(tcx).is_none_or(|item_span| !item_span.inner().contains(*span)) + { + inline_stmt_id.to_def_id() + } else { + item.item_id.expect_def_id() + }; + insert_links(item_id, note) } } diff --git a/tests/rustdoc-html/intra-doc/ice-deprecated-note-on-reexport.rs b/tests/rustdoc-html/intra-doc/ice-deprecated-note-on-reexport.rs new file mode 100644 index 0000000000000..99415a9a2fd4a --- /dev/null +++ b/tests/rustdoc-html/intra-doc/ice-deprecated-note-on-reexport.rs @@ -0,0 +1,11 @@ +// This test ensures that the intra-doc link in `deprecated` note is resolved at the correct +// location (ie in the current crate and not in the reexported item's location/crate) and +// therefore doesn't crash. +// +// This is a regression test for . + +#![crate_name = "foo"] + +#[deprecated(note = "use [`std::mem::forget`]")] +#[doc(inline)] +pub use std::mem::drop;