1use std::cell::RefCell;
17use std::ffi::OsString;
18use std::fs::File;
19use std::io::{self, Write as _};
20use std::iter::once;
21use std::marker::PhantomData;
22use std::path::{Component, Path, PathBuf};
23use std::rc::{Rc, Weak};
24use std::str::FromStr;
25use std::{fmt, fs};
26
27use indexmap::IndexMap;
28use regex::Regex;
29use rustc_ast::join_path_syms;
30use rustc_data_structures::flock;
31use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
32use rustc_middle::ty::TyCtxt;
33use rustc_middle::ty::fast_reject::DeepRejectCtxt;
34use rustc_span::Symbol;
35use rustc_span::def_id::DefId;
36use serde::de::DeserializeOwned;
37use serde::ser::SerializeSeq;
38use serde::{Deserialize, Serialize, Serializer};
39
40use super::{Context, RenderMode, collect_paths_for_type, ensure_trailing_slash};
41use crate::clean::{Crate, Item, ItemId, ItemKind};
42use crate::config::{EmitType, PathToParts, RenderOptions, ShouldMerge};
43use crate::docfs::PathError;
44use crate::error::Error;
45use crate::formats::Impl;
46use crate::formats::item_type::ItemType;
47use crate::html::format::{print_impl, print_path};
48use crate::html::layout;
49use crate::html::render::ordered_json::{EscapedJson, OrderedJson};
50use crate::html::render::search_index::{SerializedSearchIndex, build_index};
51use crate::html::render::sorted_template::{self, FileFormat, SortedTemplate};
52use crate::html::render::{AssocItemLink, ImplRenderingParameters, StylePath};
53use crate::html::static_files::{self, suffix_path};
54use crate::visit::DocVisitor;
55use crate::{try_err, try_none};
56
57pub(crate) fn write_shared(
58 cx: &mut Context<'_>,
59 krate: &Crate,
60 opt: &RenderOptions,
61 tcx: TyCtxt<'_>,
62) -> Result<(), Error> {
63 cx.shared.fs.set_sync_only(true);
65 let lock_file = cx.dst.join(".lock");
66 let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file);
68
69 let search_index =
70 build_index(krate, &mut cx.shared.cache, tcx, &cx.dst, &cx.shared.resource_suffix)?;
71
72 let crate_name = krate.name(cx.tcx());
73 let crate_name = crate_name.as_str(); let crate_name_json = OrderedJson::serialize(crate_name).unwrap(); let external_crates = hack_get_external_crate_names(&cx.dst, &cx.shared.resource_suffix)?;
76 let info = CrateInfo {
77 version: CrateInfoVersion::V2,
78 src_files_js: SourcesPart::get(cx, &crate_name_json)?,
79 search_index,
80 all_crates: AllCratesPart::get(crate_name_json.clone(), &cx.shared.resource_suffix)?,
81 crates_index: CratesIndexPart::get(crate_name, &external_crates)?,
82 trait_impl: TraitAliasPart::get(cx, &crate_name_json)?,
83 type_impl: TypeAliasPart::get(cx, krate, &crate_name_json)?,
84 };
85
86 if let Some(parts_out_dir) = &opt.parts_out_dir {
87 create_parents(&parts_out_dir.0)?;
88 try_err!(
89 fs::write(&parts_out_dir.0, serde_json::to_string(&info).unwrap()),
90 &parts_out_dir.0
91 );
92 }
93
94 let mut crates = CrateInfo::read_many(&opt.include_parts_dir)?;
95 crates.push(info);
96
97 if opt.should_merge.write_rendered_cci {
98 write_not_crate_specific(
99 &crates,
100 &cx.dst,
101 opt,
102 &cx.shared.style_files,
103 cx.shared.layout.css_file_extension.as_deref(),
104 &cx.shared.resource_suffix,
105 cx.info.include_sources,
106 )?;
107 match &opt.index_page {
108 Some(index_page) if opt.enable_index_page => {
109 let mut md_opts = opt.clone();
110 md_opts.output = cx.dst.clone();
111 md_opts.external_html = cx.shared.layout.external_html.clone();
112 try_err!(
113 crate::markdown::render_and_write(index_page, md_opts, cx.shared.edition()),
114 &index_page
115 );
116 }
117 None if opt.enable_index_page => {
118 write_rendered_cci::<CratesIndexPart, _>(
119 || CratesIndexPart::blank(cx),
120 &cx.dst,
121 &crates,
122 &opt.should_merge,
123 )?;
124 }
125 _ => {} }
127 }
128
129 cx.shared.fs.set_sync_only(false);
130 Ok(())
131}
132
133pub(crate) fn write_not_crate_specific(
137 crates: &[CrateInfo],
138 dst: &Path,
139 opt: &RenderOptions,
140 style_files: &[StylePath],
141 css_file_extension: Option<&Path>,
142 resource_suffix: &str,
143 include_sources: bool,
144) -> Result<(), Error> {
145 write_rendered_cross_crate_info(crates, dst, opt, include_sources, resource_suffix)?;
146 write_static_files(dst, opt, style_files, css_file_extension, resource_suffix)?;
147 Ok(())
148}
149
150fn write_rendered_cross_crate_info(
151 crates: &[CrateInfo],
152 dst: &Path,
153 opt: &RenderOptions,
154 include_sources: bool,
155 resource_suffix: &str,
156) -> Result<(), Error> {
157 let m = &opt.should_merge;
158 if opt.should_emit_crate() {
159 if include_sources {
160 write_rendered_cci::<SourcesPart, _>(SourcesPart::blank, dst, crates, m)?;
161 }
162 crates
163 .iter()
164 .fold(SerializedSearchIndex::default(), |a, b| a.union(&b.search_index))
165 .sort()
166 .write_to(dst, resource_suffix)?;
167 write_rendered_cci::<AllCratesPart, _>(AllCratesPart::blank, dst, crates, m)?;
168 }
169 write_rendered_cci::<TraitAliasPart, _>(TraitAliasPart::blank, dst, crates, m)?;
170 write_rendered_cci::<TypeAliasPart, _>(TypeAliasPart::blank, dst, crates, m)?;
171 Ok(())
172}
173
174fn write_static_files(
177 dst: &Path,
178 opt: &RenderOptions,
179 style_files: &[StylePath],
180 css_file_extension: Option<&Path>,
181 resource_suffix: &str,
182) -> Result<(), Error> {
183 let static_dir = dst.join("static.files");
184 try_err!(fs::create_dir_all(&static_dir), &static_dir);
185
186 for entry in style_files {
188 let theme = entry.basename()?;
189 let extension =
190 try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path);
191
192 if matches!(theme.as_str(), "light" | "dark" | "ayu") {
194 continue;
195 }
196
197 let bytes = try_err!(fs::read(&entry.path), &entry.path);
198 let filename = format!("{theme}{resource_suffix}.{extension}");
199 let dst_filename = dst.join(filename);
200 try_err!(fs::write(&dst_filename, bytes), &dst_filename);
201 }
202
203 if let Some(css) = css_file_extension {
206 let buffer = try_err!(fs::read_to_string(css), css);
207 let path = static_files::suffix_path("theme.css", resource_suffix);
208 let dst_path = dst.join(path);
209 try_err!(fs::write(&dst_path, buffer), &dst_path);
210 }
211
212 if opt.emit.is_empty() || opt.emit.contains(&EmitType::Toolchain) {
213 static_files::for_each(|f: &static_files::StaticFile| {
214 let filename = static_dir.join(f.output_filename());
215 let contents: &[u8] =
216 if opt.disable_minification { f.src_bytes } else { f.minified_bytes };
217 fs::write(&filename, contents).map_err(|e| PathError::new(e, &filename))
218 })?;
219 }
220
221 Ok(())
222}
223
224#[derive(Serialize, Deserialize, Clone, Debug)]
226pub(crate) struct CrateInfo {
227 version: CrateInfoVersion,
228 src_files_js: PartsAndLocations<SourcesPart>,
229 search_index: SerializedSearchIndex,
230 all_crates: PartsAndLocations<AllCratesPart>,
231 crates_index: PartsAndLocations<CratesIndexPart>,
232 trait_impl: PartsAndLocations<TraitAliasPart>,
233 type_impl: PartsAndLocations<TypeAliasPart>,
234}
235
236impl CrateInfo {
237 pub(crate) fn read_many(parts_paths: &[PathToParts]) -> Result<Vec<Self>, Error> {
239 parts_paths
240 .iter()
241 .map(|parts_path| {
242 let path = &parts_path.0;
243 let parts = try_err!(fs::read(path), &path);
244 let parts: CrateInfo = try_err!(serde_json::from_slice(&parts), &path);
245 Ok::<_, Error>(parts)
246 })
247 .collect::<Result<Vec<CrateInfo>, Error>>()
248 }
249}
250
251#[derive(Serialize, Deserialize, Clone, Debug)]
259enum CrateInfoVersion {
260 V2,
261}
262
263#[derive(Serialize, Deserialize, Debug, Clone)]
265#[serde(transparent)]
266struct PartsAndLocations<P> {
267 parts: Vec<(PathBuf, P)>,
268}
269
270impl<P> Default for PartsAndLocations<P> {
271 fn default() -> Self {
272 Self { parts: Vec::default() }
273 }
274}
275
276impl<T, U> PartsAndLocations<Part<T, U>> {
277 fn push(&mut self, path: PathBuf, item: U) {
278 self.parts.push((path, Part { _artifact: PhantomData, item }));
279 }
280
281 fn with(path: PathBuf, part: U) -> Self {
283 let mut ret = Self::default();
284 ret.push(path, part);
285 ret
286 }
287}
288
289#[derive(Serialize, Deserialize, Debug, Clone)]
293#[serde(transparent)]
294struct Part<T, U> {
295 #[serde(skip)]
296 _artifact: PhantomData<T>,
297 item: U,
298}
299
300impl<T, U: fmt::Display> fmt::Display for Part<T, U> {
301 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303 write!(f, "{}", self.item)
304 }
305}
306
307trait CciPart: Sized + fmt::Display + DeserializeOwned + 'static {
309 type FileFormat: sorted_template::FileFormat;
311 fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self>;
312}
313
314#[derive(Serialize, Deserialize, Clone, Default, Debug)]
315struct AllCrates;
316type AllCratesPart = Part<AllCrates, OrderedJson>;
317impl CciPart for AllCratesPart {
318 type FileFormat = sorted_template::Js;
319 fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
320 &crate_info.all_crates
321 }
322}
323
324impl AllCratesPart {
325 fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
326 SortedTemplate::from_before_after("window.ALL_CRATES = [", "];")
327 }
328
329 fn get(
330 crate_name_json: OrderedJson,
331 resource_suffix: &str,
332 ) -> Result<PartsAndLocations<Self>, Error> {
333 let path = suffix_path("crates.js", resource_suffix);
336 Ok(PartsAndLocations::with(path, crate_name_json))
337 }
338}
339
340fn hack_get_external_crate_names(
347 doc_root: &Path,
348 resource_suffix: &str,
349) -> Result<Vec<String>, Error> {
350 let path = doc_root.join(suffix_path("crates.js", resource_suffix));
351 let Ok(content) = fs::read_to_string(&path) else {
352 return Ok(Vec::default());
354 };
355 let regex = Regex::new(r"\[.*\]").unwrap();
358 let Some(content) = regex.find(&content) else {
359 return Err(Error::new("could not find crates list in crates.js", path));
360 };
361 let content: Vec<String> = try_err!(serde_json::from_str(content.as_str()), &path);
362 Ok(content)
363}
364
365#[derive(Serialize, Deserialize, Clone, Default, Debug)]
366struct CratesIndex;
367type CratesIndexPart = Part<CratesIndex, String>;
368impl CciPart for CratesIndexPart {
369 type FileFormat = sorted_template::Html;
370 fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
371 &crate_info.crates_index
372 }
373}
374
375impl CratesIndexPart {
376 fn blank(cx: &Context<'_>) -> SortedTemplate<<Self as CciPart>::FileFormat> {
377 let page = layout::Page {
378 title: "Index of crates",
379 short_title: "Crates",
380 css_class: "mod sys",
381 root_path: "./",
382 static_root_path: cx.shared.static_root_path.as_deref(),
383 description: "List of crates",
384 resource_suffix: &cx.shared.resource_suffix,
385 rust_logo: true,
386 };
387 let layout = &cx.shared.layout;
388 let style_files = &cx.shared.style_files;
389 const DELIMITER: &str = "\u{FFFC}"; let content = format!(
391 "<div class=\"main-heading\">\
392 <h1>List of all crates</h1>\
393 <rustdoc-toolbar></rustdoc-toolbar>\
394 </div>\
395 <ul class=\"all-items\">{DELIMITER}</ul>"
396 );
397 let template = layout::render(layout, &page, "", content, style_files);
398 SortedTemplate::from_template(&template, DELIMITER)
399 .expect("Object Replacement Character (U+FFFC) should not appear in the --index-page")
400 }
401
402 fn get(crate_name: &str, external_crates: &[String]) -> Result<PartsAndLocations<Self>, Error> {
404 let mut ret = PartsAndLocations::default();
405 let path = Path::new("index.html");
406 for crate_name in external_crates.iter().map(|s| s.as_str()).chain(once(crate_name)) {
407 let part = format!(
408 "<li><a href=\"{trailing_slash}index.html\">{crate_name}</a></li>",
409 trailing_slash = ensure_trailing_slash(crate_name),
410 );
411 ret.push(path.to_path_buf(), part);
412 }
413 Ok(ret)
414 }
415}
416
417#[derive(Serialize, Deserialize, Clone, Default, Debug)]
418struct Sources;
419type SourcesPart = Part<Sources, EscapedJson>;
420impl CciPart for SourcesPart {
421 type FileFormat = sorted_template::Js;
422 fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
423 &crate_info.src_files_js
424 }
425}
426
427impl SourcesPart {
428 fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
429 SortedTemplate::from_before_after(r"createSrcSidebar('[", r"]');")
433 }
434
435 fn get(cx: &Context<'_>, crate_name: &OrderedJson) -> Result<PartsAndLocations<Self>, Error> {
436 let hierarchy = Rc::new(Hierarchy::default());
437 cx.shared
438 .local_sources
439 .iter()
440 .filter_map(|p| p.0.strip_prefix(&cx.shared.src_root).ok())
441 .for_each(|source| hierarchy.add_path(source));
442 let path = suffix_path("src-files.js", &cx.shared.resource_suffix);
443 let hierarchy = hierarchy.to_json_string();
444 let part = OrderedJson::array_unsorted([crate_name, &hierarchy]);
445 let part = EscapedJson::from(part);
446 Ok(PartsAndLocations::with(path, part))
447 }
448}
449
450#[derive(Debug, Default)]
452struct Hierarchy {
453 parent: Weak<Self>,
454 elem: OsString,
455 children: RefCell<FxIndexMap<OsString, Rc<Self>>>,
456 elems: RefCell<FxIndexSet<OsString>>,
457}
458
459impl Hierarchy {
460 fn with_parent(elem: OsString, parent: &Rc<Self>) -> Self {
461 Self { elem, parent: Rc::downgrade(parent), ..Self::default() }
462 }
463
464 fn to_json_string(&self) -> OrderedJson {
465 let subs = self.children.borrow();
466 let files = self.elems.borrow();
467 let name = OrderedJson::serialize(self.elem.to_str().expect("invalid osstring conversion"))
468 .unwrap();
469 let mut out = Vec::from([name]);
470 if !subs.is_empty() || !files.is_empty() {
471 let subs = subs.iter().map(|(_, s)| s.to_json_string());
472 out.push(OrderedJson::array_sorted(subs));
473 }
474 if !files.is_empty() {
475 let files = files
476 .iter()
477 .map(|s| OrderedJson::serialize(s.to_str().expect("invalid osstring")).unwrap());
478 out.push(OrderedJson::array_sorted(files));
479 }
480 OrderedJson::array_unsorted(out)
481 }
482
483 fn add_path(self: &Rc<Self>, path: &Path) {
484 let mut h = Rc::clone(self);
485 let mut elems = path
486 .components()
487 .filter_map(|s| match s {
488 Component::Normal(s) => Some(s.to_owned()),
489 Component::ParentDir => Some(OsString::from("..")),
490 _ => None,
491 })
492 .peekable();
493 loop {
494 let cur_elem = elems.next().expect("empty file path");
495 if cur_elem == ".." {
496 if let Some(parent) = h.parent.upgrade() {
497 h = parent;
498 }
499 continue;
500 }
501 if elems.peek().is_none() {
502 h.elems.borrow_mut().insert(cur_elem);
503 break;
504 } else {
505 let entry = Rc::clone(
506 h.children
507 .borrow_mut()
508 .entry(cur_elem.clone())
509 .or_insert_with(|| Rc::new(Self::with_parent(cur_elem, &h))),
510 );
511 h = entry;
512 }
513 }
514 }
515}
516
517#[derive(Serialize, Deserialize, Clone, Default, Debug)]
518struct TypeAlias;
519type TypeAliasPart = Part<TypeAlias, OrderedJson>;
520impl CciPart for TypeAliasPart {
521 type FileFormat = sorted_template::Js;
522 fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
523 &crate_info.type_impl
524 }
525}
526
527impl TypeAliasPart {
528 fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
529 SortedTemplate::from_before_after(
530 r"(function() {
531 var type_impls = Object.fromEntries([",
532 r"]);
533 if (window.register_type_impls) {
534 window.register_type_impls(type_impls);
535 } else {
536 window.pending_type_impls = type_impls;
537 }
538})()",
539 )
540 }
541
542 fn get(
543 cx: &mut Context<'_>,
544 krate: &Crate,
545 crate_name_json: &OrderedJson,
546 ) -> Result<PartsAndLocations<Self>, Error> {
547 let mut path_parts = PartsAndLocations::default();
548
549 let mut type_impl_collector = TypeImplCollector {
550 aliased_types: IndexMap::default(),
551 visited_aliases: FxHashSet::default(),
552 cx,
553 };
554 DocVisitor::visit_crate(&mut type_impl_collector, krate);
555 let cx = type_impl_collector.cx;
556 let aliased_types = type_impl_collector.aliased_types;
557 for aliased_type in aliased_types.values() {
558 let impls = aliased_type.impl_.values().filter_map(
559 |AliasedTypeImpl { impl_, type_aliases }| {
560 let mut ret: Option<AliasSerializableImpl> = None;
561 for &(type_alias_fqp, type_alias_item) in type_aliases {
565 cx.id_map.borrow_mut().clear();
566 cx.deref_id_map.borrow_mut().clear();
567 let type_alias_fqp = join_path_syms(type_alias_fqp);
568 if let Some(ret) = &mut ret {
569 ret.aliases.push(type_alias_fqp);
570 } else {
571 let target_did = impl_
572 .inner_impl()
573 .trait_
574 .as_ref()
575 .map(|trait_| trait_.def_id())
576 .or_else(|| impl_.inner_impl().for_.def_id(&cx.shared.cache));
577 let provided_methods;
578 let assoc_link = if let Some(target_did) = target_did {
579 provided_methods =
580 impl_.inner_impl().provided_trait_methods(cx.tcx());
581 AssocItemLink::GotoSource(
582 ItemId::DefId(target_did),
583 &provided_methods,
584 )
585 } else {
586 AssocItemLink::Anchor(None)
587 };
588 let text = super::render_impl(
589 cx,
590 impl_,
591 type_alias_item,
592 assoc_link,
593 RenderMode::Normal,
594 None,
595 &[],
596 ImplRenderingParameters {
597 show_def_docs: true,
598 show_default_items: true,
599 show_non_assoc_items: true,
600 toggle_open_by_default: true,
601 },
602 )
603 .to_string();
604 let trait_ = impl_
606 .inner_impl()
607 .trait_
608 .as_ref()
609 .map(|trait_| format!("{:#}", print_path(trait_, cx)));
610 ret = Some(AliasSerializableImpl {
611 text,
612 trait_,
613 aliases: vec![type_alias_fqp],
614 })
615 }
616 }
617 ret
618 },
619 );
620
621 let mut path = PathBuf::from("type.impl");
622 for component in &aliased_type.target_fqp[..aliased_type.target_fqp.len() - 1] {
623 path.push(component.as_str());
624 }
625 let aliased_item_type = aliased_type.target_type;
626 path.push(format!(
627 "{aliased_item_type}.{}.js",
628 aliased_type.target_fqp[aliased_type.target_fqp.len() - 1]
629 ));
630
631 let part = OrderedJson::array_sorted(
632 impls.map(|impl_| OrderedJson::serialize(impl_).unwrap()),
633 );
634 path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part]));
635 }
636 Ok(path_parts)
637 }
638}
639
640#[derive(Serialize, Deserialize, Clone, Default, Debug)]
641struct TraitAlias;
642type TraitAliasPart = Part<TraitAlias, OrderedJson>;
643impl CciPart for TraitAliasPart {
644 type FileFormat = sorted_template::Js;
645 fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
646 &crate_info.trait_impl
647 }
648}
649
650impl TraitAliasPart {
651 fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
652 SortedTemplate::from_before_after(
653 r"(function() {
654 var implementors = Object.fromEntries([",
655 r"]);
656 if (window.register_implementors) {
657 window.register_implementors(implementors);
658 } else {
659 window.pending_implementors = implementors;
660 }
661})()",
662 )
663 }
664
665 fn get(
666 cx: &Context<'_>,
667 crate_name_json: &OrderedJson,
668 ) -> Result<PartsAndLocations<Self>, Error> {
669 let cache = &cx.shared.cache;
670 let mut path_parts = PartsAndLocations::default();
671 for (&did, imps) in &cache.implementors {
674 let (remote_path, remote_item_type) = match cache.exact_paths.get(&did) {
682 Some(p) => match cache.paths.get(&did).or_else(|| cache.external_paths.get(&did)) {
683 Some((_, t)) => (p, t),
684 None => continue,
685 },
686 None => match cache.external_paths.get(&did) {
687 Some((p, t)) => (p, t),
688 None => continue,
689 },
690 };
691
692 let mut implementors = imps
693 .iter()
694 .filter_map(|imp| {
695 if imp.impl_item.item_id.krate() == did.krate
703 || !imp.impl_item.item_id.is_local()
704 {
705 None
706 } else {
707 Some(Implementor {
708 text: print_impl(imp.inner_impl(), false, cx).to_string(),
709 synthetic: imp.inner_impl().kind.is_auto(),
710 types: collect_paths_for_type(&imp.inner_impl().for_, cache),
711 })
712 }
713 })
714 .peekable();
715
716 if implementors.peek().is_none() && !cache.paths.contains_key(&did) {
720 continue;
721 }
722
723 let mut path = PathBuf::from("trait.impl");
724 for component in &remote_path[..remote_path.len() - 1] {
725 path.push(component.as_str());
726 }
727 path.push(format!("{remote_item_type}.{}.js", remote_path[remote_path.len() - 1]));
728
729 let part = OrderedJson::array_sorted(
730 implementors.map(|implementor| OrderedJson::serialize(implementor).unwrap()),
731 );
732 path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part]));
733 }
734 Ok(path_parts)
735 }
736}
737
738struct Implementor {
739 text: String,
740 synthetic: bool,
741 types: Vec<String>,
742}
743
744impl Serialize for Implementor {
745 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
746 where
747 S: Serializer,
748 {
749 let mut seq = serializer.serialize_seq(None)?;
750 seq.serialize_element(&self.text)?;
751 if self.synthetic {
752 seq.serialize_element(&1)?;
753 seq.serialize_element(&self.types)?;
754 }
755 seq.end()
756 }
757}
758
759struct TypeImplCollector<'cx, 'cache, 'item> {
767 aliased_types: IndexMap<DefId, AliasedType<'cache, 'item>>,
769 visited_aliases: FxHashSet<DefId>,
770 cx: &'cache Context<'cx>,
771}
772
773struct AliasedType<'cache, 'item> {
789 target_fqp: &'cache [Symbol],
791 target_type: ItemType,
792 impl_: IndexMap<ItemId, AliasedTypeImpl<'cache, 'item>>,
795}
796
797struct AliasedTypeImpl<'cache, 'item> {
802 impl_: &'cache Impl,
803 type_aliases: Vec<(&'cache [Symbol], &'item Item)>,
804}
805
806impl<'item> DocVisitor<'item> for TypeImplCollector<'_, '_, 'item> {
807 fn visit_item(&mut self, it: &'item Item) {
808 self.visit_item_recur(it);
809 let cache = &self.cx.shared.cache;
810 let ItemKind::TypeAliasItem(ref t) = it.kind else { return };
811 let Some(self_did) = it.item_id.as_def_id() else { return };
812 if !self.visited_aliases.insert(self_did) {
813 return;
814 }
815 let Some(target_did) = t.type_.def_id(cache) else { return };
816 let get_extern = { || cache.external_paths.get(&target_did) };
817 let Some(&(ref target_fqp, target_type)) = cache.paths.get(&target_did).or_else(get_extern)
818 else {
819 return;
820 };
821 let aliased_type = self.aliased_types.entry(target_did).or_insert_with(|| {
822 let impl_ = cache
823 .impls
824 .get(&target_did)
825 .into_iter()
826 .flatten()
827 .map(|impl_| {
828 (impl_.impl_item.item_id, AliasedTypeImpl { impl_, type_aliases: Vec::new() })
829 })
830 .collect();
831 AliasedType { target_fqp: &target_fqp[..], target_type, impl_ }
832 });
833 let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) };
834 let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) else {
835 return;
836 };
837 let aliased_ty = self.cx.tcx().type_of(self_did).skip_binder();
838 let mut seen_impls: FxHashSet<ItemId> =
842 cache.impls.get(&self_did).into_iter().flatten().map(|i| i.impl_item.item_id).collect();
843 for (impl_item_id, aliased_type_impl) in &mut aliased_type.impl_ {
844 let Some(impl_did) = impl_item_id.as_def_id() else { continue };
851 let for_ty = self.cx.tcx().type_of(impl_did).skip_binder();
852 let reject_cx = DeepRejectCtxt::relate_infer_infer(self.cx.tcx());
853 if !reject_cx.types_may_unify(aliased_ty, for_ty) {
854 continue;
855 }
856 if !seen_impls.insert(*impl_item_id) {
858 continue;
859 }
860 aliased_type_impl.type_aliases.push((&self_fqp[..], it));
862 }
863 }
864}
865
866struct AliasSerializableImpl {
868 text: String,
869 trait_: Option<String>,
870 aliases: Vec<String>,
871}
872
873impl Serialize for AliasSerializableImpl {
874 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
875 where
876 S: Serializer,
877 {
878 let mut seq = serializer.serialize_seq(None)?;
879 seq.serialize_element(&self.text)?;
880 if let Some(trait_) = &self.trait_ {
881 seq.serialize_element(trait_)?;
882 } else {
883 seq.serialize_element(&0)?;
884 }
885 for type_ in &self.aliases {
886 seq.serialize_element(type_)?;
887 }
888 seq.end()
889 }
890}
891
892fn get_path_parts<T: CciPart>(
893 dst: &Path,
894 crates_info: &[CrateInfo],
895) -> FxIndexMap<PathBuf, Vec<String>> {
896 let mut templates: FxIndexMap<PathBuf, Vec<String>> = FxIndexMap::default();
897 crates_info.iter().flat_map(|crate_info| T::from_crate_info(crate_info).parts.iter()).for_each(
898 |(path, part)| {
899 let path = dst.join(path);
900 let part = part.to_string();
901 templates.entry(path).or_default().push(part);
902 },
903 );
904 templates
905}
906
907fn create_parents(path: &Path) -> Result<(), Error> {
909 let parent = path.parent().expect("should not have an empty path here");
910 try_err!(fs::create_dir_all(parent), parent);
911 Ok(())
912}
913
914fn read_template_or_blank<F, T: FileFormat>(
916 mut make_blank: F,
917 path: &Path,
918 should_merge: &ShouldMerge,
919) -> Result<SortedTemplate<T>, Error>
920where
921 F: FnMut() -> SortedTemplate<T>,
922{
923 if !should_merge.read_rendered_cci {
924 return Ok(make_blank());
925 }
926 match fs::read_to_string(path) {
927 Ok(template) => Ok(try_err!(SortedTemplate::from_str(&template), &path)),
928 Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(make_blank()),
929 Err(e) => Err(Error::new(e, path)),
930 }
931}
932
933fn write_rendered_cci<T: CciPart, F>(
935 mut make_blank: F,
936 dst: &Path,
937 crates_info: &[CrateInfo],
938 should_merge: &ShouldMerge,
939) -> Result<(), Error>
940where
941 F: FnMut() -> SortedTemplate<T::FileFormat>,
942{
943 for (path, parts) in get_path_parts::<T>(dst, crates_info) {
945 create_parents(&path)?;
946 let mut template =
948 read_template_or_blank::<_, T::FileFormat>(&mut make_blank, &path, should_merge)?;
949 for part in parts {
950 template.append(part);
951 }
952 let mut file = try_err!(File::create_buffered(&path), &path);
953 try_err!(write!(file, "{template}"), &path);
954 try_err!(file.flush(), &path);
955 }
956 Ok(())
957}
958
959#[cfg(test)]
960mod tests;