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_data_structures::fx::FxHashSet;
18use rustc_hir::def_id::{DefId, DefIdSet};
19use rustc_middle::ty::TyCtxt;
20use rustc_session::Session;
21use rustc_span::def_id::LOCAL_CRATE;
22use rustdoc_json_types as types;
23use rustdoc_json_types::FxHashMap;
27use tracing::{debug, trace};
28
29use crate::clean::ItemKind;
30use crate::clean::types::{ExternalCrate, ExternalLocation};
31use crate::config::RenderOptions;
32use crate::docfs::PathError;
33use crate::error::Error;
34use crate::formats::FormatRenderer;
35use crate::formats::cache::Cache;
36use crate::json::conversions::IntoJson;
37use crate::{clean, try_err};
38
39pub(crate) struct JsonRenderer<'tcx> {
40 tcx: TyCtxt<'tcx>,
41 index: FxHashMap<types::Id, types::Item>,
44 out_dir: Option<PathBuf>,
48 cache: Rc<Cache>,
49 imported_items: DefIdSet,
50 id_interner: 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).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).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
126fn target(sess: &rustc_session::Session) -> types::Target {
127 let globally_enabled_features: FxHashSet<&str> =
129 sess.unstable_target_features.iter().map(|name| name.as_str()).collect();
130
131 use rustc_target::target_features::Stability;
133 let feature_stability: FxHashMap<&str, Stability> = sess
134 .target
135 .rust_target_features()
136 .into_iter()
137 .copied()
138 .map(|(name, stability, _)| (name, stability))
139 .collect();
140
141 types::Target {
142 triple: sess.opts.target_triple.tuple().into(),
143 target_features: sess
144 .target
145 .rust_target_features()
146 .into_iter()
147 .copied()
148 .filter(|(_, stability, _)| {
149 stability.toggle_allowed().is_ok()
151 })
152 .map(|(name, stability, implied_features)| {
153 types::TargetFeature {
154 name: name.into(),
155 unstable_feature_gate: match stability {
156 Stability::Unstable(feature_gate) => Some(feature_gate.as_str().into()),
157 _ => None,
158 },
159 implies_features: implied_features
160 .into_iter()
161 .copied()
162 .filter(|name| {
163 feature_stability
165 .get(name)
166 .map(|stability| stability.toggle_allowed().is_ok())
167 .unwrap_or(false)
168 })
169 .map(String::from)
170 .collect(),
171 globally_enabled: globally_enabled_features.contains(name),
172 }
173 })
174 .collect(),
175 }
176}
177
178impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
179 fn descr() -> &'static str {
180 "json"
181 }
182
183 const RUN_ON_MODULE: bool = false;
184 type ModuleData = ();
185
186 fn init(
187 krate: clean::Crate,
188 options: RenderOptions,
189 cache: Cache,
190 tcx: TyCtxt<'tcx>,
191 ) -> Result<(Self, clean::Crate), Error> {
192 debug!("Initializing json renderer");
193
194 let (krate, imported_items) = import_finder::get_imports(krate);
195
196 Ok((
197 JsonRenderer {
198 tcx,
199 index: FxHashMap::default(),
200 out_dir: if options.output_to_stdout { None } else { Some(options.output) },
201 cache: Rc::new(cache),
202 imported_items,
203 id_interner: Default::default(),
204 },
205 krate,
206 ))
207 }
208
209 fn save_module_data(&mut self) -> Self::ModuleData {
210 unreachable!("RUN_ON_MODULE = false, should never call save_module_data")
211 }
212 fn restore_module_data(&mut self, _info: Self::ModuleData) {
213 unreachable!("RUN_ON_MODULE = false, should never call set_back_info")
214 }
215
216 fn item(&mut self, item: &clean::Item) -> Result<(), Error> {
220 use std::collections::hash_map::Entry;
221
222 let item_type = item.type_();
223 let item_name = item.name;
224 trace!("rendering {item_type} {item_name:?}");
225
226 if let ItemKind::StrippedItem(inner) = &item.kind {
229 inner.inner_items().for_each(|i| self.item(i).unwrap());
230 }
231
232 item.kind.inner_items().for_each(|i| self.item(i).unwrap());
234
235 let item_id = item.item_id;
236 if let Some(mut new_item) = self.convert_item(item) {
237 let can_be_ignored = match new_item.inner {
238 types::ItemEnum::Trait(ref mut t) => {
239 t.implementations = self.get_trait_implementors(item_id.expect_def_id());
240 false
241 }
242 types::ItemEnum::Struct(ref mut s) => {
243 s.impls = self.get_impls(item_id.expect_def_id());
244 false
245 }
246 types::ItemEnum::Enum(ref mut e) => {
247 e.impls = self.get_impls(item_id.expect_def_id());
248 false
249 }
250 types::ItemEnum::Union(ref mut u) => {
251 u.impls = self.get_impls(item_id.expect_def_id());
252 false
253 }
254 types::ItemEnum::Primitive(ref mut p) => {
255 p.impls = self.get_impls(item_id.expect_def_id());
256 false
257 }
258
259 types::ItemEnum::Function(_)
260 | types::ItemEnum::Module(_)
261 | types::ItemEnum::Use(_)
262 | types::ItemEnum::AssocConst { .. }
263 | types::ItemEnum::AssocType { .. } => true,
264 types::ItemEnum::ExternCrate { .. }
265 | types::ItemEnum::StructField(_)
266 | types::ItemEnum::Variant(_)
267 | types::ItemEnum::TraitAlias(_)
268 | types::ItemEnum::Impl(_)
269 | types::ItemEnum::TypeAlias(_)
270 | types::ItemEnum::Constant { .. }
271 | types::ItemEnum::Static(_)
272 | types::ItemEnum::ExternType
273 | types::ItemEnum::Macro(_)
274 | types::ItemEnum::ProcMacro(_) => false,
275 };
276
277 match self.index.entry(new_item.id) {
281 Entry::Vacant(entry) => {
282 entry.insert(new_item);
283 }
284 Entry::Occupied(mut entry) => {
285 let old_item = entry.get_mut();
289 if !can_be_ignored {
290 assert_eq!(*old_item, new_item);
291 }
292 trace!("replaced {old_item:?}\nwith {new_item:?}");
293 *old_item = new_item;
294 }
295 }
296 }
297
298 trace!("done rendering {item_type} {item_name:?}");
299 Ok(())
300 }
301
302 fn mod_item_in(&mut self, _item: &clean::Item) -> Result<(), Error> {
303 unreachable!("RUN_ON_MODULE = false, should never call mod_item_in")
304 }
305
306 fn after_krate(mut self) -> Result<(), Error> {
307 debug!("Done with crate");
308
309 let e = ExternalCrate { crate_num: LOCAL_CRATE };
310
311 let index = std::mem::take(&mut self.index);
313
314 let target = target(self.tcx.sess);
319
320 debug!("Constructing Output");
321 let output_crate = types::Crate {
322 root: self.id_from_item_default(e.def_id().into()),
323 crate_version: self.cache.crate_version.clone(),
324 includes_private: self.cache.document_private,
325 index,
326 paths: self
327 .cache
328 .paths
329 .iter()
330 .chain(&self.cache.external_paths)
331 .map(|(&k, &(ref path, kind))| {
332 (
333 self.id_from_item_default(k.into()),
334 types::ItemSummary {
335 crate_id: k.krate.as_u32(),
336 path: path.iter().map(|s| s.to_string()).collect(),
337 kind: kind.into_json(&self),
338 },
339 )
340 })
341 .collect(),
342 external_crates: self
343 .cache
344 .extern_locations
345 .iter()
346 .map(|(crate_num, external_location)| {
347 let e = ExternalCrate { crate_num: *crate_num };
348 (
349 crate_num.as_u32(),
350 types::ExternalCrate {
351 name: e.name(self.tcx).to_string(),
352 html_root_url: match external_location {
353 ExternalLocation::Remote(s) => Some(s.clone()),
354 _ => None,
355 },
356 },
357 )
358 })
359 .collect(),
360 target,
361 format_version: types::FORMAT_VERSION,
362 };
363 if let Some(ref out_dir) = self.out_dir {
364 try_err!(create_dir_all(out_dir), out_dir);
365
366 let mut p = out_dir.clone();
367 p.push(output_crate.index.get(&output_crate.root).unwrap().name.clone().unwrap());
368 p.set_extension("json");
369
370 self.serialize_and_write(
371 output_crate,
372 try_err!(File::create_buffered(&p), p),
373 &p.display().to_string(),
374 )
375 } else {
376 self.serialize_and_write(output_crate, BufWriter::new(stdout().lock()), "<stdout>")
377 }
378 }
379}
380
381#[cfg(target_pointer_width = "64")]
386mod size_asserts {
387 use rustc_data_structures::static_assert_size;
388
389 use super::types::*;
390 static_assert_size!(AssocItemConstraint, 112);
392 static_assert_size!(Crate, 184);
393 static_assert_size!(ExternalCrate, 48);
394 static_assert_size!(FunctionPointer, 168);
395 static_assert_size!(GenericArg, 80);
396 static_assert_size!(GenericArgs, 104);
397 static_assert_size!(GenericBound, 72);
398 static_assert_size!(GenericParamDef, 136);
399 static_assert_size!(Impl, 304);
400 static_assert_size!(Item, 528 + size_of::<std::path::PathBuf>());
402 static_assert_size!(ItemSummary, 32);
403 static_assert_size!(PolyTrait, 64);
404 static_assert_size!(PreciseCapturingArg, 32);
405 static_assert_size!(TargetFeature, 80);
406 static_assert_size!(Type, 80);
407 static_assert_size!(WherePredicate, 160);
408 }