rustdoc/json/
mod.rs
1mod conversions;
8mod ids;
9mod import_finder;
10
11use std::cell::RefCell;
12use std::fs::{File, create_dir_all};
13use std::io::{BufWriter, Write, stdout};
14use std::path::PathBuf;
15use std::rc::Rc;
16
17use rustc_hir::def_id::{DefId, DefIdSet};
18use rustc_middle::ty::TyCtxt;
19use rustc_session::Session;
20use rustc_span::def_id::LOCAL_CRATE;
21use rustdoc_json_types as types;
22use rustdoc_json_types::FxHashMap;
26use tracing::{debug, trace};
27
28use crate::clean::ItemKind;
29use crate::clean::types::{ExternalCrate, ExternalLocation};
30use crate::config::RenderOptions;
31use crate::docfs::PathError;
32use crate::error::Error;
33use crate::formats::FormatRenderer;
34use crate::formats::cache::Cache;
35use crate::json::conversions::IntoJson;
36use crate::{clean, try_err};
37
38#[derive(Clone)]
39pub(crate) struct JsonRenderer<'tcx> {
40 tcx: TyCtxt<'tcx>,
41 index: Rc<RefCell<FxHashMap<types::Id, types::Item>>>,
44 out_dir: Option<PathBuf>,
48 cache: Rc<Cache>,
49 imported_items: DefIdSet,
50 id_interner: Rc<RefCell<ids::IdInterner>>,
51}
52
53impl<'tcx> JsonRenderer<'tcx> {
54 fn sess(&self) -> &'tcx Session {
55 self.tcx.sess
56 }
57
58 fn get_trait_implementors(&mut self, id: DefId) -> Vec<types::Id> {
59 Rc::clone(&self.cache)
60 .implementors
61 .get(&id)
62 .map(|implementors| {
63 implementors
64 .iter()
65 .map(|i| {
66 let item = &i.impl_item;
67 self.item(item.clone()).unwrap();
68 self.id_from_item(item)
69 })
70 .collect()
71 })
72 .unwrap_or_default()
73 }
74
75 fn get_impls(&mut self, id: DefId) -> Vec<types::Id> {
76 Rc::clone(&self.cache)
77 .impls
78 .get(&id)
79 .map(|impls| {
80 impls
81 .iter()
82 .filter_map(|i| {
83 let item = &i.impl_item;
84
85 let mut is_primitive_impl = false;
90 if let clean::types::ItemKind::ImplItem(ref impl_) = item.kind
91 && impl_.trait_.is_none()
92 && let clean::types::Type::Primitive(_) = impl_.for_
93 {
94 is_primitive_impl = true;
95 }
96
97 if item.item_id.is_local() || is_primitive_impl {
98 self.item(item.clone()).unwrap();
99 Some(self.id_from_item(item))
100 } else {
101 None
102 }
103 })
104 .collect()
105 })
106 .unwrap_or_default()
107 }
108
109 fn serialize_and_write<T: Write>(
110 &self,
111 output_crate: types::Crate,
112 mut writer: BufWriter<T>,
113 path: &str,
114 ) -> Result<(), Error> {
115 self.sess().time("rustdoc_json_serialize_and_write", || {
116 try_err!(
117 serde_json::ser::to_writer(&mut writer, &output_crate).map_err(|e| e.to_string()),
118 path
119 );
120 try_err!(writer.flush(), path);
121 Ok(())
122 })
123 }
124}
125
126impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
127 fn descr() -> &'static str {
128 "json"
129 }
130
131 const RUN_ON_MODULE: bool = false;
132 type ModuleData = ();
133
134 fn init(
135 krate: clean::Crate,
136 options: RenderOptions,
137 cache: Cache,
138 tcx: TyCtxt<'tcx>,
139 ) -> Result<(Self, clean::Crate), Error> {
140 debug!("Initializing json renderer");
141
142 let (krate, imported_items) = import_finder::get_imports(krate);
143
144 Ok((
145 JsonRenderer {
146 tcx,
147 index: Rc::new(RefCell::new(FxHashMap::default())),
148 out_dir: if options.output_to_stdout { None } else { Some(options.output) },
149 cache: Rc::new(cache),
150 imported_items,
151 id_interner: Default::default(),
152 },
153 krate,
154 ))
155 }
156
157 fn save_module_data(&mut self) -> Self::ModuleData {
158 unreachable!("RUN_ON_MODULE = false, should never call save_module_data")
159 }
160 fn restore_module_data(&mut self, _info: Self::ModuleData) {
161 unreachable!("RUN_ON_MODULE = false, should never call set_back_info")
162 }
163
164 fn item(&mut self, item: clean::Item) -> Result<(), Error> {
168 let item_type = item.type_();
169 let item_name = item.name;
170 trace!("rendering {item_type} {item_name:?}");
171
172 if let ItemKind::StrippedItem(inner) = &item.kind {
175 inner.inner_items().for_each(|i| self.item(i.clone()).unwrap());
176 }
177
178 item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap());
180
181 let item_id = item.item_id;
182 if let Some(mut new_item) = self.convert_item(item) {
183 let can_be_ignored = match new_item.inner {
184 types::ItemEnum::Trait(ref mut t) => {
185 t.implementations = self.get_trait_implementors(item_id.expect_def_id());
186 false
187 }
188 types::ItemEnum::Struct(ref mut s) => {
189 s.impls = self.get_impls(item_id.expect_def_id());
190 false
191 }
192 types::ItemEnum::Enum(ref mut e) => {
193 e.impls = self.get_impls(item_id.expect_def_id());
194 false
195 }
196 types::ItemEnum::Union(ref mut u) => {
197 u.impls = self.get_impls(item_id.expect_def_id());
198 false
199 }
200 types::ItemEnum::Primitive(ref mut p) => {
201 p.impls = self.get_impls(item_id.expect_def_id());
202 false
203 }
204
205 types::ItemEnum::Function(_)
206 | types::ItemEnum::Module(_)
207 | types::ItemEnum::Use(_)
208 | types::ItemEnum::AssocConst { .. }
209 | types::ItemEnum::AssocType { .. } => true,
210 types::ItemEnum::ExternCrate { .. }
211 | types::ItemEnum::StructField(_)
212 | types::ItemEnum::Variant(_)
213 | types::ItemEnum::TraitAlias(_)
214 | types::ItemEnum::Impl(_)
215 | types::ItemEnum::TypeAlias(_)
216 | types::ItemEnum::Constant { .. }
217 | types::ItemEnum::Static(_)
218 | types::ItemEnum::ExternType
219 | types::ItemEnum::Macro(_)
220 | types::ItemEnum::ProcMacro(_) => false,
221 };
222 let removed = self.index.borrow_mut().insert(new_item.id, new_item.clone());
223
224 if let Some(old_item) = removed {
228 if !can_be_ignored {
231 assert_eq!(old_item, new_item);
232 }
233 trace!("replaced {old_item:?}\nwith {new_item:?}");
234 }
235 }
236
237 trace!("done rendering {item_type} {item_name:?}");
238 Ok(())
239 }
240
241 fn mod_item_in(&mut self, _item: &clean::Item) -> Result<(), Error> {
242 unreachable!("RUN_ON_MODULE = false, should never call mod_item_in")
243 }
244
245 fn after_krate(&mut self) -> Result<(), Error> {
246 debug!("Done with crate");
247
248 let e = ExternalCrate { crate_num: LOCAL_CRATE };
249 let index = (*self.index).clone().into_inner();
250
251 debug!("Constructing Output");
252 let output_crate = types::Crate {
253 root: self.id_from_item_default(e.def_id().into()),
254 crate_version: self.cache.crate_version.clone(),
255 includes_private: self.cache.document_private,
256 index,
257 paths: self
258 .cache
259 .paths
260 .iter()
261 .chain(&self.cache.external_paths)
262 .map(|(&k, &(ref path, kind))| {
263 (
264 self.id_from_item_default(k.into()),
265 types::ItemSummary {
266 crate_id: k.krate.as_u32(),
267 path: path.iter().map(|s| s.to_string()).collect(),
268 kind: kind.into_json(self),
269 },
270 )
271 })
272 .collect(),
273 external_crates: self
274 .cache
275 .extern_locations
276 .iter()
277 .map(|(crate_num, external_location)| {
278 let e = ExternalCrate { crate_num: *crate_num };
279 (
280 crate_num.as_u32(),
281 types::ExternalCrate {
282 name: e.name(self.tcx).to_string(),
283 html_root_url: match external_location {
284 ExternalLocation::Remote(s) => Some(s.clone()),
285 _ => None,
286 },
287 },
288 )
289 })
290 .collect(),
291 format_version: types::FORMAT_VERSION,
292 };
293 if let Some(ref out_dir) = self.out_dir {
294 try_err!(create_dir_all(out_dir), out_dir);
295
296 let mut p = out_dir.clone();
297 p.push(output_crate.index.get(&output_crate.root).unwrap().name.clone().unwrap());
298 p.set_extension("json");
299
300 self.serialize_and_write(
301 output_crate,
302 try_err!(File::create_buffered(&p), p),
303 &p.display().to_string(),
304 )
305 } else {
306 self.serialize_and_write(output_crate, BufWriter::new(stdout().lock()), "<stdout>")
307 }
308 }
309
310 fn cache(&self) -> &Cache {
311 &self.cache
312 }
313}