1 //! Rustdoc's JSON backend
3 //! This module contains the logic for rendering a crate as JSON rather than the normal static HTML
4 //! output. See [the RFC](https://github.com/rust-lang/rfcs/pull/2963) and the [`types`] module
5 //! docs for usage and details.
10 use std
::cell
::RefCell
;
11 use std
::fs
::{create_dir_all, File}
;
12 use std
::io
::{BufWriter, Write}
;
13 use std
::path
::PathBuf
;
16 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
17 use rustc_hir
::def_id
::DefId
;
18 use rustc_middle
::ty
::TyCtxt
;
19 use rustc_session
::Session
;
20 use rustc_span
::def_id
::LOCAL_CRATE
;
22 use rustdoc_json_types
as types
;
24 use crate::clean
::types
::{ExternalCrate, ExternalLocation}
;
25 use crate::clean
::ItemKind
;
26 use crate::config
::RenderOptions
;
27 use crate::docfs
::PathError
;
28 use crate::error
::Error
;
29 use crate::formats
::cache
::Cache
;
30 use crate::formats
::FormatRenderer
;
31 use crate::json
::conversions
::{from_item_id, from_item_id_with_name, IntoWithTcx}
;
32 use crate::{clean, try_err}
;
35 pub(crate) struct JsonRenderer
<'tcx
> {
37 /// A mapping of IDs that contains all local items for this crate which gets output as a top
38 /// level field of the JSON blob.
39 index
: Rc
<RefCell
<FxHashMap
<types
::Id
, types
::Item
>>>,
40 /// The directory where the blob will be written to.
43 imported_items
: FxHashSet
<DefId
>,
46 impl<'tcx
> JsonRenderer
<'tcx
> {
47 fn sess(&self) -> &'tcx Session
{
51 fn get_trait_implementors(&mut self, id
: DefId
) -> Vec
<types
::Id
> {
52 Rc
::clone(&self.cache
)
59 let item
= &i
.impl_item
;
60 self.item(item
.clone()).unwrap();
61 from_item_id_with_name(item
.item_id
, self.tcx
, item
.name
)
68 fn get_impls(&mut self, id
: DefId
) -> Vec
<types
::Id
> {
69 Rc
::clone(&self.cache
)
76 let item
= &i
.impl_item
;
78 // HACK(hkmatsumoto): For impls of primitive types, we index them
79 // regardless of whether they're local. This is because users can
80 // document primitive items in an arbitrary crate by using
82 let mut is_primitive_impl
= false;
83 if let clean
::types
::ItemKind
::ImplItem(ref impl_
) = *item
.kind
{
84 if impl_
.trait_
.is_none() {
85 if let clean
::types
::Type
::Primitive(_
) = impl_
.for_
{
86 is_primitive_impl
= true;
91 if item
.item_id
.is_local() || is_primitive_impl
{
92 self.item(item
.clone()).unwrap();
93 Some(from_item_id_with_name(item
.item_id
, self.tcx
, item
.name
))
103 fn get_trait_items(&mut self) -> Vec
<(types
::Id
, types
::Item
)> {
104 debug
!("Adding foreign trait items");
105 Rc
::clone(&self.cache
)
108 .filter_map(|(&id
, trait_item
)| {
109 // only need to synthesize items for external traits
111 for item
in &trait_item
.items
{
112 trace
!("Adding subitem to {id:?}: {:?}", item
.item_id
);
113 self.item(item
.clone()).unwrap();
115 let item_id
= from_item_id(id
.into(), self.tcx
);
120 crate_id
: id
.krate
.as_u32(),
129 .expect("Trait should either be in local or external paths")
133 .map(|s
| s
.to_string()),
134 visibility
: types
::Visibility
::Public
,
135 inner
: types
::ItemEnum
::Trait(trait_item
.clone().into_tcx(self.tcx
)),
137 docs
: Default
::default(),
138 links
: Default
::default(),
139 attrs
: Default
::default(),
140 deprecation
: Default
::default(),
151 impl<'tcx
> FormatRenderer
<'tcx
> for JsonRenderer
<'tcx
> {
152 fn descr() -> &'
static str {
156 const RUN_ON_MODULE
: bool
= false;
160 options
: RenderOptions
,
163 ) -> Result
<(Self, clean
::Crate
), Error
> {
164 debug
!("Initializing json renderer");
166 let (krate
, imported_items
) = import_finder
::get_imports(krate
);
171 index
: Rc
::new(RefCell
::new(FxHashMap
::default())),
172 out_path
: options
.output
,
173 cache
: Rc
::new(cache
),
180 fn make_child_renderer(&self) -> Self {
184 /// Inserts an item into the index. This should be used rather than directly calling insert on
185 /// the hashmap because certain items (traits and types) need to have their mappings for trait
186 /// implementations filled out before they're inserted.
187 fn item(&mut self, item
: clean
::Item
) -> Result
<(), Error
> {
188 let item_type
= item
.type_();
189 let item_name
= item
.name
;
190 trace
!("rendering {} {:?}", item_type
, item_name
);
192 // Flatten items that recursively store other items. We include orphaned items from
193 // stripped modules and etc that are otherwise reachable.
194 if let ItemKind
::StrippedItem(inner
) = &*item
.kind
{
195 inner
.inner_items().for_each(|i
| self.item(i
.clone()).unwrap());
198 // Flatten items that recursively store other items
199 item
.kind
.inner_items().for_each(|i
| self.item(i
.clone()).unwrap());
201 let name
= item
.name
;
202 let item_id
= item
.item_id
;
203 if let Some(mut new_item
) = self.convert_item(item
) {
204 let can_be_ignored
= match new_item
.inner
{
205 types
::ItemEnum
::Trait(ref mut t
) => {
206 t
.implementations
= self.get_trait_implementors(item_id
.expect_def_id());
209 types
::ItemEnum
::Struct(ref mut s
) => {
210 s
.impls
= self.get_impls(item_id
.expect_def_id());
213 types
::ItemEnum
::Enum(ref mut e
) => {
214 e
.impls
= self.get_impls(item_id
.expect_def_id());
217 types
::ItemEnum
::Union(ref mut u
) => {
218 u
.impls
= self.get_impls(item_id
.expect_def_id());
221 types
::ItemEnum
::Primitive(ref mut p
) => {
222 p
.impls
= self.get_impls(item_id
.expect_def_id());
226 types
::ItemEnum
::Method(_
)
227 | types
::ItemEnum
::Module(_
)
228 | types
::ItemEnum
::AssocConst { .. }
229 | types
::ItemEnum
::AssocType { .. }
=> true,
230 types
::ItemEnum
::ExternCrate { .. }
231 | types
::ItemEnum
::Import(_
)
232 | types
::ItemEnum
::StructField(_
)
233 | types
::ItemEnum
::Variant(_
)
234 | types
::ItemEnum
::Function(_
)
235 | types
::ItemEnum
::TraitAlias(_
)
236 | types
::ItemEnum
::Impl(_
)
237 | types
::ItemEnum
::Typedef(_
)
238 | types
::ItemEnum
::OpaqueTy(_
)
239 | types
::ItemEnum
::Constant(_
)
240 | types
::ItemEnum
::Static(_
)
241 | types
::ItemEnum
::ForeignType
242 | types
::ItemEnum
::Macro(_
)
243 | types
::ItemEnum
::ProcMacro(_
) => false,
248 .insert(from_item_id_with_name(item_id
, self.tcx
, name
), new_item
.clone());
250 // FIXME(adotinthevoid): Currently, the index is duplicated. This is a sanity check
251 // to make sure the items are unique. The main place this happens is when an item, is
252 // reexported in more than one place. See `rustdoc-json/reexport/in_root_and_mod`
253 if let Some(old_item
) = removed
{
254 // In case of generic implementations (like `impl<T> Trait for T {}`), all the
255 // inner items will be duplicated so we can ignore if they are slightly different.
257 assert_eq
!(old_item
, new_item
);
262 trace
!("done rendering {} {:?}", item_type
, item_name
);
266 fn mod_item_in(&mut self, _item
: &clean
::Item
) -> Result
<(), Error
> {
267 unreachable
!("RUN_ON_MODULE = false should never call mod_item_in")
270 fn after_krate(&mut self) -> Result
<(), Error
> {
271 debug
!("Done with crate");
273 debug
!("Adding Primitve impls");
274 for primitive
in Rc
::clone(&self.cache
).primitive_locations
.values() {
275 self.get_impls(*primitive
);
278 let e
= ExternalCrate { crate_num: LOCAL_CRATE }
;
280 // FIXME(adotinthevoid): Remove this, as it's not consistant with not
281 // inlining foreign items.
282 let foreign_trait_items
= self.get_trait_items();
283 let mut index
= (*self.index
).clone().into_inner();
284 index
.extend(foreign_trait_items
);
286 debug
!("Constructing Output");
287 // This needs to be the default HashMap for compatibility with the public interface for
288 // rustdoc-json-types
289 #[allow(rustc::default_hash_types)]
290 let output
= types
::Crate
{
291 root
: types
::Id(format
!("0:0:{}", e
.name(self.tcx
).as_u32())),
292 crate_version
: self.cache
.crate_version
.clone(),
293 includes_private
: self.cache
.document_private
,
294 index
: index
.into_iter().collect(),
299 .chain(&self.cache
.external_paths
)
300 .map(|(&k
, &(ref path
, kind
))| {
302 from_item_id(k
.into(), self.tcx
),
304 crate_id
: k
.krate
.as_u32(),
305 path
: path
.iter().map(|s
| s
.to_string()).collect(),
306 kind
: kind
.into_tcx(self.tcx
),
311 external_crates
: self
315 .map(|(crate_num
, external_location
)| {
316 let e
= ExternalCrate { crate_num: *crate_num }
;
319 types
::ExternalCrate
{
320 name
: e
.name(self.tcx
).to_string(),
321 html_root_url
: match external_location
{
322 ExternalLocation
::Remote(s
) => Some(s
.clone()),
329 format_version
: types
::FORMAT_VERSION
,
331 let out_dir
= self.out_path
.clone();
332 try_err
!(create_dir_all(&out_dir
), out_dir
);
335 p
.push(output
.index
.get(&output
.root
).unwrap().name
.clone().unwrap());
336 p
.set_extension("json");
337 let mut file
= BufWriter
::new(try_err
!(File
::create(&p
), p
));
338 serde_json
::ser
::to_writer(&mut file
, &output
).unwrap();
339 try_err
!(file
.flush(), p
);
344 fn cache(&self) -> &Cache
{