]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/json/mod.rs
Rollup merge of #87507 - jethrogb:jb/sgx-unmoveable-mutex, r=dtolnay
[rust.git] / src / librustdoc / json / mod.rs
1 //! Rustdoc's JSON backend
2 //!
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.
6
7 mod conversions;
8
9 use std::cell::RefCell;
10 use std::fs::File;
11 use std::path::PathBuf;
12 use std::rc::Rc;
13
14 use rustc_data_structures::fx::FxHashMap;
15 use rustc_hir::def_id::DefId;
16 use rustc_middle::ty::TyCtxt;
17 use rustc_session::Session;
18
19 use rustdoc_json_types as types;
20
21 use crate::clean;
22 use crate::clean::ExternalCrate;
23 use crate::config::RenderOptions;
24 use crate::error::Error;
25 use crate::formats::cache::Cache;
26 use crate::formats::FormatRenderer;
27 use crate::html::render::cache::ExternalLocation;
28 use crate::json::conversions::{from_item_id, IntoWithTcx};
29
30 #[derive(Clone)]
31 crate struct JsonRenderer<'tcx> {
32     tcx: TyCtxt<'tcx>,
33     /// A mapping of IDs that contains all local items for this crate which gets output as a top
34     /// level field of the JSON blob.
35     index: Rc<RefCell<FxHashMap<types::Id, types::Item>>>,
36     /// The directory where the blob will be written to.
37     out_path: PathBuf,
38     cache: Rc<Cache>,
39 }
40
41 impl JsonRenderer<'tcx> {
42     fn sess(&self) -> &'tcx Session {
43         self.tcx.sess
44     }
45
46     fn get_trait_implementors(&mut self, id: DefId) -> Vec<types::Id> {
47         Rc::clone(&self.cache)
48             .implementors
49             .get(&id)
50             .map(|implementors| {
51                 implementors
52                     .iter()
53                     .map(|i| {
54                         let item = &i.impl_item;
55                         self.item(item.clone()).unwrap();
56                         from_item_id(item.def_id)
57                     })
58                     .collect()
59             })
60             .unwrap_or_default()
61     }
62
63     fn get_impls(&mut self, id: DefId) -> Vec<types::Id> {
64         Rc::clone(&self.cache)
65             .impls
66             .get(&id)
67             .map(|impls| {
68                 impls
69                     .iter()
70                     .filter_map(|i| {
71                         let item = &i.impl_item;
72                         if item.def_id.is_local() {
73                             self.item(item.clone()).unwrap();
74                             Some(from_item_id(item.def_id))
75                         } else {
76                             None
77                         }
78                     })
79                     .collect()
80             })
81             .unwrap_or_default()
82     }
83
84     fn get_trait_items(&mut self) -> Vec<(types::Id, types::Item)> {
85         Rc::clone(&self.cache)
86             .traits
87             .iter()
88             .filter_map(|(&id, trait_item)| {
89                 // only need to synthesize items for external traits
90                 if !id.is_local() {
91                     let trait_item = &trait_item.trait_;
92                     trait_item.items.clone().into_iter().for_each(|i| self.item(i).unwrap());
93                     Some((
94                         from_item_id(id.into()),
95                         types::Item {
96                             id: from_item_id(id.into()),
97                             crate_id: id.krate.as_u32(),
98                             name: self
99                                 .cache
100                                 .paths
101                                 .get(&id)
102                                 .unwrap_or_else(|| {
103                                     self.cache
104                                         .external_paths
105                                         .get(&id)
106                                         .expect("Trait should either be in local or external paths")
107                                 })
108                                 .0
109                                 .last()
110                                 .map(Clone::clone),
111                             visibility: types::Visibility::Public,
112                             inner: types::ItemEnum::Trait(trait_item.clone().into_tcx(self.tcx)),
113                             span: None,
114                             docs: Default::default(),
115                             links: Default::default(),
116                             attrs: Default::default(),
117                             deprecation: Default::default(),
118                         },
119                     ))
120                 } else {
121                     None
122                 }
123             })
124             .collect()
125     }
126 }
127
128 impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
129     fn descr() -> &'static str {
130         "json"
131     }
132
133     const RUN_ON_MODULE: bool = false;
134
135     fn init(
136         krate: clean::Crate,
137         options: RenderOptions,
138         cache: Cache,
139         tcx: TyCtxt<'tcx>,
140     ) -> Result<(Self, clean::Crate), Error> {
141         debug!("Initializing json renderer");
142         Ok((
143             JsonRenderer {
144                 tcx,
145                 index: Rc::new(RefCell::new(FxHashMap::default())),
146                 out_path: options.output,
147                 cache: Rc::new(cache),
148             },
149             krate,
150         ))
151     }
152
153     fn make_child_renderer(&self) -> Self {
154         self.clone()
155     }
156
157     /// Inserts an item into the index. This should be used rather than directly calling insert on
158     /// the hashmap because certain items (traits and types) need to have their mappings for trait
159     /// implementations filled out before they're inserted.
160     fn item(&mut self, item: clean::Item) -> Result<(), Error> {
161         // Flatten items that recursively store other items
162         item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap());
163
164         let id = item.def_id;
165         if let Some(mut new_item) = self.convert_item(item) {
166             if let types::ItemEnum::Trait(ref mut t) = new_item.inner {
167                 t.implementors = self.get_trait_implementors(id.expect_def_id())
168             } else if let types::ItemEnum::Struct(ref mut s) = new_item.inner {
169                 s.impls = self.get_impls(id.expect_def_id())
170             } else if let types::ItemEnum::Enum(ref mut e) = new_item.inner {
171                 e.impls = self.get_impls(id.expect_def_id())
172             }
173             let removed = self.index.borrow_mut().insert(from_item_id(id), new_item.clone());
174
175             // FIXME(adotinthevoid): Currently, the index is duplicated. This is a sanity check
176             // to make sure the items are unique. The main place this happens is when an item, is
177             // reexported in more than one place. See `rustdoc-json/reexport/in_root_and_mod`
178             if let Some(old_item) = removed {
179                 assert_eq!(old_item, new_item);
180             }
181         }
182
183         Ok(())
184     }
185
186     fn mod_item_in(&mut self, _item: &clean::Item) -> Result<(), Error> {
187         unreachable!("RUN_ON_MODULE = false should never call mod_item_in")
188     }
189
190     fn after_krate(&mut self) -> Result<(), Error> {
191         debug!("Done with crate");
192         let mut index = (*self.index).clone().into_inner();
193         index.extend(self.get_trait_items());
194         // This needs to be the default HashMap for compatibility with the public interface for
195         // rustdoc-json
196         #[allow(rustc::default_hash_types)]
197         let output = types::Crate {
198             root: types::Id(String::from("0:0")),
199             crate_version: self.cache.crate_version.clone(),
200             includes_private: self.cache.document_private,
201             index: index.into_iter().collect(),
202             paths: self
203                 .cache
204                 .paths
205                 .clone()
206                 .into_iter()
207                 .chain(self.cache.external_paths.clone().into_iter())
208                 .map(|(k, (path, kind))| {
209                     (
210                         from_item_id(k.into()),
211                         types::ItemSummary {
212                             crate_id: k.krate.as_u32(),
213                             path,
214                             kind: kind.into_tcx(self.tcx),
215                         },
216                     )
217                 })
218                 .collect(),
219             external_crates: self
220                 .cache
221                 .extern_locations
222                 .iter()
223                 .map(|(crate_num, external_location)| {
224                     let e = ExternalCrate { crate_num: *crate_num };
225                     (
226                         crate_num.as_u32(),
227                         types::ExternalCrate {
228                             name: e.name(self.tcx).to_string(),
229                             html_root_url: match external_location {
230                                 ExternalLocation::Remote(s) => Some(s.clone()),
231                                 _ => None,
232                             },
233                         },
234                     )
235                 })
236                 .collect(),
237             format_version: 6,
238         };
239         let mut p = self.out_path.clone();
240         p.push(output.index.get(&output.root).unwrap().name.clone().unwrap());
241         p.set_extension("json");
242         let file = File::create(&p).map_err(|error| Error { error: error.to_string(), file: p })?;
243         serde_json::ser::to_writer(&file, &output).unwrap();
244         Ok(())
245     }
246
247     fn cache(&self) -> &Cache {
248         &self.cache
249     }
250 }