diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 9d84089eb405c..c6291587c1b84 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -29,6 +29,7 @@ use rustc_span::{self, ExpnKind, Pos}; use rustc_typeck::hir_ty_to_ty; use std::collections::hash_map::Entry; +use std::collections::BTreeMap; use std::default::Default; use std::hash::Hash; use std::rc::Rc; @@ -60,6 +61,12 @@ impl, U> Clean> for [T] { } } +impl, U> Clean> for BTreeMap { + fn clean(&self, cx: &DocContext<'_>) -> Vec { + self.values().map(|x| x.clean(cx)).collect() + } +} + impl, U, V: Idx> Clean> for IndexVec { fn clean(&self, cx: &DocContext<'_>) -> IndexVec { self.iter().map(|x| x.clean(cx)).collect() @@ -221,51 +228,161 @@ impl Clean for CrateNum { } } -impl Clean for doctree::Module<'_> { +enum MaybeInlined { + NotInlined(ItemKind), + InlinedWithoutOriginal(Vec), + InlinedWithOriginal(Vec, ItemKind), +} + +impl Clean> for hir::Item<'_> { + fn clean(&self, cx: &DocContext<'_>) -> Vec { + use hir::ItemKind; + use MaybeInlined::NotInlined; + + let def_id = cx.tcx.hir().local_def_id(self.hir_id).to_def_id(); + let name = cx.tcx.hir().name(self.hir_id); + let maybe_inlined = match self.kind { + ItemKind::ExternCrate(renamed) => clean_extern_crate(self, renamed, cx), + ItemKind::Use(path, kind) => clean_import(self, path, kind, cx), + ItemKind::Static(ty, mutability, body_id) => NotInlined(StaticItem(Static { + type_: ty.clean(cx), + mutability, + expr: print_const_expr(cx, body_id), + })), + ItemKind::Const(ty, body_id) => NotInlined(ConstantItem(Constant { + type_: ty.clean(cx), + expr: print_const_expr(cx, body_id), + value: print_evaluated_const(cx, def_id), + is_literal: is_literal_expr(cx, body_id.hir_id), + })), + ItemKind::Fn(ref sig, ref generics, body) => { + NotInlined(clean_function(self, sig, &generics, body, cx)) + } + // TODO: this should also take the span into account (inner or outer) + ItemKind::Mod(ref mod_) => NotInlined(mod_.clean(cx)), + // `extern "C" { ... }` + ItemKind::ForeignMod(ref mod_) => MaybeInlined::InlinedWithoutOriginal(mod_.clean(cx)), + ItemKind::GlobalAsm(..) => MaybeInlined::InlinedWithoutOriginal(vec![]), // not handled + ItemKind::TyAlias(ty, ref generics) => { + let rustdoc_ty = ty.clean(cx); + let item_type = rustdoc_ty.def_id().and_then(|did| inline::build_ty(cx, did)); + NotInlined(TypedefItem( + Typedef { type_: rustdoc_ty, generics: generics.clean(cx), item_type }, + false, + )) + } + ItemKind::OpaqueTy(ref ty) => NotInlined(OpaqueTyItem(OpaqueTy { + bounds: ty.bounds.clean(cx), + generics: ty.generics.clean(cx), + })), + ItemKind::Enum(ref def, ref generics) => NotInlined(EnumItem(Enum { + variants: def.variants.iter().map(|v| v.clean(cx)).collect(), + generics: generics.clean(cx), + variants_stripped: false, + })), + ItemKind::Struct(ref variant_data, ref generics) => NotInlined(StructItem(Struct { + struct_type: doctree::struct_type_from_def(&variant_data), + generics: generics.clean(cx), + fields: variant_data.fields().clean(cx), + fields_stripped: false, + })), + ItemKind::Union(ref variant_data, ref generics) => NotInlined(UnionItem(Union { + struct_type: doctree::struct_type_from_def(&variant_data), + generics: generics.clean(cx), + fields: variant_data.fields().clean(cx), + fields_stripped: false, + })), + ItemKind::Trait(is_auto, unsafety, ref generics, bounds, item_ids) => { + let attrs = self.attrs.clean(cx); + let is_spotlight = attrs.has_doc_flag(sym::spotlight); + NotInlined(TraitItem(Trait { + unsafety, + items: item_ids + .iter() + .map(|ti| cx.tcx.hir().trait_item(ti.id).clean(cx)) + .collect(), + generics: generics.clean(cx), + bounds: bounds.clean(cx), + is_spotlight, + is_auto: is_auto.clean(cx), + })) + } + ItemKind::TraitAlias(ref generics, bounds) => NotInlined(TraitAliasItem(TraitAlias { + generics: generics.clean(cx), + bounds: bounds.clean(cx), + })), + ItemKind::Impl { .. } => return clean_impl(self, cx), + }; + + let build_item = |kind| Item { + def_id, + kind, + name: Some(name.to_string()), + source: cx.tcx.def_span(def_id).clean(cx), + attrs: self.attrs.clean(cx), // TODO: should this use tcx.attrs instead? + visibility: self.vis.clean(cx), // TODO: should this use tcx.visibility instead? + stability: cx.tcx.lookup_stability(def_id).copied(), + deprecation: cx.tcx.lookup_deprecation(def_id).clean(cx), + }; + + match maybe_inlined { + MaybeInlined::NotInlined(inner) => vec![build_item(inner)], + MaybeInlined::InlinedWithoutOriginal(items) => items, + MaybeInlined::InlinedWithOriginal(mut items, inner) => { + let current = build_item(inner); + items.push(current); + items + } + } + } +} + +impl Clean for hir::Crate<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { - // maintain a stack of mod ids, for doc comment path resolution - // but we also need to resolve the module's own docs based on whether its docs were written - // inside or outside the module, so check for that - let attrs = self.attrs.clean(cx); - - let mut items: Vec = vec![]; - items.extend(self.extern_crates.iter().flat_map(|x| x.clean(cx))); - items.extend(self.imports.iter().flat_map(|x| x.clean(cx))); - items.extend(self.structs.iter().map(|x| x.clean(cx))); - items.extend(self.unions.iter().map(|x| x.clean(cx))); - items.extend(self.enums.iter().map(|x| x.clean(cx))); - items.extend(self.fns.iter().map(|x| x.clean(cx))); - items.extend(self.foreigns.iter().map(|x| x.clean(cx))); - items.extend(self.mods.iter().map(|x| x.clean(cx))); - items.extend(self.typedefs.iter().map(|x| x.clean(cx))); - items.extend(self.opaque_tys.iter().map(|x| x.clean(cx))); - items.extend(self.statics.iter().map(|x| x.clean(cx))); - items.extend(self.constants.iter().map(|x| x.clean(cx))); - items.extend(self.traits.iter().map(|x| x.clean(cx))); - items.extend(self.impls.iter().flat_map(|x| x.clean(cx))); - items.extend(self.macros.iter().map(|x| x.clean(cx))); - items.extend(self.proc_macros.iter().map(|x| x.clean(cx))); - items.extend(self.trait_aliases.iter().map(|x| x.clean(cx))); + let attrs = self.item.attrs.clean(cx); + + // Get _all_ the items! + let items = self.items.clean(cx).into_iter().flatten(); + let items = items.chain(self.item.module.item_ids.clean(cx).into_iter().flatten()); + let items = items.chain(self.exported_macros.clean(cx)); + let items = items.chain(self.trait_items.clean(cx)); + let items = items.chain(self.impl_items.clean(cx)); + // NOTE: bodies intentionally skipped + + let items = items.chain( + self.trait_impls + .iter() + .flat_map(|(_trait, impls)| { + impls.into_iter().map(|&impl_| cx.tcx.hir().item(impl_).clean(cx)) + }) + .flatten(), + ); + let items = items.chain(self.proc_macros.iter().map(|hir_id| { + let _def_id = hir_id.owner.local_def_index; + // TODO: look how `rustc_metadata::rmeta::encoder` does this + unimplemented!() + })); // determine if we should display the inner contents or // the outer `mod` item for the source code. + // TODO: for the crate root, I think this should always be `self.item.span`? let span = { let sm = cx.sess().source_map(); - let outer = sm.lookup_char_pos(self.where_outer.lo()); - let inner = sm.lookup_char_pos(self.where_inner.lo()); + let outer = sm.lookup_char_pos(self.item.span.lo()); + let inner = sm.lookup_char_pos(self.item.module.inner.lo()); if outer.file.start_pos == inner.file.start_pos { // mod foo { ... } - self.where_outer + self.item.span } else { // mod foo; (and a separate SourceFile for the contents) - self.where_inner + self.item.module.inner } }; let what_rustc_thinks = Item::from_hir_id_and_parts( - self.id, - self.name, - ModuleItem(Module { is_crate: self.is_crate, items }), + hir::CRATE_HIR_ID, + Some(cx.tcx.crate_name), + ModuleItem(Module { is_crate: true, items: items.collect() }), cx, ); Item { @@ -277,6 +394,27 @@ impl Clean for doctree::Module<'_> { } } +impl Clean for hir::Mod<'_> { + fn clean(&self, cx: &DocContext<'_>) -> ItemKind { + ModuleItem(Module { + is_crate: false, + items: self.item_ids.clean(cx).into_iter().flatten().collect(), + }) + } +} + +impl Clean> for hir::ForeignMod<'_> { + fn clean(&self, cx: &DocContext<'_>) -> Vec { + self.items.iter().map(|x| x.clean(cx)).collect() + } +} + +impl Clean> for hir::ItemId { + fn clean(&self, cx: &DocContext<'_>) -> Vec { + cx.tcx.hir().item(self.id).clean(cx) + } +} + impl Clean for [ast::Attribute] { fn clean(&self, cx: &DocContext<'_>) -> Attributes { Attributes::from_ast(cx.sess().diagnostic(), self, None) @@ -718,7 +856,6 @@ impl Clean for hir::Generics<'_> { impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx>) { fn clean(&self, cx: &DocContext<'_>) -> Generics { use self::WherePredicate as WP; - use std::collections::BTreeMap; let (gens, preds) = *self; @@ -888,32 +1025,32 @@ impl<'a> Clean for (&'a hir::FnSig<'a>, &'a hir::Generics<'a>, hir::Bo } } -impl Clean for doctree::Function<'_> { - fn clean(&self, cx: &DocContext<'_>) -> Item { - let (generics, decl) = - enter_impl_trait(cx, || (self.generics.clean(cx), (self.decl, self.body).clean(cx))); - - let did = cx.tcx.hir().local_def_id(self.id).to_def_id(); - let constness = if is_const_fn(cx.tcx, did) && !is_unstable_const_fn(cx.tcx, did).is_some() - { - hir::Constness::Const - } else { - hir::Constness::NotConst - }; - let (all_types, ret_types) = get_all_types(&generics, &decl, cx); - Item::from_def_id_and_parts( - did, - Some(self.name), - FunctionItem(Function { - decl, - generics, - header: hir::FnHeader { constness, ..self.header }, - all_types, - ret_types, - }), - cx, - ) - } +fn clean_function( + item: &hir::Item<'_>, + sig: &hir::FnSig<'_>, + generics: &hir::Generics<'_>, + body: hir::BodyId, + cx: &DocContext<'_>, +) -> ItemKind { + let (generics, decl) = + enter_impl_trait(cx, || (generics.clean(cx), (sig.decl, body).clean(cx))); + + let did = cx.tcx.hir().local_def_id(item.hir_id); + let constness = if is_const_fn(cx.tcx, did.to_def_id()) + && !is_unstable_const_fn(cx.tcx, did.to_def_id()).is_some() + { + hir::Constness::Const + } else { + hir::Constness::NotConst + }; + let (all_types, ret_types) = get_all_types(&generics, &decl, cx); + FunctionItem(Function { + decl, + generics, + header: hir::FnHeader { constness, ..sig.header }, + all_types, + ret_types, + }) } impl<'a> Clean for (&'a [hir::Ty<'a>], &'a [Ident]) { @@ -1000,40 +1137,6 @@ impl Clean for hir::FnRetTy<'_> { } } -impl Clean for doctree::Trait<'_> { - fn clean(&self, cx: &DocContext<'_>) -> Item { - let attrs = self.attrs.clean(cx); - let is_spotlight = attrs.has_doc_flag(sym::spotlight); - Item::from_hir_id_and_parts( - self.id, - Some(self.name), - TraitItem(Trait { - unsafety: self.unsafety, - items: self.items.iter().map(|ti| ti.clean(cx)).collect(), - generics: self.generics.clean(cx), - bounds: self.bounds.clean(cx), - is_spotlight, - is_auto: self.is_auto.clean(cx), - }), - cx, - ) - } -} - -impl Clean for doctree::TraitAlias<'_> { - fn clean(&self, cx: &DocContext<'_>) -> Item { - Item::from_hir_id_and_parts( - self.id, - Some(self.name), - TraitAliasItem(TraitAlias { - generics: self.generics.clean(cx), - bounds: self.bounds.clean(cx), - }), - cx, - ) - } -} - impl Clean for hir::IsAuto { fn clean(&self, _: &DocContext<'_>) -> bool { match *self { @@ -1777,38 +1880,6 @@ impl Clean for ty::Visibility { } } -impl Clean for doctree::Struct<'_> { - fn clean(&self, cx: &DocContext<'_>) -> Item { - Item::from_hir_id_and_parts( - self.id, - Some(self.name), - StructItem(Struct { - struct_type: self.struct_type, - generics: self.generics.clean(cx), - fields: self.fields.clean(cx), - fields_stripped: false, - }), - cx, - ) - } -} - -impl Clean for doctree::Union<'_> { - fn clean(&self, cx: &DocContext<'_>) -> Item { - Item::from_hir_id_and_parts( - self.id, - Some(self.name), - UnionItem(Union { - struct_type: self.struct_type, - generics: self.generics.clean(cx), - fields: self.fields.clean(cx), - fields_stripped: false, - }), - cx, - ) - } -} - impl Clean for rustc_hir::VariantData<'_> { fn clean(&self, cx: &DocContext<'_>) -> VariantStruct { VariantStruct { @@ -1819,31 +1890,10 @@ impl Clean for rustc_hir::VariantData<'_> { } } -impl Clean for doctree::Enum<'_> { +impl Clean for hir::Variant<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { - Item::from_hir_id_and_parts( - self.id, - Some(self.name), - EnumItem(Enum { - variants: self.variants.iter().map(|v| v.clean(cx)).collect(), - generics: self.generics.clean(cx), - variants_stripped: false, - }), - cx, - ) - } -} - -impl Clean for doctree::Variant<'_> { - fn clean(&self, cx: &DocContext<'_>) -> Item { - let what_rustc_thinks = Item::from_hir_id_and_parts( - self.id, - Some(self.name), - VariantItem(Variant { kind: self.def.clean(cx) }), - cx, - ); - // don't show `pub` for variants, which are always public - Item { visibility: Inherited, ..what_rustc_thinks } + let kind = VariantItem(Variant { kind: self.data.clean(cx) }); + Item::from_hir_id_and_parts(self.id, Some(self.ident.name), kind, cx) } } @@ -1981,33 +2031,6 @@ impl Clean for Symbol { } } -impl Clean for doctree::Typedef<'_> { - fn clean(&self, cx: &DocContext<'_>) -> Item { - let type_ = self.ty.clean(cx); - let item_type = type_.def_id().and_then(|did| inline::build_ty(cx, did)); - Item::from_hir_id_and_parts( - self.id, - Some(self.name), - TypedefItem(Typedef { type_, generics: self.gen.clean(cx), item_type }, false), - cx, - ) - } -} - -impl Clean for doctree::OpaqueTy<'_> { - fn clean(&self, cx: &DocContext<'_>) -> Item { - Item::from_hir_id_and_parts( - self.id, - Some(self.name), - OpaqueTyItem(OpaqueTy { - bounds: self.opaque_ty.bounds.clean(cx), - generics: self.opaque_ty.generics.clean(cx), - }), - cx, - ) - } -} - impl Clean for hir::BareFnTy<'_> { fn clean(&self, cx: &DocContext<'_>) -> BareFunctionDecl { let (generic_params, decl) = enter_impl_trait(cx, || { @@ -2017,40 +2040,6 @@ impl Clean for hir::BareFnTy<'_> { } } -impl Clean for doctree::Static<'_> { - fn clean(&self, cx: &DocContext<'_>) -> Item { - debug!("cleaning static {}: {:?}", self.name.clean(cx), self); - Item::from_hir_id_and_parts( - self.id, - Some(self.name), - StaticItem(Static { - type_: self.type_.clean(cx), - mutability: self.mutability, - expr: print_const_expr(cx, self.expr), - }), - cx, - ) - } -} - -impl Clean for doctree::Constant<'_> { - fn clean(&self, cx: &DocContext<'_>) -> Item { - let def_id = cx.tcx.hir().local_def_id(self.id).to_def_id(); - - Item::from_def_id_and_parts( - def_id, - Some(self.name), - ConstantItem(Constant { - type_: self.type_.clean(cx), - expr: print_const_expr(cx, self.expr), - value: print_evaluated_const(cx, def_id), - is_literal: is_literal_expr(cx, self.expr.hir_id), - }), - cx, - ) - } -} - impl Clean for ty::ImplPolarity { fn clean(&self, _: &DocContext<'_>) -> ImplPolarity { match self { @@ -2062,196 +2051,188 @@ impl Clean for ty::ImplPolarity { } } -impl Clean> for doctree::Impl<'_> { - fn clean(&self, cx: &DocContext<'_>) -> Vec { - let mut ret = Vec::new(); - let trait_ = self.trait_.clean(cx); - let items = self.items.iter().map(|ii| ii.clean(cx)).collect::>(); - let def_id = cx.tcx.hir().local_def_id(self.id); - - // If this impl block is an implementation of the Deref trait, then we - // need to try inlining the target's inherent impl blocks as well. - if trait_.def_id() == cx.tcx.lang_items().deref_trait() { - build_deref_target_impls(cx, &items, &mut ret); +fn clean_impl(item: &hir::Item<'_>, cx: &DocContext<'_>) -> Vec { + let (unsafety, of_trait, self_ty, items, generics) = match item.kind { + hir::ItemKind::Impl { unsafety, ref of_trait, self_ty, items, ref generics, .. } => { + (unsafety, of_trait, self_ty, items, generics) } - - let provided: FxHashSet = trait_ - .def_id() - .map(|did| { - cx.tcx.provided_trait_methods(did).map(|meth| meth.ident.to_string()).collect() - }) - .unwrap_or_default(); - - let for_ = self.for_.clean(cx); - let type_alias = for_.def_id().and_then(|did| match cx.tcx.def_kind(did) { - DefKind::TyAlias => Some(cx.tcx.type_of(did).clean(cx)), - _ => None, + _ => unreachable!(), + }; + + // Don't duplicate impls when implementing a trait, we'll pick + // them up regardless of where they're located. + if of_trait.is_some() { + return vec![]; + } + + let mut ret = Vec::new(); + let trait_ = of_trait.clean(cx); + let items: Vec<_> = items.iter().map(|ii| cx.tcx.hir().impl_item(ii.id).clean(cx)).collect(); + let def_id = cx.tcx.hir().local_def_id(item.hir_id); + + // If this impl block is an implementation of the Deref trait, then we + // need to try inlining the target's inherent impl blocks as well. + if trait_.def_id() == cx.tcx.lang_items().deref_trait() { + build_deref_target_impls(cx, &items, &mut ret); + } + + let provided: FxHashSet = trait_ + .def_id() + .map(|did| cx.tcx.provided_trait_methods(did).map(|meth| meth.ident.to_string()).collect()) + .unwrap_or_default(); + + let for_ = self_ty.clean(cx); + let type_alias = for_.def_id().and_then(|did| match cx.tcx.def_kind(did) { + DefKind::TyAlias => Some(cx.tcx.type_of(did).clean(cx)), + _ => None, + }); + let make_item = |trait_: Option, for_: Type, items: Vec| Item { + name: None, + attrs: item.attrs.clean(cx), + source: item.span.clean(cx), + def_id: def_id.to_def_id(), + visibility: item.vis.clean(cx), + stability: cx.stability(item.hir_id), + deprecation: cx.deprecation(item.hir_id).clean(cx), + kind: ImplItem(Impl { + unsafety, + generics: generics.clean(cx), + provided_trait_methods: provided.clone(), + trait_, + for_, + items, + polarity: Some(cx.tcx.impl_polarity(def_id).clean(cx)), + synthetic: false, + blanket_impl: None, + }), + }; + if let Some(type_alias) = type_alias { + ret.push(make_item(trait_.clone(), type_alias, items.clone())); + } + ret.push(make_item(trait_, for_, items)); + ret +} + +fn clean_extern_crate( + item: &hir::Item<'_>, + renamed: Option, + cx: &DocContext<'_>, +) -> MaybeInlined { + let please_inline = item.vis.node.is_pub() + && item.attrs.iter().any(|a| { + a.has_name(sym::doc) + && match a.meta_item_list() { + Some(l) => attr::list_contains_name(&l, sym::inline), + None => false, + } }); - let make_item = |trait_: Option, for_: Type, items: Vec| Item { - name: None, - attrs: self.attrs.clean(cx), - source: self.span.clean(cx), - def_id: def_id.to_def_id(), - visibility: self.vis.clean(cx), - stability: cx.stability(self.id), - deprecation: cx.deprecation(self.id).clean(cx), - kind: ImplItem(Impl { - unsafety: self.unsafety, - generics: self.generics.clean(cx), - provided_trait_methods: provided.clone(), - trait_, - for_, - items, - polarity: Some(cx.tcx.impl_polarity(def_id).clean(cx)), - synthetic: false, - blanket_impl: None, - }), - }; - if let Some(type_alias) = type_alias { - ret.push(make_item(trait_.clone(), type_alias, items.clone())); - } - ret.push(make_item(trait_, for_, items)); - ret - } -} + let def_id = cx.tcx.hir().local_def_id(item.hir_id).to_def_id(); + let name = cx.tcx.item_name(def_id); -impl Clean> for doctree::ExternCrate<'_> { - fn clean(&self, cx: &DocContext<'_>) -> Vec { - let please_inline = self.vis.node.is_pub() - && self.attrs.iter().any(|a| { - a.has_name(sym::doc) - && match a.meta_item_list() { - Some(l) => attr::list_contains_name(&l, sym::inline), - None => false, - } - }); + if please_inline { + let mut visited = FxHashSet::default(); - if please_inline { - let mut visited = FxHashSet::default(); - - let res = Res::Def(DefKind::Mod, DefId { krate: self.cnum, index: CRATE_DEF_INDEX }); + let res = Res::Def(DefKind::Mod, def_id); - if let Some(items) = inline::try_inline( - cx, - cx.tcx.parent_module(self.hir_id).to_def_id(), - res, - self.name, - Some(self.attrs), - &mut visited, - ) { - return items; - } + if let Some(items) = inline::try_inline( + cx, + cx.tcx.parent_module(item.hir_id).to_def_id(), + res, + name, + Some(item.attrs), + &mut visited, + ) { + return MaybeInlined::InlinedWithoutOriginal(items); } - - vec![Item { - name: None, - attrs: self.attrs.clean(cx), - source: self.span.clean(cx), - def_id: DefId { krate: self.cnum, index: CRATE_DEF_INDEX }, - visibility: self.vis.clean(cx), - stability: None, - deprecation: None, - kind: ExternCrateItem(self.name.clean(cx), self.path.clone()), - }] } -} -impl Clean> for doctree::Import<'_> { - fn clean(&self, cx: &DocContext<'_>) -> Vec { - // We need this comparison because some imports (for std types for example) - // are "inserted" as well but directly by the compiler and they should not be - // taken into account. - if self.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::StdImports) { - return Vec::new(); - } - - // We consider inlining the documentation of `pub use` statements, but we - // forcefully don't inline if this is not public or if the - // #[doc(no_inline)] attribute is present. - // Don't inline doc(hidden) imports so they can be stripped at a later stage. - let mut denied = !self.vis.node.is_pub() - || self.attrs.iter().any(|a| { - a.has_name(sym::doc) - && match a.meta_item_list() { - Some(l) => { - attr::list_contains_name(&l, sym::no_inline) - || attr::list_contains_name(&l, sym::hidden) - } - None => false, + MaybeInlined::NotInlined(ExternCrateItem(name.clean(cx), renamed.clean(cx))) +} + +fn clean_import( + item: &hir::Item<'_>, + path: &hir::Path<'_>, + kind: hir::UseKind, + cx: &DocContext<'_>, +) -> MaybeInlined { + // We need this comparison because some imports (for std types for example) + // are "inserted" as well but directly by the compiler and they should not be + // taken into account. + if item.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::StdImports) + // Ignore ListStem; rustdoc doesn't care about it + || kind == hir::UseKind::ListStem + { + return MaybeInlined::InlinedWithoutOriginal(Vec::new()); + } + + // We consider inlining the documentation of `pub use` statements, but we + // forcefully don't inline if this is not public or if the + // #[doc(no_inline)] attribute is present. + // Don't inline doc(hidden) imports so they can be stripped at a later stage. + let mut denied = !item.vis.node.is_pub() + || item.attrs.iter().any(|a| { + a.has_name(sym::doc) + && match a.meta_item_list() { + Some(l) => { + attr::list_contains_name(&l, sym::no_inline) + || attr::list_contains_name(&l, sym::hidden) } - }); - // Also check whether imports were asked to be inlined, in case we're trying to re-export a - // crate in Rust 2018+ - let please_inline = self.attrs.lists(sym::doc).has_word(sym::inline); - let path = self.path.clean(cx); - let inner = if self.glob { - if !denied { - let mut visited = FxHashSet::default(); - if let Some(items) = inline::try_inline_glob(cx, path.res, &mut visited) { - return items; + None => false, } + }); + // Also check whether imports were asked to be inlined, in case we're trying to re-export a + // crate in Rust 2018+ + let please_inline = item.attrs.lists(sym::doc).has_word(sym::inline); + let path = path.clean(cx); + let kind = if kind == hir::UseKind::Glob { + if !denied { + let mut visited = FxHashSet::default(); + if let Some(items) = inline::try_inline_glob(cx, path.res, &mut visited) { + return MaybeInlined::InlinedWithoutOriginal(items); } - Import::new_glob(resolve_use_source(cx, path), true) - } else { - let name = self.name; - if !please_inline { - if let Res::Def(DefKind::Mod, did) = path.res { - if !did.is_local() && did.index == CRATE_DEF_INDEX { - // if we're `pub use`ing an extern crate root, don't inline it unless we - // were specifically asked for it - denied = true; - } + } + Import::new_glob(resolve_use_source(cx, path), true) + } else { + let name = cx.tcx.hir().name(item.hir_id); + if !please_inline { + if let Res::Def(DefKind::Mod, did) = path.res { + if !did.is_local() && did.index == CRATE_DEF_INDEX { + // if we're `pub use`ing an extern crate root, don't inline it unless we + // were specifically asked for it + denied = true; } } - if !denied { - let mut visited = FxHashSet::default(); + } + if !denied { + let mut visited = FxHashSet::default(); - if let Some(mut items) = inline::try_inline( - cx, - cx.tcx.parent_module(self.id).to_def_id(), - path.res, - name, - Some(self.attrs), - &mut visited, - ) { - items.push(Item { - name: None, - attrs: self.attrs.clean(cx), - source: self.span.clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), - visibility: self.vis.clean(cx), - stability: None, - deprecation: None, - kind: ImportItem(Import::new_simple( - self.name.clean(cx), - resolve_use_source(cx, path), - false, - )), - }); - return items; - } + if let Some(items) = inline::try_inline( + cx, + cx.tcx.parent_module(item.hir_id).to_def_id(), + path.res, + name, + Some(item.attrs), + &mut visited, + ) { + let kind = ImportItem(Import::new_simple( + name.clean(cx), + resolve_use_source(cx, path), + false, + )); + return MaybeInlined::InlinedWithOriginal(items, kind); } - Import::new_simple(name.clean(cx), resolve_use_source(cx, path), true) - }; + } + Import::new_simple(name.clean(cx), resolve_use_source(cx, path), true) + }; - vec![Item { - name: None, - attrs: self.attrs.clean(cx), - source: self.span.clean(cx), - def_id: DefId::local(CRATE_DEF_INDEX), - visibility: self.vis.clean(cx), - stability: None, - deprecation: None, - kind: ImportItem(inner), - }] - } + MaybeInlined::NotInlined(ImportItem(kind)) } -impl Clean for doctree::ForeignItem<'_> { +impl Clean for hir::ForeignItem<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { let kind = match self.kind { hir::ForeignItemKind::Fn(ref decl, ref names, ref generics) => { - let abi = cx.tcx.hir().get_foreign_abi(self.id); + let abi = cx.tcx.hir().get_foreign_abi(self.hir_id); let (generics, decl) = enter_impl_trait(cx, || (generics.clean(cx), (&**decl, &names[..]).clean(cx))); let (all_types, ret_types) = get_all_types(&generics, &decl, cx); @@ -2268,36 +2249,41 @@ impl Clean for doctree::ForeignItem<'_> { ret_types, }) } - hir::ForeignItemKind::Static(ref ty, mutbl) => ForeignStaticItem(Static { - type_: ty.clean(cx), - mutability: *mutbl, - expr: String::new(), - }), + hir::ForeignItemKind::Static(ref ty, mutability) => { + ForeignStaticItem(Static { type_: ty.clean(cx), mutability, expr: String::new() }) + } hir::ForeignItemKind::Type => ForeignTypeItem, }; - Item::from_hir_id_and_parts(self.id, Some(self.name), kind, cx) + Item::from_hir_id_and_parts(self.hir_id, Some(self.ident.name), kind, cx) } } -impl Clean for doctree::Macro { +impl Clean for hir::MacroDef<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { - Item::from_def_id_and_parts( - self.def_id, - Some(self.name), - MacroItem(Macro { + let tts: Vec<_> = self.ast.body.inner_tokens().trees().collect(); + // Extract the spans of all matchers. They represent the "interface" of the macro. + let matchers = tts.chunks(4).map(|arm| arm[0].span()); + + Item { + name: Some(self.ident.name.to_string()), + attrs: self.attrs.clean(cx), + source: self.span.clean(cx), + visibility: Public, + stability: cx.stability(self.hir_id), + deprecation: cx.deprecation(self.hir_id).clean(cx), + def_id: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(), + kind: MacroItem(Macro { source: format!( "macro_rules! {} {{\n{}}}", - self.name, - self.matchers - .iter() + self.ident.name, + matchers .map(|span| { format!(" {} => {{ ... }};\n", span.to_src(cx)) }) .collect::() ), - imported_from: self.imported_from.clean(cx), + imported_from: None, }), - cx, - ) + } } } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 22917fbceb48a..14f49a2d38b0b 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -24,7 +24,7 @@ crate fn krate(mut cx: &mut DocContext<'_>) -> Crate { use crate::visit_lib::LibEmbargoVisitor; let krate = cx.tcx.hir().krate(); - let module = crate::visit_ast::RustdocVisitor::new(&mut cx).visit(krate); + //let module = crate::visit_ast::RustdocVisitor::new(&mut cx).visit(krate); let mut r = cx.renderinfo.get_mut(); r.deref_trait_did = cx.tcx.lang_items().deref_trait(); @@ -41,10 +41,10 @@ crate fn krate(mut cx: &mut DocContext<'_>) -> Crate { // Clean the crate, translating the entire librustc_ast AST to one that is // understood by rustdoc. - let mut module = module.clean(cx); + let mut krate = krate.clean(cx); let mut masked_crates = FxHashSet::default(); - match module.kind { + match krate.kind { ItemKind::ModuleItem(ref module) => { for it in &module.items { // `compiler_builtins` should be masked too, but we can't apply @@ -62,7 +62,7 @@ crate fn krate(mut cx: &mut DocContext<'_>) -> Crate { let ExternalCrate { name, src, primitives, keywords, .. } = LOCAL_CRATE.clean(cx); { - let m = match module.kind { + let m = match krate.kind { ItemKind::ModuleItem(ref mut m) => m, _ => unreachable!(), }; @@ -92,7 +92,7 @@ crate fn krate(mut cx: &mut DocContext<'_>) -> Crate { name, version: None, src, - module: Some(module), + module: Some(krate), externs, primitives, external_traits: cx.external_traits.clone(), diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index bd9262191356f..f71ea805e1f99 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -7,64 +7,6 @@ use rustc_span::hygiene::MacroKind; use rustc_span::{self, Span, Symbol}; use rustc_hir as hir; -use rustc_hir::def_id::CrateNum; -use rustc_hir::HirId; - -crate struct Module<'hir> { - crate name: Option, - crate attrs: &'hir [ast::Attribute], - crate where_outer: Span, - crate where_inner: Span, - crate extern_crates: Vec>, - crate imports: Vec>, - crate structs: Vec>, - crate unions: Vec>, - crate enums: Vec>, - crate fns: Vec>, - crate mods: Vec>, - crate id: hir::HirId, - crate typedefs: Vec>, - crate opaque_tys: Vec>, - crate statics: Vec>, - crate constants: Vec>, - crate traits: Vec>, - crate impls: Vec>, - crate foreigns: Vec>, - crate macros: Vec, - crate proc_macros: Vec, - crate trait_aliases: Vec>, - crate is_crate: bool, -} - -impl Module<'hir> { - crate fn new(name: Option, attrs: &'hir [ast::Attribute]) -> Module<'hir> { - Module { - name, - id: hir::CRATE_HIR_ID, - where_outer: rustc_span::DUMMY_SP, - where_inner: rustc_span::DUMMY_SP, - attrs, - extern_crates: Vec::new(), - imports: Vec::new(), - structs: Vec::new(), - unions: Vec::new(), - enums: Vec::new(), - fns: Vec::new(), - mods: Vec::new(), - typedefs: Vec::new(), - opaque_tys: Vec::new(), - statics: Vec::new(), - constants: Vec::new(), - traits: Vec::new(), - impls: Vec::new(), - foreigns: Vec::new(), - macros: Vec::new(), - proc_macros: Vec::new(), - trait_aliases: Vec::new(), - is_crate: false, - } - } -} #[derive(Debug, Clone, Copy)] crate enum StructType { @@ -76,57 +18,6 @@ crate enum StructType { Unit, } -crate struct Struct<'hir> { - crate id: hir::HirId, - crate struct_type: StructType, - crate name: Symbol, - crate generics: &'hir hir::Generics<'hir>, - crate fields: &'hir [hir::StructField<'hir>], -} - -crate struct Union<'hir> { - crate id: hir::HirId, - crate struct_type: StructType, - crate name: Symbol, - crate generics: &'hir hir::Generics<'hir>, - crate fields: &'hir [hir::StructField<'hir>], -} - -crate struct Enum<'hir> { - crate variants: Vec>, - crate generics: &'hir hir::Generics<'hir>, - crate id: hir::HirId, - crate name: Symbol, -} - -crate struct Variant<'hir> { - crate name: Symbol, - crate id: hir::HirId, - crate def: &'hir hir::VariantData<'hir>, -} - -crate struct Function<'hir> { - crate decl: &'hir hir::FnDecl<'hir>, - crate id: hir::HirId, - crate name: Symbol, - crate header: hir::FnHeader, - crate generics: &'hir hir::Generics<'hir>, - crate body: hir::BodyId, -} - -crate struct Typedef<'hir> { - crate ty: &'hir hir::Ty<'hir>, - crate gen: &'hir hir::Generics<'hir>, - crate name: Symbol, - crate id: hir::HirId, -} - -crate struct OpaqueTy<'hir> { - crate opaque_ty: &'hir hir::OpaqueTy<'hir>, - crate name: Symbol, - crate id: hir::HirId, -} - #[derive(Debug)] crate struct Static<'hir> { crate type_: &'hir hir::Ty<'hir>, @@ -139,72 +30,6 @@ crate struct Static<'hir> { crate span: Span, } -crate struct Constant<'hir> { - crate type_: &'hir hir::Ty<'hir>, - crate expr: hir::BodyId, - crate name: Symbol, - crate id: hir::HirId, -} - -crate struct Trait<'hir> { - crate is_auto: hir::IsAuto, - crate unsafety: hir::Unsafety, - crate name: Symbol, - crate items: Vec<&'hir hir::TraitItem<'hir>>, - crate generics: &'hir hir::Generics<'hir>, - crate bounds: &'hir [hir::GenericBound<'hir>], - crate attrs: &'hir [ast::Attribute], - crate id: hir::HirId, -} - -crate struct TraitAlias<'hir> { - crate name: Symbol, - crate generics: &'hir hir::Generics<'hir>, - crate bounds: &'hir [hir::GenericBound<'hir>], - crate id: hir::HirId, -} - -#[derive(Debug)] -crate struct Impl<'hir> { - crate unsafety: hir::Unsafety, - crate polarity: hir::ImplPolarity, - crate defaultness: hir::Defaultness, - crate constness: hir::Constness, - crate generics: &'hir hir::Generics<'hir>, - crate trait_: &'hir Option>, - crate for_: &'hir hir::Ty<'hir>, - crate items: Vec<&'hir hir::ImplItem<'hir>>, - crate attrs: &'hir [ast::Attribute], - crate span: Span, - crate vis: &'hir hir::Visibility<'hir>, - crate id: hir::HirId, -} - -crate struct ForeignItem<'hir> { - crate id: hir::HirId, - crate name: Symbol, - crate kind: &'hir hir::ForeignItemKind<'hir>, -} - -// For Macro we store the DefId instead of the NodeId, since we also create -// these imported macro_rules (which only have a DUMMY_NODE_ID). -crate struct Macro { - crate name: Symbol, - crate def_id: hir::def_id::DefId, - crate matchers: Vec, - crate imported_from: Option, -} - -crate struct ExternCrate<'hir> { - crate name: Symbol, - crate hir_id: HirId, - crate cnum: CrateNum, - crate path: Option, - crate vis: &'hir hir::Visibility<'hir>, - crate attrs: &'hir [ast::Attribute], - crate span: Span, -} - #[derive(Debug)] crate struct Import<'hir> { crate name: Symbol, diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 917c1a95fdbf5..4e01e2824a4cf 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -269,7 +269,8 @@ impl DocFolder for Cache { | clean::StructFieldItem(..) | clean::VariantItem(..) => ( ( - Some(*self.parent_stack.last().expect("parent_stack is empty")), + Some(*self.parent_stack.last() + .unwrap_or_else(|| panic!("parent_stack is empty (while indexing {:?})", item.kind))), Some(&self.stack[..self.stack.len() - 1]), ), false, @@ -303,7 +304,7 @@ impl DocFolder for Cache { match parent { (parent, Some(path)) if is_inherent_impl_item || !self.stripped_mod => { - debug_assert!(!item.is_stripped()); + debug_assert!(!item.is_stripped(), "name={:?}, kind={:?}", item.name, item.kind); // A crate has a module at its root, containing all items, // which should not be indexed. The crate-item itself is diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs index d0fdc69cc1932..fb0b41984af10 100644 --- a/src/librustdoc/formats/renderer.rs +++ b/src/librustdoc/formats/renderer.rs @@ -82,7 +82,7 @@ crate fn run_format( // recurse into the items of the module as well. let name = item.name.as_ref().unwrap().to_string(); if name.is_empty() { - panic!("Unexpected module with empty name"); + panic!("Unexpected module with empty name: {:?}", item); } cx.mod_item_in(&item, &name, &cache)?; @@ -97,7 +97,10 @@ crate fn run_format( cx.mod_item_out(&name)?; } else if item.name.is_some() { - cx.item(item, &cache)?; + match item.kind { + clean::ItemKind::ImportItem(..) => {} + _ => cx.item(item, &cache)?, + } } } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 28f7a4d316248..3061f98c073d7 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1725,7 +1725,10 @@ fn print_item(cx: &Context, item: &clean::Item, buf: &mut Buffer, cache: &Cache) "Module " } } - clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ", + clean::FunctionItem(..) + | clean::ForeignFunctionItem(..) + | clean::TyMethodItem(..) + | clean::MethodItem(..) => "Function ", clean::TraitItem(..) => "Trait ", clean::StructItem(..) => "Struct ", clean::UnionItem(..) => "Union ", @@ -1746,7 +1749,7 @@ fn print_item(cx: &Context, item: &clean::Item, buf: &mut Buffer, cache: &Cache) clean::TraitAliasItem(..) => "Trait Alias ", _ => { // We don't generate pages for any other type. - unreachable!(); + unreachable!("unknown kind {:?}", item.kind); } }; buf.write_str(name); @@ -1768,9 +1771,10 @@ fn print_item(cx: &Context, item: &clean::Item, buf: &mut Buffer, cache: &Cache) match item.kind { clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items), - clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => { - item_function(buf, cx, item, f) - } + clean::FunctionItem(ref f) + | clean::ForeignFunctionItem(ref f) + | clean::TyMethodItem(ref f) + | clean::MethodItem(ref f, _) => item_function(buf, cx, item, f), clean::TraitItem(ref t) => item_trait(buf, cx, item, t, cache), clean::StructItem(ref s) => item_struct(buf, cx, item, s, cache), clean::UnionItem(ref s) => item_union(buf, cx, item, s, cache), @@ -1787,7 +1791,7 @@ fn print_item(cx: &Context, item: &clean::Item, buf: &mut Buffer, cache: &Cache) clean::TraitAliasItem(ref ta) => item_trait_alias(buf, cx, item, ta, cache), _ => { // We don't generate pages for any other type. - unreachable!(); + unreachable!("unknown kind {:?}", item.kind); } } } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index a88efba77b41c..e15d11da1b4a9 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -82,7 +82,6 @@ mod json; mod markdown; mod passes; mod theme; -mod visit_ast; mod visit_lib; pub fn main() { diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs deleted file mode 100644 index 76fae11291805..0000000000000 --- a/src/librustdoc/visit_ast.rs +++ /dev/null @@ -1,545 +0,0 @@ -//! The Rust AST Visitor. Extracts useful information and massages it into a form -//! usable for `clean`. - -use rustc_ast as ast; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::Node; -use rustc_middle::middle::privacy::AccessLevel; -use rustc_middle::ty::TyCtxt; -use rustc_span::hygiene::MacroKind; -use rustc_span::source_map::Spanned; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{self, Span}; - -use std::mem; - -use crate::clean::{self, AttributesExt, NestedAttributesExt}; -use crate::core; -use crate::doctree::*; - -// FIXME: Should this be replaced with tcx.def_path_str? -fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec { - let crate_name = tcx.crate_name(did.krate).to_string(); - let relative = tcx.def_path(did).data.into_iter().filter_map(|elem| { - // extern blocks have an empty name - let s = elem.data.to_string(); - if !s.is_empty() { Some(s) } else { None } - }); - std::iter::once(crate_name).chain(relative).collect() -} - -// Also, is there some reason that this doesn't use the 'visit' -// framework from syntax?. - -crate struct RustdocVisitor<'a, 'tcx> { - cx: &'a mut core::DocContext<'tcx>, - view_item_stack: FxHashSet, - inlining: bool, - /// Are the current module and all of its parents public? - inside_public_path: bool, - exact_paths: FxHashMap>, -} - -impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { - crate fn new(cx: &'a mut core::DocContext<'tcx>) -> RustdocVisitor<'a, 'tcx> { - // If the root is re-exported, terminate all recursion. - let mut stack = FxHashSet::default(); - stack.insert(hir::CRATE_HIR_ID); - RustdocVisitor { - cx, - view_item_stack: stack, - inlining: false, - inside_public_path: true, - exact_paths: FxHashMap::default(), - } - } - - fn store_path(&mut self, did: DefId) { - let tcx = self.cx.tcx; - self.exact_paths.entry(did).or_insert_with(|| def_id_to_path(tcx, did)); - } - - crate fn visit(mut self, krate: &'tcx hir::Crate<'_>) -> Module<'tcx> { - let mut module = self.visit_mod_contents( - krate.item.span, - krate.item.attrs, - &Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Public }, - hir::CRATE_HIR_ID, - &krate.item.module, - None, - ); - // Attach the crate's exported macros to the top-level module: - module - .macros - .extend(krate.exported_macros.iter().map(|def| self.visit_local_macro(def, None))); - module.is_crate = true; - - self.cx.renderinfo.get_mut().exact_paths = self.exact_paths; - - module - } - - fn visit_variant_data( - &mut self, - item: &'tcx hir::Item<'_>, - name: Symbol, - sd: &'tcx hir::VariantData<'_>, - generics: &'tcx hir::Generics<'_>, - ) -> Struct<'tcx> { - debug!("visiting struct"); - let struct_type = struct_type_from_def(&*sd); - Struct { id: item.hir_id, struct_type, name, generics, fields: sd.fields() } - } - - fn visit_union_data( - &mut self, - item: &'tcx hir::Item<'_>, - name: Symbol, - sd: &'tcx hir::VariantData<'_>, - generics: &'tcx hir::Generics<'_>, - ) -> Union<'tcx> { - debug!("visiting union"); - let struct_type = struct_type_from_def(&*sd); - Union { id: item.hir_id, struct_type, name, generics, fields: sd.fields() } - } - - fn visit_enum_def( - &mut self, - it: &'tcx hir::Item<'_>, - name: Symbol, - def: &'tcx hir::EnumDef<'_>, - generics: &'tcx hir::Generics<'_>, - ) -> Enum<'tcx> { - debug!("visiting enum"); - Enum { - name, - variants: def - .variants - .iter() - .map(|v| Variant { name: v.ident.name, id: v.id, def: &v.data }) - .collect(), - generics, - id: it.hir_id, - } - } - - fn visit_fn( - &mut self, - om: &mut Module<'tcx>, - item: &'tcx hir::Item<'_>, - name: Symbol, - decl: &'tcx hir::FnDecl<'_>, - header: hir::FnHeader, - generics: &'tcx hir::Generics<'_>, - body: hir::BodyId, - ) { - debug!("visiting fn"); - let macro_kind = item.attrs.iter().find_map(|a| { - if a.has_name(sym::proc_macro) { - Some(MacroKind::Bang) - } else if a.has_name(sym::proc_macro_derive) { - Some(MacroKind::Derive) - } else if a.has_name(sym::proc_macro_attribute) { - Some(MacroKind::Attr) - } else { - None - } - }); - match macro_kind { - Some(kind) => { - let name = if kind == MacroKind::Derive { - item.attrs - .lists(sym::proc_macro_derive) - .find_map(|mi| mi.ident()) - .expect("proc-macro derives require a name") - .name - } else { - name - }; - - let mut helpers = Vec::new(); - for mi in item.attrs.lists(sym::proc_macro_derive) { - if !mi.has_name(sym::attributes) { - continue; - } - - if let Some(list) = mi.meta_item_list() { - for inner_mi in list { - if let Some(ident) = inner_mi.ident() { - helpers.push(ident.name); - } - } - } - } - - om.proc_macros.push(ProcMacro { name, id: item.hir_id, kind, helpers }); - } - None => { - om.fns.push(Function { id: item.hir_id, decl, name, generics, header, body }); - } - } - } - - fn visit_mod_contents( - &mut self, - span: Span, - attrs: &'tcx [ast::Attribute], - vis: &'tcx hir::Visibility<'_>, - id: hir::HirId, - m: &'tcx hir::Mod<'tcx>, - name: Option, - ) -> Module<'tcx> { - let mut om = Module::new(name, attrs); - om.where_outer = span; - om.where_inner = m.inner; - om.id = id; - // Keep track of if there were any private modules in the path. - let orig_inside_public_path = self.inside_public_path; - self.inside_public_path &= vis.node.is_pub(); - for i in m.item_ids { - let item = self.cx.tcx.hir().expect_item(i.id); - self.visit_item(item, None, &mut om); - } - self.inside_public_path = orig_inside_public_path; - om - } - - /// Tries to resolve the target of a `crate use` statement and inlines the - /// target if it is defined locally and would not be documented otherwise, - /// or when it is specifically requested with `please_inline`. - /// (the latter is the case when the import is marked `doc(inline)`) - /// - /// Cross-crate inlining occurs later on during crate cleaning - /// and follows different rules. - /// - /// Returns `true` if the target has been inlined. - fn maybe_inline_local( - &mut self, - id: hir::HirId, - res: Res, - renamed: Option, - glob: bool, - om: &mut Module<'tcx>, - please_inline: bool, - ) -> bool { - fn inherits_doc_hidden(cx: &core::DocContext<'_>, mut node: hir::HirId) -> bool { - while let Some(id) = cx.tcx.hir().get_enclosing_scope(node) { - node = id; - if cx.tcx.hir().attrs(node).lists(sym::doc).has_word(sym::hidden) { - return true; - } - if node == hir::CRATE_HIR_ID { - break; - } - } - false - } - - debug!("maybe_inline_local res: {:?}", res); - - let tcx = self.cx.tcx; - let res_did = if let Some(did) = res.opt_def_id() { - did - } else { - return false; - }; - - let use_attrs = tcx.hir().attrs(id); - // Don't inline `doc(hidden)` imports so they can be stripped at a later stage. - let is_no_inline = use_attrs.lists(sym::doc).has_word(sym::no_inline) - || use_attrs.lists(sym::doc).has_word(sym::hidden); - - // For cross-crate impl inlining we need to know whether items are - // reachable in documentation -- a previously nonreachable item can be - // made reachable by cross-crate inlining which we're checking here. - // (this is done here because we need to know this upfront). - if !res_did.is_local() && !is_no_inline { - let attrs = clean::inline::load_attrs(self.cx, res_did); - let self_is_hidden = attrs.lists(sym::doc).has_word(sym::hidden); - if !self_is_hidden { - if let Res::Def(kind, did) = res { - if kind == DefKind::Mod { - crate::visit_lib::LibEmbargoVisitor::new(self.cx).visit_mod(did) - } else { - // All items need to be handled here in case someone wishes to link - // to them with intra-doc links - self.cx - .renderinfo - .get_mut() - .access_levels - .map - .insert(did, AccessLevel::Public); - } - } - } - return false; - } - - let res_hir_id = match res_did.as_local() { - Some(n) => tcx.hir().local_def_id_to_hir_id(n), - None => return false, - }; - - let is_private = !self.cx.renderinfo.borrow().access_levels.is_public(res_did); - let is_hidden = inherits_doc_hidden(self.cx, res_hir_id); - - // Only inline if requested or if the item would otherwise be stripped. - if (!please_inline && !is_private && !is_hidden) || is_no_inline { - return false; - } - - if !self.view_item_stack.insert(res_hir_id) { - return false; - } - - let ret = match tcx.hir().get(res_hir_id) { - Node::Item(&hir::Item { kind: hir::ItemKind::Mod(ref m), .. }) if glob => { - let prev = mem::replace(&mut self.inlining, true); - for i in m.item_ids { - let i = self.cx.tcx.hir().expect_item(i.id); - self.visit_item(i, None, om); - } - self.inlining = prev; - true - } - Node::Item(it) if !glob => { - let prev = mem::replace(&mut self.inlining, true); - self.visit_item(it, renamed, om); - self.inlining = prev; - true - } - Node::ForeignItem(it) if !glob => { - let prev = mem::replace(&mut self.inlining, true); - self.visit_foreign_item(it, renamed, om); - self.inlining = prev; - true - } - Node::MacroDef(def) if !glob => { - om.macros.push(self.visit_local_macro(def, renamed.map(|i| i.name))); - true - } - _ => false, - }; - self.view_item_stack.remove(&res_hir_id); - ret - } - - fn visit_item( - &mut self, - item: &'tcx hir::Item<'_>, - renamed: Option, - om: &mut Module<'tcx>, - ) { - debug!("visiting item {:?}", item); - let ident = renamed.unwrap_or(item.ident); - - if item.vis.node.is_pub() { - let def_id = self.cx.tcx.hir().local_def_id(item.hir_id); - self.store_path(def_id.to_def_id()); - } - - match item.kind { - hir::ItemKind::ForeignMod(ref fm) => { - for item in fm.items { - self.visit_foreign_item(item, None, om); - } - } - // If we're inlining, skip private items. - _ if self.inlining && !item.vis.node.is_pub() => {} - hir::ItemKind::GlobalAsm(..) => {} - hir::ItemKind::ExternCrate(orig_name) => { - let def_id = self.cx.tcx.hir().local_def_id(item.hir_id); - om.extern_crates.push(ExternCrate { - cnum: self.cx.tcx.extern_mod_stmt_cnum(def_id).unwrap_or(LOCAL_CRATE), - name: ident.name, - hir_id: item.hir_id, - path: orig_name.map(|x| x.to_string()), - vis: &item.vis, - attrs: &item.attrs, - span: item.span, - }) - } - hir::ItemKind::Use(_, hir::UseKind::ListStem) => {} - hir::ItemKind::Use(ref path, kind) => { - let is_glob = kind == hir::UseKind::Glob; - - // Struct and variant constructors and proc macro stubs always show up alongside - // their definitions, we've already processed them so just discard these. - if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = path.res { - return; - } - - // If there was a private module in the current path then don't bother inlining - // anything as it will probably be stripped anyway. - if item.vis.node.is_pub() && self.inside_public_path { - let please_inline = item.attrs.iter().any(|item| match item.meta_item_list() { - Some(ref list) if item.has_name(sym::doc) => { - list.iter().any(|i| i.has_name(sym::inline)) - } - _ => false, - }); - let ident = if is_glob { None } else { Some(ident) }; - if self.maybe_inline_local( - item.hir_id, - path.res, - ident, - is_glob, - om, - please_inline, - ) { - return; - } - } - - om.imports.push(Import { - name: ident.name, - id: item.hir_id, - vis: &item.vis, - attrs: &item.attrs, - path, - glob: is_glob, - span: item.span, - }); - } - hir::ItemKind::Mod(ref m) => { - om.mods.push(self.visit_mod_contents( - item.span, - &item.attrs, - &item.vis, - item.hir_id, - m, - Some(ident.name), - )); - } - hir::ItemKind::Enum(ref ed, ref gen) => { - om.enums.push(self.visit_enum_def(item, ident.name, ed, gen)) - } - hir::ItemKind::Struct(ref sd, ref gen) => { - om.structs.push(self.visit_variant_data(item, ident.name, sd, gen)) - } - hir::ItemKind::Union(ref sd, ref gen) => { - om.unions.push(self.visit_union_data(item, ident.name, sd, gen)) - } - hir::ItemKind::Fn(ref sig, ref gen, body) => { - self.visit_fn(om, item, ident.name, &sig.decl, sig.header, gen, body) - } - hir::ItemKind::TyAlias(ty, ref gen) => { - let t = Typedef { ty, gen, name: ident.name, id: item.hir_id }; - om.typedefs.push(t); - } - hir::ItemKind::OpaqueTy(ref opaque_ty) => { - let t = OpaqueTy { opaque_ty, name: ident.name, id: item.hir_id }; - om.opaque_tys.push(t); - } - hir::ItemKind::Static(type_, mutability, expr) => { - let s = Static { - type_, - mutability, - expr, - id: item.hir_id, - name: ident.name, - attrs: &item.attrs, - span: item.span, - vis: &item.vis, - }; - om.statics.push(s); - } - hir::ItemKind::Const(type_, expr) => { - // Underscore constants do not correspond to a nameable item and - // so are never useful in documentation. - if ident.name != kw::Underscore { - let s = Constant { type_, expr, id: item.hir_id, name: ident.name }; - om.constants.push(s); - } - } - hir::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref item_ids) => { - let items = item_ids.iter().map(|ti| self.cx.tcx.hir().trait_item(ti.id)).collect(); - let t = Trait { - is_auto, - unsafety, - name: ident.name, - items, - generics, - bounds, - id: item.hir_id, - attrs: &item.attrs, - }; - om.traits.push(t); - } - hir::ItemKind::TraitAlias(ref generics, ref bounds) => { - let t = TraitAlias { name: ident.name, generics, bounds, id: item.hir_id }; - om.trait_aliases.push(t); - } - - hir::ItemKind::Impl { - unsafety, - polarity, - defaultness, - constness, - defaultness_span: _, - ref generics, - ref of_trait, - self_ty, - ref items, - } => { - // Don't duplicate impls when inlining or if it's implementing a trait, we'll pick - // them up regardless of where they're located. - if !self.inlining && of_trait.is_none() { - let items = - items.iter().map(|item| self.cx.tcx.hir().impl_item(item.id)).collect(); - let i = Impl { - unsafety, - polarity, - defaultness, - constness, - generics, - trait_: of_trait, - for_: self_ty, - items, - attrs: &item.attrs, - id: item.hir_id, - span: item.span, - vis: &item.vis, - }; - om.impls.push(i); - } - } - } - } - - fn visit_foreign_item( - &mut self, - item: &'tcx hir::ForeignItem<'_>, - renamed: Option, - om: &mut Module<'tcx>, - ) { - // If inlining we only want to include public functions. - if self.inlining && !item.vis.node.is_pub() { - return; - } - - om.foreigns.push(ForeignItem { - id: item.hir_id, - name: renamed.unwrap_or(item.ident).name, - kind: &item.kind, - }); - } - - // Convert each `exported_macro` into a doc item. - fn visit_local_macro(&self, def: &'tcx hir::MacroDef<'_>, renamed: Option) -> Macro { - debug!("visit_local_macro: {}", def.ident); - let tts = def.ast.body.inner_tokens().trees().collect::>(); - // Extract the spans of all matchers. They represent the "interface" of the macro. - let matchers = tts.chunks(4).map(|arm| arm[0].span()).collect(); - - Macro { - def_id: self.cx.tcx.hir().local_def_id(def.hir_id).to_def_id(), - name: renamed.unwrap_or(def.ident.name), - matchers, - imported_from: None, - } - } -}