From 7ee9b7a410f955f220e9bbe6ce55c642bafec17b Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sat, 27 Jul 2019 14:18:50 -0400 Subject: [PATCH] Use doc comments from 'pub use' statements Split off from #62855 Currently, rustdoc ignores any doc comments found on 'pub use' statements. As described in issue #58700, this makes it impossible to properly document procedural macros. Any doc comments must be written on the procedural macro definition, which must occur in a dedicated proc-macro crate. This means that any doc comments or doc tests cannot reference items defined in re-exporting crate, despite the fact that such items may be required to use the procedural macro. To solve this issue, this commit allows doc comments to be written on 'pub use' statements. For consistency, this applies to *all* 'pub use' statements, not just those importing procedural macros. When inlining documentation, documentation on 'pub use' statements will be prepended to the documentation of the inlined item. For example, the following items: ```rust mod other_mod { /// Doc comment from definition pub struct MyStruct; } /// Doc comment from 'pub use' /// pub use other_mod::MyStruct; ``` will caues the documentation for the re-export of 'MyStruct' to be rendered as: ``` Doc comment from 'pub use' Doc comment from definition ``` Note the empty line in the 'pub use' doc comments - because doc comments are concatenated as-is, this ensure that the doc comments on the definition start on a new line. --- src/librustdoc/clean/inline.rs | 58 ++++++++++++++----- src/librustdoc/clean/mod.rs | 20 +++++-- src/librustdoc/passes/collect_trait_impls.rs | 6 +- src/test/rustdoc/inline_cross/add-docs.rs | 9 +++ .../inline_cross/auxiliary/add-docs.rs | 4 ++ 5 files changed, 73 insertions(+), 24 deletions(-) create mode 100644 src/test/rustdoc/inline_cross/add-docs.rs create mode 100644 src/test/rustdoc/inline_cross/auxiliary/add-docs.rs diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 9259b3b5d3abb..8463fbfbd200b 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -24,6 +24,8 @@ use crate::clean::{ use super::Clean; +type Attrs<'hir> = rustc::ty::Attributes<'hir>; + /// Attempt to inline a definition into this AST. /// /// This function will fetch the definition specified, and if it is @@ -40,6 +42,7 @@ pub fn try_inline( cx: &DocContext<'_>, res: Res, name: ast::Name, + attrs: Option>, visited: &mut FxHashSet ) -> Option> { let did = if let Some(did) = res.opt_def_id() { @@ -49,10 +52,13 @@ pub fn try_inline( }; if did.is_local() { return None } let mut ret = Vec::new(); + + let attrs_clone = attrs.clone(); + let inner = match res { Res::Def(DefKind::Trait, did) => { record_extern_fqn(cx, did, clean::TypeKind::Trait); - ret.extend(build_impls(cx, did)); + ret.extend(build_impls(cx, did, attrs)); clean::TraitItem(build_external_trait(cx, did)) } Res::Def(DefKind::Fn, did) => { @@ -61,27 +67,27 @@ pub fn try_inline( } Res::Def(DefKind::Struct, did) => { record_extern_fqn(cx, did, clean::TypeKind::Struct); - ret.extend(build_impls(cx, did)); + ret.extend(build_impls(cx, did, attrs)); clean::StructItem(build_struct(cx, did)) } Res::Def(DefKind::Union, did) => { record_extern_fqn(cx, did, clean::TypeKind::Union); - ret.extend(build_impls(cx, did)); + ret.extend(build_impls(cx, did, attrs)); clean::UnionItem(build_union(cx, did)) } Res::Def(DefKind::TyAlias, did) => { record_extern_fqn(cx, did, clean::TypeKind::Typedef); - ret.extend(build_impls(cx, did)); + ret.extend(build_impls(cx, did, attrs)); clean::TypedefItem(build_type_alias(cx, did), false) } Res::Def(DefKind::Enum, did) => { record_extern_fqn(cx, did, clean::TypeKind::Enum); - ret.extend(build_impls(cx, did)); + ret.extend(build_impls(cx, did, attrs)); clean::EnumItem(build_enum(cx, did)) } Res::Def(DefKind::ForeignTy, did) => { record_extern_fqn(cx, did, clean::TypeKind::Foreign); - ret.extend(build_impls(cx, did)); + ret.extend(build_impls(cx, did, attrs)); clean::ForeignTypeItem } // Never inline enum variants but leave them shown as re-exports. @@ -113,11 +119,15 @@ pub fn try_inline( } _ => return None, }; + + let target_attrs = load_attrs(cx, did); + let attrs = merge_attrs(cx, target_attrs, attrs_clone); + cx.renderinfo.borrow_mut().inlined.insert(did); ret.push(clean::Item { source: cx.tcx.def_span(did).clean(cx), name: Some(name.clean(cx)), - attrs: load_attrs(cx, did), + attrs, inner, visibility: Some(clean::Public), stability: cx.tcx.lookup_stability(did).clean(cx), @@ -144,8 +154,8 @@ pub fn try_inline_glob(cx: &DocContext<'_>, res: Res, visited: &mut FxHashSet, did: DefId) -> clean::Attributes { - cx.tcx.get_attrs(did).clean(cx) +pub fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> { + cx.tcx.get_attrs(did) } /// Record an external fully qualified name in the external_paths cache. @@ -187,7 +197,7 @@ pub fn build_external_trait(cx: &DocContext<'_>, did: DefId) -> clean::Trait { let generics = (cx.tcx.generics_of(did), &predicates).clean(cx); let generics = filter_non_trait_generics(did, generics); let (generics, supertrait_bounds) = separate_supertrait_bounds(generics); - let is_spotlight = load_attrs(cx, did).has_doc_flag(sym::spotlight); + let is_spotlight = load_attrs(cx, did).clean(cx).has_doc_flag(sym::spotlight); let is_auto = cx.tcx.trait_is_auto(did); clean::Trait { auto: auto_trait, @@ -274,23 +284,41 @@ fn build_type_alias(cx: &DocContext<'_>, did: DefId) -> clean::Typedef { } } -pub fn build_impls(cx: &DocContext<'_>, did: DefId) -> Vec { +pub fn build_impls(cx: &DocContext<'_>, did: DefId, attrs: Option>) -> Vec { let tcx = cx.tcx; let mut impls = Vec::new(); for &did in tcx.inherent_impls(did).iter() { - build_impl(cx, did, &mut impls); + build_impl(cx, did, attrs.clone(), &mut impls); } impls } -pub fn build_impl(cx: &DocContext<'_>, did: DefId, ret: &mut Vec) { +fn merge_attrs(cx: &DocContext<'_>, attrs: Attrs<'_>, other_attrs: Option> +) -> clean::Attributes { + let mut merged_attrs: Vec = Vec::with_capacity(attrs.len()); + // 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(a) = other_attrs { + merged_attrs.extend(a.iter().cloned()); + } + merged_attrs.extend(attrs.to_vec()); + merged_attrs.clean(cx) +} + +pub fn build_impl(cx: &DocContext<'_>, did: DefId, attrs: Option>, + ret: &mut Vec +) { if !cx.renderinfo.borrow_mut().inlined.insert(did) { return } - let attrs = load_attrs(cx, did); + let attrs = merge_attrs(cx, load_attrs(cx, did), attrs); + + let tcx = cx.tcx; let associated_trait = tcx.impl_trait_ref(did); @@ -416,7 +444,7 @@ fn build_module( let def_id = item.res.def_id(); if item.vis == ty::Visibility::Public { if did == def_id || !visited.insert(def_id) { continue } - if let Some(i) = try_inline(cx, item.res, item.ident.name, visited) { + if let Some(i) = try_inline(cx, item.res, item.ident.name, None, visited) { items.extend(i) } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 30419d3d3c650..d0b9e9799bea3 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2429,7 +2429,7 @@ impl Clean for ty::AssocItem { stability: get_stability(cx, self.def_id), deprecation: get_deprecation(cx, self.def_id), def_id: self.def_id, - attrs: inline::load_attrs(cx, self.def_id), + attrs: inline::load_attrs(cx, self.def_id).clean(cx), source: cx.tcx.def_span(self.def_id).clean(cx), inner, } @@ -3372,7 +3372,7 @@ impl Clean for ty::VariantDef { }; Item { name: Some(self.ident.clean(cx)), - attrs: inline::load_attrs(cx, self.def_id), + attrs: inline::load_attrs(cx, self.def_id).clean(cx), source: cx.tcx.def_span(self.def_id).clean(cx), visibility: Some(Inherited), def_id: self.def_id, @@ -3856,7 +3856,7 @@ fn build_deref_target_impls(cx: &DocContext<'_>, let primitive = match *target { ResolvedPath { did, .. } if did.is_local() => continue, ResolvedPath { did, .. } => { - ret.extend(inline::build_impls(cx, did)); + ret.extend(inline::build_impls(cx, did, None)); continue } _ => match target.primitive_type() { @@ -3894,7 +3894,7 @@ fn build_deref_target_impls(cx: &DocContext<'_>, }; if let Some(did) = did { if !did.is_local() { - inline::build_impl(cx, did, ret); + inline::build_impl(cx, did, None, ret); } } } @@ -3921,7 +3921,11 @@ impl Clean> for doctree::ExternCrate<'_> { }, ); - if let Some(items) = inline::try_inline(cx, res, self.name, &mut visited) { + if let Some(items) = inline::try_inline( + cx, res, self.name, + Some(rustc::ty::Attributes::Borrowed(self.attrs)), + &mut visited + ) { return items; } } @@ -3981,7 +3985,11 @@ impl Clean> for doctree::Import<'_> { } if !denied { let mut visited = FxHashSet::default(); - if let Some(items) = inline::try_inline(cx, path.res, name, &mut visited) { + if let Some(items) = inline::try_inline( + cx, path.res, name, + Some(rustc::ty::Attributes::Borrowed(self.attrs)), + &mut visited + ) { return items; } } diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index a2a6b1efe820c..cd488b9df78d2 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -30,7 +30,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { for &cnum in cx.tcx.crates().iter() { for &did in cx.tcx.all_trait_implementations(cnum).iter() { - inline::build_impl(cx, did, &mut new_items); + inline::build_impl(cx, did, None, &mut new_items); } } @@ -66,7 +66,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { for def_id in primitive_impls.iter().filter_map(|&def_id| def_id) { if !def_id.is_local() { - inline::build_impl(cx, def_id, &mut new_items); + inline::build_impl(cx, def_id, None, &mut new_items); // FIXME(eddyb) is this `doc(hidden)` check needed? if !cx.tcx.get_attrs(def_id).lists(sym::doc).has_word(sym::hidden) { @@ -119,7 +119,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { for &trait_did in cx.all_traits.iter() { for &impl_node in cx.tcx.hir().trait_impls(trait_did) { let impl_did = cx.tcx.hir().local_def_id(impl_node); - inline::build_impl(cx, impl_did, &mut new_items); + inline::build_impl(cx, impl_did, None, &mut new_items); } } diff --git a/src/test/rustdoc/inline_cross/add-docs.rs b/src/test/rustdoc/inline_cross/add-docs.rs new file mode 100644 index 0000000000000..1af5e8f03b44e --- /dev/null +++ b/src/test/rustdoc/inline_cross/add-docs.rs @@ -0,0 +1,9 @@ +// aux-build:add-docs.rs + +extern crate inner; + + +// @has add_docs/struct.MyStruct.html +// @has add_docs/struct.MyStruct.html "Doc comment from 'pub use', Doc comment from definition" +/// Doc comment from 'pub use', +pub use inner::MyStruct; diff --git a/src/test/rustdoc/inline_cross/auxiliary/add-docs.rs b/src/test/rustdoc/inline_cross/auxiliary/add-docs.rs new file mode 100644 index 0000000000000..85efa508f30b5 --- /dev/null +++ b/src/test/rustdoc/inline_cross/auxiliary/add-docs.rs @@ -0,0 +1,4 @@ +#![crate_name = "inner"] + +/// Doc comment from definition +pub struct MyStruct;