From 1a15c7147f90afaa64ae3ff27fcbd678e2e44a8e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 15 Jul 2022 17:37:07 +0200 Subject: [PATCH] Fix rustdoc JSON inline --- src/librustdoc/clean/mod.rs | 5 +++-- src/librustdoc/json/conversions.rs | 30 ++++++++++++++++++++++++++---- src/librustdoc/json/mod.rs | 9 +++++++++ src/librustdoc/visit_ast.rs | 4 ++++ src/rustdoc-json-types/lib.rs | 5 ++++- 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2c98cba90d7..d6260b8ca06 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2120,8 +2120,9 @@ fn clean_use_statement<'tcx>( // 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 = !(visibility.is_public() - || (cx.render_options.document_private && is_visible_from_parent_mod)) + let mut denied = cx.output_format.is_json() + || !(visibility.is_public() + || (cx.render_options.document_private && is_visible_from_parent_mod)) || pub_underscore || attrs.iter().any(|a| { a.has_name(sym::doc) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index db2ad953f6a..2598b9b0b28 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -43,7 +43,16 @@ impl JsonRenderer<'_> { let span = item.span(self.tcx); let clean::Item { name, attrs: _, kind: _, visibility, item_id, cfg: _ } = item; let inner = match *item.kind { - clean::StrippedItem(_) | clean::KeywordItem(_) => return None, + clean::KeywordItem(_) => return None, + clean::StrippedItem(ref inner) => { + match &**inner { + // We document non-empty stripped modules as with `Module::is_stripped` set to + // `true`, to prevent contained items from being orphaned for downstream users, + // as JSON does no inlining. + clean::ModuleItem(m) if !m.items.is_empty() => from_clean_item(item, self.tcx), + _ => return None, + } + } _ => from_clean_item(item, self.tcx), }; Some(Item { @@ -220,7 +229,9 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum { let header = item.fn_header(tcx); match *item.kind { - ModuleItem(m) => ItemEnum::Module(Module { is_crate, items: ids(m.items, tcx) }), + ModuleItem(m) => { + ItemEnum::Module(Module { is_crate, items: ids(m.items, tcx), is_stripped: false }) + } ImportItem(i) => ItemEnum::Import(i.into_tcx(tcx)), StructItem(s) => ItemEnum::Struct(s.into_tcx(tcx)), UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)), @@ -257,8 +268,19 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum { bounds: b.into_iter().map(|x| x.into_tcx(tcx)).collect(), default: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)), }, - // `convert_item` early returns `None` for striped items and keywords. - StrippedItem(_) | KeywordItem(_) => unreachable!(), + // `convert_item` early returns `None` for stripped items and keywords. + KeywordItem(_) => unreachable!(), + StrippedItem(inner) => { + match *inner { + ModuleItem(m) => ItemEnum::Module(Module { + is_crate, + items: ids(m.items, tcx), + is_stripped: true, + }), + // `convert_item` early returns `None` for stripped items we're not including + _ => unreachable!(), + } + } ExternCrateItem { ref src } => ItemEnum::ExternCrate { name: name.as_ref().unwrap().to_string(), rename: src.map(|x| x.to_string()), diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index c7251b51152..6364d00d062 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -21,6 +21,7 @@ use rustc_span::def_id::LOCAL_CRATE; use rustdoc_json_types as types; use crate::clean::types::{ExternalCrate, ExternalLocation}; +use crate::clean::ItemKind; use crate::config::RenderOptions; use crate::docfs::PathError; use crate::error::Error; @@ -175,6 +176,14 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { /// the hashmap because certain items (traits and types) need to have their mappings for trait /// implementations filled out before they're inserted. fn item(&mut self, item: clean::Item) -> Result<(), Error> { + trace!("rendering {} {:?}", item.type_(), item.name); + + // Flatten items that recursively store other items. We include orphaned items from + // stripped modules and etc that are otherwise reachable. + if let ItemKind::StrippedItem(inner) = &*item.kind { + inner.inner_items().for_each(|i| self.item(i.clone()).unwrap()); + } + // Flatten items that recursively store other items item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap()); diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index ac934f6925d..ca7a20bf368 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -190,6 +190,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { ) -> bool { debug!("maybe_inline_local res: {:?}", res); + if self.cx.output_format.is_json() { + return false; + } + let tcx = self.cx.tcx; let Some(res_did) = res.opt_def_id() else { return false; diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 1168a89a8b2..761e94c7ebb 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; use serde::{Deserialize, Serialize}; /// rustdoc format-version. -pub const FORMAT_VERSION: u32 = 15; +pub const FORMAT_VERSION: u32 = 16; /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information /// about the language items in the local crate, as well as info about external items to allow @@ -245,6 +245,9 @@ pub enum ItemEnum { pub struct Module { pub is_crate: bool, pub items: Vec, + /// If `true`, this module is not part of the public API, but it contains + /// items that are re-exported as public API. + pub is_stripped: bool, } #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]