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