Rollup merge of #109312 - petrochenkov:docice5, r=GuillaumeGomez
rustdoc: Cleanup parent module tracking for doc links Keep ids of the documented items themselves, not their parent modules. Parent modules can be retreived from those ids when necessary. Fixes https://github.com/rust-lang/rust/issues/108501. That issue could be fixed in a more local way, but this refactoring is something that I wanted to do since https://github.com/rust-lang/rust/pull/93805 anyway.
This commit is contained in:
commit
af3bd22783
10 changed files with 123 additions and 185 deletions
|
@ -26,11 +26,13 @@ pub enum DocFragmentKind {
|
|||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct DocFragment {
|
||||
pub span: Span,
|
||||
/// The module this doc-comment came from.
|
||||
///
|
||||
/// This allows distinguishing between the original documentation and a pub re-export.
|
||||
/// If it is `None`, the item was not re-exported.
|
||||
pub parent_module: Option<DefId>,
|
||||
/// The item this doc-comment came from.
|
||||
/// Used to determine the scope in which doc links in this fragment are resolved.
|
||||
/// Typically filled for reexport docs when they are merged into the docs of the
|
||||
/// original reexported item.
|
||||
/// If the id is not filled, which happens for the original reexported item, then
|
||||
/// it has to be taken from somewhere else during doc link resolution.
|
||||
pub item_id: Option<DefId>,
|
||||
pub doc: Symbol,
|
||||
pub kind: DocFragmentKind,
|
||||
pub indent: usize,
|
||||
|
@ -186,7 +188,7 @@ pub fn attrs_to_doc_fragments<'a>(
|
|||
) -> (Vec<DocFragment>, ast::AttrVec) {
|
||||
let mut doc_fragments = Vec::new();
|
||||
let mut other_attrs = ast::AttrVec::new();
|
||||
for (attr, parent_module) in attrs {
|
||||
for (attr, item_id) in attrs {
|
||||
if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() {
|
||||
let doc = beautify_doc_string(doc_str, comment_kind);
|
||||
let kind = if attr.is_doc_comment() {
|
||||
|
@ -194,7 +196,7 @@ pub fn attrs_to_doc_fragments<'a>(
|
|||
} else {
|
||||
DocFragmentKind::RawDoc
|
||||
};
|
||||
let fragment = DocFragment { span: attr.span, doc, kind, parent_module, indent: 0 };
|
||||
let fragment = DocFragment { span: attr.span, doc, kind, item_id, indent: 0 };
|
||||
doc_fragments.push(fragment);
|
||||
} else if !doc_only {
|
||||
other_attrs.push(attr.clone());
|
||||
|
@ -216,7 +218,7 @@ pub fn prepare_to_doc_link_resolution(
|
|||
) -> FxHashMap<Option<DefId>, String> {
|
||||
let mut res = FxHashMap::default();
|
||||
for fragment in doc_fragments {
|
||||
let out_str = res.entry(fragment.parent_module).or_default();
|
||||
let out_str = res.entry(fragment.item_id).or_default();
|
||||
add_doc_fragment(out_str, fragment);
|
||||
}
|
||||
res
|
||||
|
|
|
@ -36,15 +36,11 @@ use crate::formats::item_type::ItemType;
|
|||
///
|
||||
/// The returned value is `None` if the definition could not be inlined,
|
||||
/// and `Some` of a vector of items if it was successfully expanded.
|
||||
///
|
||||
/// `parent_module` refers to the parent of the *re-export*, not the original item.
|
||||
pub(crate) fn try_inline(
|
||||
cx: &mut DocContext<'_>,
|
||||
parent_module: DefId,
|
||||
import_def_id: Option<DefId>,
|
||||
res: Res,
|
||||
name: Symbol,
|
||||
attrs: Option<&[ast::Attribute]>,
|
||||
attrs: Option<(&[ast::Attribute], Option<DefId>)>,
|
||||
visited: &mut DefIdSet,
|
||||
) -> Option<Vec<clean::Item>> {
|
||||
let did = res.opt_def_id()?;
|
||||
|
@ -55,38 +51,17 @@ pub(crate) fn try_inline(
|
|||
|
||||
debug!("attrs={:?}", attrs);
|
||||
|
||||
let attrs_without_docs = attrs.map(|attrs| {
|
||||
attrs.into_iter().filter(|a| a.doc_str().is_none()).cloned().collect::<Vec<_>>()
|
||||
let attrs_without_docs = attrs.map(|(attrs, def_id)| {
|
||||
(attrs.into_iter().filter(|a| a.doc_str().is_none()).cloned().collect::<Vec<_>>(), def_id)
|
||||
});
|
||||
// We need this ugly code because:
|
||||
//
|
||||
// ```
|
||||
// attrs_without_docs.map(|a| a.as_slice())
|
||||
// ```
|
||||
//
|
||||
// will fail because it returns a temporary slice and:
|
||||
//
|
||||
// ```
|
||||
// attrs_without_docs.map(|s| {
|
||||
// vec = s.as_slice();
|
||||
// vec
|
||||
// })
|
||||
// ```
|
||||
//
|
||||
// will fail because we're moving an uninitialized variable into a closure.
|
||||
let vec;
|
||||
let attrs_without_docs = match attrs_without_docs {
|
||||
Some(s) => {
|
||||
vec = s;
|
||||
Some(vec.as_slice())
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let attrs_without_docs =
|
||||
attrs_without_docs.as_ref().map(|(attrs, def_id)| (&attrs[..], *def_id));
|
||||
|
||||
let import_def_id = attrs.and_then(|(_, def_id)| def_id);
|
||||
let kind = match res {
|
||||
Res::Def(DefKind::Trait, did) => {
|
||||
record_extern_fqn(cx, did, ItemType::Trait);
|
||||
build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret);
|
||||
build_impls(cx, did, attrs_without_docs, &mut ret);
|
||||
clean::TraitItem(Box::new(build_external_trait(cx, did)))
|
||||
}
|
||||
Res::Def(DefKind::Fn, did) => {
|
||||
|
@ -95,27 +70,27 @@ pub(crate) fn try_inline(
|
|||
}
|
||||
Res::Def(DefKind::Struct, did) => {
|
||||
record_extern_fqn(cx, did, ItemType::Struct);
|
||||
build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret);
|
||||
build_impls(cx, did, attrs_without_docs, &mut ret);
|
||||
clean::StructItem(build_struct(cx, did))
|
||||
}
|
||||
Res::Def(DefKind::Union, did) => {
|
||||
record_extern_fqn(cx, did, ItemType::Union);
|
||||
build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret);
|
||||
build_impls(cx, did, attrs_without_docs, &mut ret);
|
||||
clean::UnionItem(build_union(cx, did))
|
||||
}
|
||||
Res::Def(DefKind::TyAlias, did) => {
|
||||
record_extern_fqn(cx, did, ItemType::Typedef);
|
||||
build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret);
|
||||
build_impls(cx, did, attrs_without_docs, &mut ret);
|
||||
clean::TypedefItem(build_type_alias(cx, did))
|
||||
}
|
||||
Res::Def(DefKind::Enum, did) => {
|
||||
record_extern_fqn(cx, did, ItemType::Enum);
|
||||
build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret);
|
||||
build_impls(cx, did, attrs_without_docs, &mut ret);
|
||||
clean::EnumItem(build_enum(cx, did))
|
||||
}
|
||||
Res::Def(DefKind::ForeignTy, did) => {
|
||||
record_extern_fqn(cx, did, ItemType::ForeignType);
|
||||
build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret);
|
||||
build_impls(cx, did, attrs_without_docs, &mut ret);
|
||||
clean::ForeignTypeItem
|
||||
}
|
||||
// Never inline enum variants but leave them shown as re-exports.
|
||||
|
@ -149,7 +124,7 @@ pub(crate) fn try_inline(
|
|||
_ => return None,
|
||||
};
|
||||
|
||||
let (attrs, cfg) = merge_attrs(cx, Some(parent_module), load_attrs(cx, did), attrs);
|
||||
let (attrs, cfg) = merge_attrs(cx, load_attrs(cx, did), attrs);
|
||||
cx.inlined.insert(did.into());
|
||||
let mut item =
|
||||
clean::Item::from_def_id_and_attrs_and_parts(did, Some(name), kind, Box::new(attrs), cfg);
|
||||
|
@ -316,9 +291,8 @@ fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::Typedef>
|
|||
/// Builds all inherent implementations of an ADT (struct/union/enum) or Trait item/path/reexport.
|
||||
pub(crate) fn build_impls(
|
||||
cx: &mut DocContext<'_>,
|
||||
parent_module: Option<DefId>,
|
||||
did: DefId,
|
||||
attrs: Option<&[ast::Attribute]>,
|
||||
attrs: Option<(&[ast::Attribute], Option<DefId>)>,
|
||||
ret: &mut Vec<clean::Item>,
|
||||
) {
|
||||
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_inherent_impls");
|
||||
|
@ -326,7 +300,7 @@ pub(crate) fn build_impls(
|
|||
|
||||
// for each implementation of an item represented by `did`, build the clean::Item for that impl
|
||||
for &did in tcx.inherent_impls(did).iter() {
|
||||
build_impl(cx, parent_module, did, attrs, ret);
|
||||
build_impl(cx, did, attrs, ret);
|
||||
}
|
||||
|
||||
// This pretty much exists expressly for `dyn Error` traits that exist in the `alloc` crate.
|
||||
|
@ -340,28 +314,26 @@ pub(crate) fn build_impls(
|
|||
let type_ =
|
||||
if tcx.is_trait(did) { TraitSimplifiedType(did) } else { AdtSimplifiedType(did) };
|
||||
for &did in tcx.incoherent_impls(type_) {
|
||||
build_impl(cx, parent_module, did, attrs, ret);
|
||||
build_impl(cx, did, attrs, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `parent_module` refers to the parent of the re-export, not the original item
|
||||
pub(crate) fn merge_attrs(
|
||||
cx: &mut DocContext<'_>,
|
||||
parent_module: Option<DefId>,
|
||||
old_attrs: &[ast::Attribute],
|
||||
new_attrs: Option<&[ast::Attribute]>,
|
||||
new_attrs: Option<(&[ast::Attribute], Option<DefId>)>,
|
||||
) -> (clean::Attributes, Option<Arc<clean::cfg::Cfg>>) {
|
||||
// NOTE: If we have additional attributes (from a re-export),
|
||||
// always insert them first. This ensure that re-export
|
||||
// doc comments show up before the original doc comments
|
||||
// when we render them.
|
||||
if let Some(inner) = new_attrs {
|
||||
if let Some((inner, item_id)) = new_attrs {
|
||||
let mut both = inner.to_vec();
|
||||
both.extend_from_slice(old_attrs);
|
||||
(
|
||||
if let Some(new_id) = parent_module {
|
||||
Attributes::from_ast_with_additional(old_attrs, (inner, new_id))
|
||||
if let Some(item_id) = item_id {
|
||||
Attributes::from_ast_with_additional(old_attrs, (inner, item_id))
|
||||
} else {
|
||||
Attributes::from_ast(&both)
|
||||
},
|
||||
|
@ -375,9 +347,8 @@ pub(crate) fn merge_attrs(
|
|||
/// Inline an `impl`, inherent or of a trait. The `did` must be for an `impl`.
|
||||
pub(crate) fn build_impl(
|
||||
cx: &mut DocContext<'_>,
|
||||
parent_module: Option<DefId>,
|
||||
did: DefId,
|
||||
attrs: Option<&[ast::Attribute]>,
|
||||
attrs: Option<(&[ast::Attribute], Option<DefId>)>,
|
||||
ret: &mut Vec<clean::Item>,
|
||||
) {
|
||||
if !cx.inlined.insert(did.into()) {
|
||||
|
@ -539,7 +510,7 @@ pub(crate) fn build_impl(
|
|||
record_extern_trait(cx, did);
|
||||
}
|
||||
|
||||
let (merged_attrs, cfg) = merge_attrs(cx, parent_module, load_attrs(cx, did), attrs);
|
||||
let (merged_attrs, cfg) = merge_attrs(cx, load_attrs(cx, did), attrs);
|
||||
trace!("merged_attrs={:?}", merged_attrs);
|
||||
|
||||
trace!(
|
||||
|
@ -635,7 +606,7 @@ fn build_module_items(
|
|||
cfg: None,
|
||||
inline_stmt_id: None,
|
||||
});
|
||||
} else if let Some(i) = try_inline(cx, did, None, res, item.ident.name, None, visited) {
|
||||
} else if let Some(i) = try_inline(cx, res, item.ident.name, None, visited) {
|
||||
items.extend(i)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2388,12 +2388,12 @@ fn clean_maybe_renamed_item<'tcx>(
|
|||
target_attrs.extend_from_slice(inline::load_attrs(cx, def_id));
|
||||
}
|
||||
|
||||
let import_parent = import_id.map(|import_id| cx.tcx.local_parent(import_id).to_def_id());
|
||||
let (attrs, cfg) = merge_attrs(cx, import_parent, &target_attrs, Some(&import_attrs));
|
||||
let import_id = import_id.map(|def_id| def_id.to_def_id());
|
||||
let (attrs, cfg) = merge_attrs(cx, &target_attrs, Some((&import_attrs, import_id)));
|
||||
|
||||
let mut item =
|
||||
Item::from_def_id_and_attrs_and_parts(def_id, Some(name), kind, Box::new(attrs), cfg);
|
||||
item.inline_stmt_id = import_id.map(|def_id| def_id.to_def_id());
|
||||
item.inline_stmt_id = import_id;
|
||||
vec![item]
|
||||
})
|
||||
}
|
||||
|
@ -2478,18 +2478,12 @@ fn clean_extern_crate<'tcx>(
|
|||
|
||||
let krate_owner_def_id = krate.owner_id.to_def_id();
|
||||
if please_inline {
|
||||
let mut visited = DefIdSet::default();
|
||||
|
||||
let res = Res::Def(DefKind::Mod, crate_def_id);
|
||||
|
||||
if let Some(items) = inline::try_inline(
|
||||
cx,
|
||||
cx.tcx.parent_module(krate.hir_id()).to_def_id(),
|
||||
Some(krate_owner_def_id),
|
||||
res,
|
||||
Res::Def(DefKind::Mod, crate_def_id),
|
||||
name,
|
||||
Some(attrs),
|
||||
&mut visited,
|
||||
Some((attrs, Some(krate_owner_def_id))),
|
||||
&mut Default::default(),
|
||||
) {
|
||||
return items;
|
||||
}
|
||||
|
@ -2613,17 +2607,13 @@ fn clean_use_statement_inner<'tcx>(
|
|||
denied = true;
|
||||
}
|
||||
if !denied {
|
||||
let mut visited = DefIdSet::default();
|
||||
let import_def_id = import.owner_id.to_def_id();
|
||||
|
||||
if let Some(mut items) = inline::try_inline(
|
||||
cx,
|
||||
cx.tcx.parent_module(import.hir_id()).to_def_id(),
|
||||
Some(import_def_id),
|
||||
path.res,
|
||||
name,
|
||||
Some(attrs),
|
||||
&mut visited,
|
||||
Some((attrs, Some(import_def_id))),
|
||||
&mut Default::default(),
|
||||
) {
|
||||
items.push(Item::from_def_id_and_parts(
|
||||
import_def_id,
|
||||
|
|
|
@ -10,7 +10,7 @@ use rustc_span::symbol::Symbol;
|
|||
fn create_doc_fragment(s: &str) -> Vec<DocFragment> {
|
||||
vec![DocFragment {
|
||||
span: DUMMY_SP,
|
||||
parent_module: None,
|
||||
item_id: None,
|
||||
doc: Symbol::intern(s),
|
||||
kind: DocFragmentKind::SugaredDoc,
|
||||
indent: 0,
|
||||
|
|
|
@ -195,12 +195,12 @@ pub(crate) fn build_deref_target_impls(
|
|||
if let Some(prim) = target.primitive_type() {
|
||||
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_primitive_inherent_impls");
|
||||
for did in prim.impls(tcx).filter(|did| !did.is_local()) {
|
||||
inline::build_impl(cx, None, did, None, ret);
|
||||
inline::build_impl(cx, did, None, ret);
|
||||
}
|
||||
} else if let Type::Path { path } = target {
|
||||
let did = path.def_id();
|
||||
if !did.is_local() {
|
||||
inline::build_impls(cx, None, did, None, ret);
|
||||
inline::build_impls(cx, did, None, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ use std::mem;
|
|||
use std::ops::Range;
|
||||
|
||||
use crate::clean::{self, utils::find_nearest_parent_module};
|
||||
use crate::clean::{Crate, Item, ItemId, ItemLink, PrimitiveType};
|
||||
use crate::clean::{Crate, Item, ItemLink, PrimitiveType};
|
||||
use crate::core::DocContext;
|
||||
use crate::html::markdown::{markdown_links, MarkdownLink};
|
||||
use crate::lint::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS};
|
||||
|
@ -42,8 +42,7 @@ pub(crate) const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
|
|||
};
|
||||
|
||||
fn collect_intra_doc_links(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
|
||||
let mut collector =
|
||||
LinkCollector { cx, mod_ids: Vec::new(), visited_links: FxHashMap::default() };
|
||||
let mut collector = LinkCollector { cx, visited_links: FxHashMap::default() };
|
||||
collector.visit_crate(&krate);
|
||||
krate
|
||||
}
|
||||
|
@ -149,7 +148,7 @@ impl TryFrom<ResolveRes> for Res {
|
|||
#[derive(Debug)]
|
||||
struct UnresolvedPath<'a> {
|
||||
/// Item on which the link is resolved, used for resolving `Self`.
|
||||
item_id: ItemId,
|
||||
item_id: DefId,
|
||||
/// The scope the link was resolved in.
|
||||
module_id: DefId,
|
||||
/// If part of the link resolved, this has the `Res`.
|
||||
|
@ -225,7 +224,7 @@ impl UrlFragment {
|
|||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
struct ResolutionInfo {
|
||||
item_id: ItemId,
|
||||
item_id: DefId,
|
||||
module_id: DefId,
|
||||
dis: Option<Disambiguator>,
|
||||
path_str: Box<str>,
|
||||
|
@ -242,11 +241,6 @@ struct DiagnosticInfo<'a> {
|
|||
|
||||
struct LinkCollector<'a, 'tcx> {
|
||||
cx: &'a mut DocContext<'tcx>,
|
||||
/// A stack of modules used to decide what scope to resolve in.
|
||||
///
|
||||
/// The last module will be used if the parent scope of the current item is
|
||||
/// unknown.
|
||||
mod_ids: Vec<DefId>,
|
||||
/// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link.
|
||||
/// The link will be `None` if it could not be resolved (i.e. the error was cached).
|
||||
visited_links: FxHashMap<ResolutionInfo, Option<(Res, Option<UrlFragment>)>>,
|
||||
|
@ -262,7 +256,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||
fn variant_field<'path>(
|
||||
&self,
|
||||
path_str: &'path str,
|
||||
item_id: ItemId,
|
||||
item_id: DefId,
|
||||
module_id: DefId,
|
||||
) -> Result<(Res, DefId), UnresolvedPath<'path>> {
|
||||
let tcx = self.cx.tcx;
|
||||
|
@ -333,35 +327,33 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||
})
|
||||
}
|
||||
|
||||
fn resolve_self_ty(&self, path_str: &str, ns: Namespace, item_id: ItemId) -> Option<Res> {
|
||||
fn resolve_self_ty(&self, path_str: &str, ns: Namespace, item_id: DefId) -> Option<Res> {
|
||||
if ns != TypeNS || path_str != "Self" {
|
||||
return None;
|
||||
}
|
||||
|
||||
let tcx = self.cx.tcx;
|
||||
item_id
|
||||
.as_def_id()
|
||||
.map(|def_id| match tcx.def_kind(def_id) {
|
||||
def_kind @ (DefKind::AssocFn
|
||||
| DefKind::AssocConst
|
||||
| DefKind::AssocTy
|
||||
| DefKind::Variant
|
||||
| DefKind::Field) => {
|
||||
let parent_def_id = tcx.parent(def_id);
|
||||
if def_kind == DefKind::Field && tcx.def_kind(parent_def_id) == DefKind::Variant
|
||||
{
|
||||
tcx.parent(parent_def_id)
|
||||
} else {
|
||||
parent_def_id
|
||||
}
|
||||
let self_id = match tcx.def_kind(item_id) {
|
||||
def_kind @ (DefKind::AssocFn
|
||||
| DefKind::AssocConst
|
||||
| DefKind::AssocTy
|
||||
| DefKind::Variant
|
||||
| DefKind::Field) => {
|
||||
let parent_def_id = tcx.parent(item_id);
|
||||
if def_kind == DefKind::Field && tcx.def_kind(parent_def_id) == DefKind::Variant {
|
||||
tcx.parent(parent_def_id)
|
||||
} else {
|
||||
parent_def_id
|
||||
}
|
||||
_ => def_id,
|
||||
})
|
||||
.and_then(|self_id| match tcx.def_kind(self_id) {
|
||||
DefKind::Impl { .. } => self.def_id_to_res(self_id),
|
||||
DefKind::Use => None,
|
||||
def_kind => Some(Res::Def(def_kind, self_id)),
|
||||
})
|
||||
}
|
||||
_ => item_id,
|
||||
};
|
||||
|
||||
match tcx.def_kind(self_id) {
|
||||
DefKind::Impl { .. } => self.def_id_to_res(self_id),
|
||||
DefKind::Use => None,
|
||||
def_kind => Some(Res::Def(def_kind, self_id)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience wrapper around `doc_link_resolutions`.
|
||||
|
@ -373,7 +365,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||
&self,
|
||||
path_str: &str,
|
||||
ns: Namespace,
|
||||
item_id: ItemId,
|
||||
item_id: DefId,
|
||||
module_id: DefId,
|
||||
) -> Option<Res> {
|
||||
if let res @ Some(..) = self.resolve_self_ty(path_str, ns, item_id) {
|
||||
|
@ -400,7 +392,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||
&mut self,
|
||||
path_str: &'path str,
|
||||
ns: Namespace,
|
||||
item_id: ItemId,
|
||||
item_id: DefId,
|
||||
module_id: DefId,
|
||||
) -> Result<(Res, Option<DefId>), UnresolvedPath<'path>> {
|
||||
if let Some(res) = self.resolve_path(path_str, ns, item_id, module_id) {
|
||||
|
@ -779,48 +771,8 @@ fn is_derive_trait_collision<T>(ns: &PerNS<Result<(Res, T), ResolutionFailure<'_
|
|||
|
||||
impl<'a, 'tcx> DocVisitor for LinkCollector<'a, 'tcx> {
|
||||
fn visit_item(&mut self, item: &Item) {
|
||||
let parent_node =
|
||||
item.item_id.as_def_id().and_then(|did| find_nearest_parent_module(self.cx.tcx, did));
|
||||
if parent_node.is_some() {
|
||||
trace!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.item_id);
|
||||
}
|
||||
|
||||
let inner_docs = item.inner_docs(self.cx.tcx);
|
||||
|
||||
if item.is_mod() && inner_docs {
|
||||
self.mod_ids.push(item.item_id.expect_def_id());
|
||||
}
|
||||
|
||||
// We want to resolve in the lexical scope of the documentation.
|
||||
// In the presence of re-exports, this is not the same as the module of the item.
|
||||
// Rather than merging all documentation into one, resolve it one attribute at a time
|
||||
// so we know which module it came from.
|
||||
for (parent_module, doc) in prepare_to_doc_link_resolution(&item.attrs.doc_strings) {
|
||||
if !may_have_doc_links(&doc) {
|
||||
continue;
|
||||
}
|
||||
debug!("combined_docs={}", doc);
|
||||
// NOTE: if there are links that start in one crate and end in another, this will not resolve them.
|
||||
// This is a degenerate case and it's not supported by rustdoc.
|
||||
let parent_node = parent_module.or(parent_node);
|
||||
for md_link in preprocessed_markdown_links(&doc) {
|
||||
let link = self.resolve_link(item, &doc, parent_node, &md_link);
|
||||
if let Some(link) = link {
|
||||
self.cx.cache.intra_doc_links.entry(item.item_id).or_default().push(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if item.is_mod() {
|
||||
if !inner_docs {
|
||||
self.mod_ids.push(item.item_id.expect_def_id());
|
||||
}
|
||||
|
||||
self.visit_item_recur(item);
|
||||
self.mod_ids.pop();
|
||||
} else {
|
||||
self.visit_item_recur(item)
|
||||
}
|
||||
self.resolve_links(item);
|
||||
self.visit_item_recur(item)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -946,14 +898,41 @@ fn preprocessed_markdown_links(s: &str) -> Vec<PreprocessedMarkdownLink> {
|
|||
}
|
||||
|
||||
impl LinkCollector<'_, '_> {
|
||||
fn resolve_links(&mut self, item: &Item) {
|
||||
// We want to resolve in the lexical scope of the documentation.
|
||||
// In the presence of re-exports, this is not the same as the module of the item.
|
||||
// Rather than merging all documentation into one, resolve it one attribute at a time
|
||||
// so we know which module it came from.
|
||||
for (item_id, doc) in prepare_to_doc_link_resolution(&item.attrs.doc_strings) {
|
||||
if !may_have_doc_links(&doc) {
|
||||
continue;
|
||||
}
|
||||
debug!("combined_docs={}", doc);
|
||||
// NOTE: if there are links that start in one crate and end in another, this will not resolve them.
|
||||
// This is a degenerate case and it's not supported by rustdoc.
|
||||
let item_id = item_id.unwrap_or_else(|| item.item_id.expect_def_id());
|
||||
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(),
|
||||
};
|
||||
for md_link in preprocessed_markdown_links(&doc) {
|
||||
let link = self.resolve_link(item, item_id, module_id, &doc, &md_link);
|
||||
if let Some(link) = link {
|
||||
self.cx.cache.intra_doc_links.entry(item.item_id).or_default().push(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the entry point for resolving an intra-doc link.
|
||||
///
|
||||
/// FIXME(jynelson): this is way too many arguments
|
||||
fn resolve_link(
|
||||
&mut self,
|
||||
item: &Item,
|
||||
item_id: DefId,
|
||||
module_id: DefId,
|
||||
dox: &str,
|
||||
parent_node: Option<DefId>,
|
||||
link: &PreprocessedMarkdownLink,
|
||||
) -> Option<ItemLink> {
|
||||
let PreprocessedMarkdownLink(pp_link, ori_link) = link;
|
||||
|
@ -970,25 +949,9 @@ impl LinkCollector<'_, '_> {
|
|||
pp_link.as_ref().map_err(|err| err.report(self.cx, diag_info.clone())).ok()?;
|
||||
let disambiguator = *disambiguator;
|
||||
|
||||
// In order to correctly resolve intra-doc links we need to
|
||||
// pick a base AST node to work from. If the documentation for
|
||||
// this module came from an inner comment (//!) then we anchor
|
||||
// our name resolution *inside* the module. If, on the other
|
||||
// hand it was an outer comment (///) then we anchor the name
|
||||
// resolution in the parent module on the basis that the names
|
||||
// used are more likely to be intended to be parent names. For
|
||||
// this, we set base_node to None for inner comments since
|
||||
// we've already pushed this node onto the resolution stack but
|
||||
// for outer comments we explicitly try and resolve against the
|
||||
// parent_node first.
|
||||
let inner_docs = item.inner_docs(self.cx.tcx);
|
||||
let base_node =
|
||||
if item.is_mod() && inner_docs { self.mod_ids.last().copied() } else { parent_node };
|
||||
let module_id = base_node.expect("doc link without parent module");
|
||||
|
||||
let (mut res, fragment) = self.resolve_with_disambiguator_cached(
|
||||
ResolutionInfo {
|
||||
item_id: item.item_id,
|
||||
item_id,
|
||||
module_id,
|
||||
dis: disambiguator,
|
||||
path_str: path_str.clone(),
|
||||
|
@ -1229,11 +1192,11 @@ impl LinkCollector<'_, '_> {
|
|||
let disambiguator = key.dis;
|
||||
let path_str = &key.path_str;
|
||||
let item_id = key.item_id;
|
||||
let base_node = key.module_id;
|
||||
let module_id = key.module_id;
|
||||
|
||||
match disambiguator.map(Disambiguator::ns) {
|
||||
Some(expected_ns) => {
|
||||
match self.resolve(path_str, expected_ns, item_id, base_node) {
|
||||
match self.resolve(path_str, expected_ns, item_id, module_id) {
|
||||
Ok(res) => Some(res),
|
||||
Err(err) => {
|
||||
// We only looked in one namespace. Try to give a better error if possible.
|
||||
|
@ -1243,7 +1206,7 @@ impl LinkCollector<'_, '_> {
|
|||
for other_ns in [TypeNS, ValueNS, MacroNS] {
|
||||
if other_ns != expected_ns {
|
||||
if let Ok(res) =
|
||||
self.resolve(path_str, other_ns, item_id, base_node)
|
||||
self.resolve(path_str, other_ns, item_id, module_id)
|
||||
{
|
||||
err = ResolutionFailure::WrongNamespace {
|
||||
res: full_res(self.cx.tcx, res),
|
||||
|
@ -1260,7 +1223,7 @@ impl LinkCollector<'_, '_> {
|
|||
None => {
|
||||
// Try everything!
|
||||
let mut candidate = |ns| {
|
||||
self.resolve(path_str, ns, item_id, base_node)
|
||||
self.resolve(path_str, ns, item_id, module_id)
|
||||
.map_err(ResolutionFailure::NotResolved)
|
||||
};
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
|
|||
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_extern_trait_impls");
|
||||
for &cnum in cx.tcx.crates(()) {
|
||||
for &impl_def_id in cx.tcx.trait_impls_in_crate(cnum) {
|
||||
inline::build_impl(cx, None, impl_def_id, None, &mut new_items_external);
|
||||
inline::build_impl(cx, impl_def_id, None, &mut new_items_external);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
|
|||
);
|
||||
parent = cx.tcx.opt_parent(did);
|
||||
}
|
||||
inline::build_impl(cx, None, impl_def_id, Some(&attr_buf), &mut new_items_local);
|
||||
inline::build_impl(cx, impl_def_id, Some((&attr_buf, None)), &mut new_items_local);
|
||||
attr_buf.clear();
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
|
|||
for def_id in PrimitiveType::all_impls(cx.tcx) {
|
||||
// Try to inline primitive impls from other crates.
|
||||
if !def_id.is_local() {
|
||||
inline::build_impl(cx, None, def_id, None, &mut new_items_external);
|
||||
inline::build_impl(cx, def_id, None, &mut new_items_external);
|
||||
}
|
||||
}
|
||||
for (prim, did) in PrimitiveType::primitive_locations(cx.tcx) {
|
||||
|
|
|
@ -57,7 +57,8 @@ impl<'a, 'tcx> CfgPropagator<'a, 'tcx> {
|
|||
next_def_id = parent_def_id;
|
||||
}
|
||||
|
||||
let (_, cfg) = merge_attrs(self.cx, None, item.attrs.other_attrs.as_slice(), Some(&attrs));
|
||||
let (_, cfg) =
|
||||
merge_attrs(self.cx, item.attrs.other_attrs.as_slice(), Some((&attrs, None)));
|
||||
item.cfg = cfg;
|
||||
}
|
||||
}
|
||||
|
|
1
tests/rustdoc-ui/intra-doc/auxiliary/inner-crate-doc.rs
Normal file
1
tests/rustdoc-ui/intra-doc/auxiliary/inner-crate-doc.rs
Normal file
|
@ -0,0 +1 @@
|
|||
//! Inner doc comment
|
10
tests/rustdoc-ui/intra-doc/import-inline-merge-module.rs
Normal file
10
tests/rustdoc-ui/intra-doc/import-inline-merge-module.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Test for issue #108501.
|
||||
// Module parent scope doesn't hijack import's parent scope for the import's doc links.
|
||||
|
||||
// check-pass
|
||||
// aux-build: inner-crate-doc.rs
|
||||
// compile-flags: --extern inner_crate_doc --edition 2018
|
||||
|
||||
/// Import doc comment [inner_crate_doc]
|
||||
#[doc(inline)]
|
||||
pub use inner_crate_doc;
|
Loading…
Add table
Reference in a new issue