Skip to content

Commit

Permalink
Use doc comments from 'pub use' statements
Browse files Browse the repository at this point in the history
Split off from rust-lang#62855

Currently, rustdoc ignores any doc comments found on 'pub use'
statements. As described in issue rust-lang#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.
  • Loading branch information
Aaron1011 committed Jul 27, 2019
1 parent 0e9b465 commit 7ee9b7a
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 24 deletions.
58 changes: 43 additions & 15 deletions src/librustdoc/clean/inline.rs
Expand Up @@ -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
Expand All @@ -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() {
Expand All @@ -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) => {
Expand All @@ -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.
Expand Down Expand Up @@ -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),
Expand All @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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)
}
}
Expand Down
20 changes: 14 additions & 6 deletions src/librustdoc/clean/mod.rs
Expand Up @@ -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,
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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);
}
}
}
Expand All @@ -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;
}
}
Expand Down Expand Up @@ -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;
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/librustdoc/passes/collect_trait_impls.rs
Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
}

Expand Down
9 changes: 9 additions & 0 deletions 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;
4 changes: 4 additions & 0 deletions src/test/rustdoc/inline_cross/auxiliary/add-docs.rs
@@ -0,0 +1,4 @@
#![crate_name = "inner"]

/// Doc comment from definition
pub struct MyStruct;

0 comments on commit 7ee9b7a

Please sign in to comment.