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.
This commit is contained in:
parent
0e9b465d72
commit
7ee9b7a410
@ -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<Attrs<'_>>,
|
||||
visited: &mut FxHashSet<DefId>
|
||||
) -> Option<Vec<clean::Item>> {
|
||||
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<De
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_attrs(cx: &DocContext<'_>, 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<clean::Item> {
|
||||
pub fn build_impls(cx: &DocContext<'_>, did: DefId, attrs: Option<Attrs<'_>>) -> Vec<clean::Item> {
|
||||
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<clean::Item>) {
|
||||
fn merge_attrs(cx: &DocContext<'_>, attrs: Attrs<'_>, other_attrs: Option<Attrs<'_>>
|
||||
) -> clean::Attributes {
|
||||
let mut merged_attrs: Vec<ast::Attribute> = 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<Attrs<'_>>,
|
||||
ret: &mut Vec<clean::Item>
|
||||
) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -2429,7 +2429,7 @@ impl Clean<Item> 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<Item> 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<Vec<Item>> 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<Vec<Item>> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
9
src/test/rustdoc/inline_cross/add-docs.rs
Normal file
9
src/test/rustdoc/inline_cross/add-docs.rs
Normal file
@ -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;
|
4
src/test/rustdoc/inline_cross/auxiliary/add-docs.rs
Normal file
4
src/test/rustdoc/inline_cross/auxiliary/add-docs.rs
Normal file
@ -0,0 +1,4 @@
|
||||
#![crate_name = "inner"]
|
||||
|
||||
/// Doc comment from definition
|
||||
pub struct MyStruct;
|
Loading…
Reference in New Issue
Block a user