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