1pub(crate) mod search_index;
27
28#[cfg(test)]
29mod tests;
30
31mod context;
32mod ordered_json;
33mod print_item;
34pub(crate) mod sidebar;
35mod sorted_template;
36mod span_map;
37mod type_layout;
38mod write_shared;
39
40use std::collections::VecDeque;
41use std::fmt::{self, Display as _, Write};
42use std::iter::Peekable;
43use std::path::PathBuf;
44use std::{fs, str};
45
46use rinja::Template;
47use rustc_attr_parsing::{
48 ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince,
49};
50use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
51use rustc_hir::Mutability;
52use rustc_hir::def_id::{DefId, DefIdSet};
53use rustc_middle::ty::print::PrintTraitRefExt;
54use rustc_middle::ty::{self, TyCtxt};
55use rustc_span::symbol::{Symbol, sym};
56use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName};
57use serde::ser::SerializeMap;
58use serde::{Serialize, Serializer};
59use tracing::{debug, info};
60
61pub(crate) use self::context::*;
62pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources};
63pub(crate) use self::write_shared::*;
64use crate::clean::{self, ItemId, RenderedLink};
65use crate::display::{Joined as _, MaybeDisplay as _};
66use crate::error::Error;
67use crate::formats::Impl;
68use crate::formats::cache::Cache;
69use crate::formats::item_type::ItemType;
70use crate::html::escape::Escape;
71use crate::html::format::{
72 Ending, HrefError, PrintWithSpace, href, join_with_double_colon, print_abi_with_space,
73 print_constness_with_space, print_default_space, print_generic_bounds, print_where_clause,
74 visibility_print_with_space, write_str,
75};
76use crate::html::markdown::{
77 HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine,
78};
79use crate::html::static_files::SCRAPE_EXAMPLES_HELP_MD;
80use crate::html::{highlight, sources};
81use crate::scrape_examples::{CallData, CallLocation};
82use crate::{DOC_RUST_LANG_ORG_VERSION, try_none};
83
84pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display {
85 fmt::from_fn(move |f| {
86 if !v.ends_with('/') && !v.is_empty() { write!(f, "{v}/") } else { f.write_str(v) }
87 })
88}
89
90#[derive(Copy, Clone)]
93pub(crate) enum AssocItemRender<'a> {
94 All,
95 DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool },
96}
97
98#[derive(Copy, Clone, PartialEq)]
101pub(crate) enum RenderMode {
102 Normal,
103 ForDeref { mut_: bool },
104}
105
106#[derive(Debug)]
112pub(crate) struct IndexItem {
113 pub(crate) ty: ItemType,
114 pub(crate) defid: Option<DefId>,
115 pub(crate) name: Symbol,
116 pub(crate) path: String,
117 pub(crate) desc: String,
118 pub(crate) parent: Option<DefId>,
119 pub(crate) parent_idx: Option<isize>,
120 pub(crate) exact_path: Option<String>,
121 pub(crate) impl_id: Option<DefId>,
122 pub(crate) search_type: Option<IndexItemFunctionType>,
123 pub(crate) aliases: Box<[Symbol]>,
124 pub(crate) deprecation: Option<Deprecation>,
125}
126
127#[derive(Debug, Eq, PartialEq)]
129pub(crate) struct RenderType {
130 id: Option<RenderTypeId>,
131 generics: Option<Vec<RenderType>>,
132 bindings: Option<Vec<(RenderTypeId, Vec<RenderType>)>>,
133}
134
135impl RenderType {
136 pub fn write_to_string(&self, string: &mut String) {
141 fn write_optional_id(id: Option<RenderTypeId>, string: &mut String) {
142 match id {
144 Some(id) => id.write_to_string(string),
145 None => string.push('`'),
146 }
147 }
148 if self.generics.is_some() || self.bindings.is_some() {
152 string.push('{');
153 write_optional_id(self.id, string);
154 string.push('{');
155 for generic in self.generics.as_deref().unwrap_or_default() {
156 generic.write_to_string(string);
157 }
158 string.push('}');
159 if self.bindings.is_some() {
160 string.push('{');
161 for binding in self.bindings.as_deref().unwrap_or_default() {
162 string.push('{');
163 binding.0.write_to_string(string);
164 string.push('{');
165 for constraint in &binding.1[..] {
166 constraint.write_to_string(string);
167 }
168 string.push_str("}}");
169 }
170 string.push('}');
171 }
172 string.push('}');
173 } else {
174 write_optional_id(self.id, string);
175 }
176 }
177}
178
179#[derive(Clone, Copy, Debug, Eq, PartialEq)]
180pub(crate) enum RenderTypeId {
181 DefId(DefId),
182 Primitive(clean::PrimitiveType),
183 AssociatedType(Symbol),
184 Index(isize),
185 Mut,
186}
187
188impl RenderTypeId {
189 pub fn write_to_string(&self, string: &mut String) {
190 let id: i32 = match &self {
191 RenderTypeId::Index(idx) if *idx >= 0 => (idx + 1isize).try_into().unwrap(),
194 RenderTypeId::Index(idx) => (*idx).try_into().unwrap(),
196 _ => panic!("must convert render types to indexes before serializing"),
197 };
198 search_index::encode::write_vlqhex_to_string(id, string);
199 }
200}
201
202#[derive(Debug, Eq, PartialEq)]
204pub(crate) struct IndexItemFunctionType {
205 inputs: Vec<RenderType>,
206 output: Vec<RenderType>,
207 where_clause: Vec<Vec<RenderType>>,
208 param_names: Vec<Symbol>,
209}
210
211impl IndexItemFunctionType {
212 pub fn write_to_string<'a>(
213 &'a self,
214 string: &mut String,
215 backref_queue: &mut VecDeque<&'a IndexItemFunctionType>,
216 ) {
217 assert!(backref_queue.len() <= 16);
218 let has_missing = self
221 .inputs
222 .iter()
223 .chain(self.output.iter())
224 .any(|i| i.id.is_none() && i.generics.is_none());
225 if has_missing {
226 string.push('`');
227 } else if let Some(idx) = backref_queue.iter().position(|other| *other == self) {
228 string.push(
231 char::try_from('0' as u32 + u32::try_from(idx).unwrap())
232 .expect("last possible value is '?'"),
233 );
234 } else {
235 backref_queue.push_front(self);
236 if backref_queue.len() > 16 {
237 backref_queue.pop_back();
238 }
239 string.push('{');
240 match &self.inputs[..] {
241 [one] if one.generics.is_none() && one.bindings.is_none() => {
242 one.write_to_string(string);
243 }
244 _ => {
245 string.push('{');
246 for item in &self.inputs[..] {
247 item.write_to_string(string);
248 }
249 string.push('}');
250 }
251 }
252 match &self.output[..] {
253 [] if self.where_clause.is_empty() => {}
254 [one] if one.generics.is_none() && one.bindings.is_none() => {
255 one.write_to_string(string);
256 }
257 _ => {
258 string.push('{');
259 for item in &self.output[..] {
260 item.write_to_string(string);
261 }
262 string.push('}');
263 }
264 }
265 for constraint in &self.where_clause {
266 if let [one] = &constraint[..]
267 && one.generics.is_none()
268 && one.bindings.is_none()
269 {
270 one.write_to_string(string);
271 } else {
272 string.push('{');
273 for item in &constraint[..] {
274 item.write_to_string(string);
275 }
276 string.push('}');
277 }
278 }
279 string.push('}');
280 }
281 }
282}
283
284#[derive(Debug, Clone)]
285pub(crate) struct StylePath {
286 pub(crate) path: PathBuf,
288}
289
290impl StylePath {
291 pub(crate) fn basename(&self) -> Result<String, Error> {
292 Ok(try_none!(try_none!(self.path.file_stem(), &self.path).to_str(), &self.path).to_string())
293 }
294}
295
296#[derive(Debug, Eq, PartialEq, Hash)]
297struct ItemEntry {
298 url: String,
299 name: String,
300}
301
302impl ItemEntry {
303 fn new(mut url: String, name: String) -> ItemEntry {
304 while url.starts_with('/') {
305 url.remove(0);
306 }
307 ItemEntry { url, name }
308 }
309}
310
311impl ItemEntry {
312 pub(crate) fn print(&self) -> impl fmt::Display {
313 fmt::from_fn(move |f| write!(f, "<a href=\"{}\">{}</a>", self.url, Escape(&self.name)))
314 }
315}
316
317impl PartialOrd for ItemEntry {
318 fn partial_cmp(&self, other: &ItemEntry) -> Option<::std::cmp::Ordering> {
319 Some(self.cmp(other))
320 }
321}
322
323impl Ord for ItemEntry {
324 fn cmp(&self, other: &ItemEntry) -> ::std::cmp::Ordering {
325 self.name.cmp(&other.name)
326 }
327}
328
329#[derive(Debug)]
330struct AllTypes {
331 structs: FxIndexSet<ItemEntry>,
332 enums: FxIndexSet<ItemEntry>,
333 unions: FxIndexSet<ItemEntry>,
334 primitives: FxIndexSet<ItemEntry>,
335 traits: FxIndexSet<ItemEntry>,
336 macros: FxIndexSet<ItemEntry>,
337 functions: FxIndexSet<ItemEntry>,
338 type_aliases: FxIndexSet<ItemEntry>,
339 statics: FxIndexSet<ItemEntry>,
340 constants: FxIndexSet<ItemEntry>,
341 attribute_macros: FxIndexSet<ItemEntry>,
342 derive_macros: FxIndexSet<ItemEntry>,
343 trait_aliases: FxIndexSet<ItemEntry>,
344}
345
346impl AllTypes {
347 fn new() -> AllTypes {
348 let new_set = |cap| FxIndexSet::with_capacity_and_hasher(cap, Default::default());
349 AllTypes {
350 structs: new_set(100),
351 enums: new_set(100),
352 unions: new_set(100),
353 primitives: new_set(26),
354 traits: new_set(100),
355 macros: new_set(100),
356 functions: new_set(100),
357 type_aliases: new_set(100),
358 statics: new_set(100),
359 constants: new_set(100),
360 attribute_macros: new_set(100),
361 derive_macros: new_set(100),
362 trait_aliases: new_set(100),
363 }
364 }
365
366 fn append(&mut self, item_name: String, item_type: &ItemType) {
367 let mut url: Vec<_> = item_name.split("::").skip(1).collect();
368 if let Some(name) = url.pop() {
369 let new_url = format!("{}/{item_type}.{name}.html", url.join("/"));
370 url.push(name);
371 let name = url.join("::");
372 match *item_type {
373 ItemType::Struct => self.structs.insert(ItemEntry::new(new_url, name)),
374 ItemType::Enum => self.enums.insert(ItemEntry::new(new_url, name)),
375 ItemType::Union => self.unions.insert(ItemEntry::new(new_url, name)),
376 ItemType::Primitive => self.primitives.insert(ItemEntry::new(new_url, name)),
377 ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)),
378 ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)),
379 ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)),
380 ItemType::TypeAlias => self.type_aliases.insert(ItemEntry::new(new_url, name)),
381 ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)),
382 ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)),
383 ItemType::ProcAttribute => {
384 self.attribute_macros.insert(ItemEntry::new(new_url, name))
385 }
386 ItemType::ProcDerive => self.derive_macros.insert(ItemEntry::new(new_url, name)),
387 ItemType::TraitAlias => self.trait_aliases.insert(ItemEntry::new(new_url, name)),
388 _ => true,
389 };
390 }
391 }
392
393 fn item_sections(&self) -> FxHashSet<ItemSection> {
394 let mut sections = FxHashSet::default();
395
396 if !self.structs.is_empty() {
397 sections.insert(ItemSection::Structs);
398 }
399 if !self.enums.is_empty() {
400 sections.insert(ItemSection::Enums);
401 }
402 if !self.unions.is_empty() {
403 sections.insert(ItemSection::Unions);
404 }
405 if !self.primitives.is_empty() {
406 sections.insert(ItemSection::PrimitiveTypes);
407 }
408 if !self.traits.is_empty() {
409 sections.insert(ItemSection::Traits);
410 }
411 if !self.macros.is_empty() {
412 sections.insert(ItemSection::Macros);
413 }
414 if !self.functions.is_empty() {
415 sections.insert(ItemSection::Functions);
416 }
417 if !self.type_aliases.is_empty() {
418 sections.insert(ItemSection::TypeAliases);
419 }
420 if !self.statics.is_empty() {
421 sections.insert(ItemSection::Statics);
422 }
423 if !self.constants.is_empty() {
424 sections.insert(ItemSection::Constants);
425 }
426 if !self.attribute_macros.is_empty() {
427 sections.insert(ItemSection::AttributeMacros);
428 }
429 if !self.derive_macros.is_empty() {
430 sections.insert(ItemSection::DeriveMacros);
431 }
432 if !self.trait_aliases.is_empty() {
433 sections.insert(ItemSection::TraitAliases);
434 }
435
436 sections
437 }
438
439 fn print(&self, f: &mut String) {
440 fn print_entries(f: &mut String, e: &FxIndexSet<ItemEntry>, kind: ItemSection) {
441 if !e.is_empty() {
442 let mut e: Vec<&ItemEntry> = e.iter().collect();
443 e.sort();
444 write_str(
445 f,
446 format_args!(
447 "<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">",
448 id = kind.id(),
449 title = kind.name(),
450 ),
451 );
452
453 for s in e.iter() {
454 write_str(f, format_args!("<li>{}</li>", s.print()));
455 }
456
457 f.push_str("</ul>");
458 }
459 }
460
461 f.push_str("<h1>List of all items</h1>");
462 print_entries(f, &self.structs, ItemSection::Structs);
465 print_entries(f, &self.enums, ItemSection::Enums);
466 print_entries(f, &self.unions, ItemSection::Unions);
467 print_entries(f, &self.primitives, ItemSection::PrimitiveTypes);
468 print_entries(f, &self.traits, ItemSection::Traits);
469 print_entries(f, &self.macros, ItemSection::Macros);
470 print_entries(f, &self.attribute_macros, ItemSection::AttributeMacros);
471 print_entries(f, &self.derive_macros, ItemSection::DeriveMacros);
472 print_entries(f, &self.functions, ItemSection::Functions);
473 print_entries(f, &self.type_aliases, ItemSection::TypeAliases);
474 print_entries(f, &self.trait_aliases, ItemSection::TraitAliases);
475 print_entries(f, &self.statics, ItemSection::Statics);
476 print_entries(f, &self.constants, ItemSection::Constants);
477 }
478}
479
480fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
481 let mut content = SCRAPE_EXAMPLES_HELP_MD.to_owned();
482 content.push_str(&format!(
483 "## More information\n\n\
484 If you want more information about this feature, please read the [corresponding chapter in \
485 the Rustdoc book]({DOC_RUST_LANG_ORG_VERSION}/rustdoc/scraped-examples.html)."
486 ));
487
488 let mut ids = IdMap::default();
489 format!(
490 "<div class=\"main-heading\">\
491 <h1>About scraped examples</h1>\
492 </div>\
493 <div>{}</div>",
494 Markdown {
495 content: &content,
496 links: &[],
497 ids: &mut ids,
498 error_codes: shared.codes,
499 edition: shared.edition(),
500 playground: &shared.playground,
501 heading_offset: HeadingOffset::H1,
502 }
503 .into_string()
504 )
505}
506
507fn document(
508 cx: &Context<'_>,
509 item: &clean::Item,
510 parent: Option<&clean::Item>,
511 heading_offset: HeadingOffset,
512) -> impl fmt::Display {
513 if let Some(ref name) = item.name {
514 info!("Documenting {name}");
515 }
516
517 fmt::from_fn(move |f| {
518 document_item_info(cx, item, parent).render_into(f).unwrap();
519 if parent.is_none() {
520 write!(f, "{}", document_full_collapsible(item, cx, heading_offset))
521 } else {
522 write!(f, "{}", document_full(item, cx, heading_offset))
523 }
524 })
525}
526
527fn render_markdown(
529 cx: &Context<'_>,
530 md_text: &str,
531 links: Vec<RenderedLink>,
532 heading_offset: HeadingOffset,
533) -> impl fmt::Display {
534 fmt::from_fn(move |f| {
535 write!(
536 f,
537 "<div class=\"docblock\">{}</div>",
538 Markdown {
539 content: md_text,
540 links: &links,
541 ids: &mut cx.id_map.borrow_mut(),
542 error_codes: cx.shared.codes,
543 edition: cx.shared.edition(),
544 playground: &cx.shared.playground,
545 heading_offset,
546 }
547 .into_string()
548 )
549 })
550}
551
552fn document_short(
555 item: &clean::Item,
556 cx: &Context<'_>,
557 link: AssocItemLink<'_>,
558 parent: &clean::Item,
559 show_def_docs: bool,
560) -> impl fmt::Display {
561 fmt::from_fn(move |f| {
562 document_item_info(cx, item, Some(parent)).render_into(f).unwrap();
563 if !show_def_docs {
564 return Ok(());
565 }
566 let s = item.doc_value();
567 if !s.is_empty() {
568 let (mut summary_html, has_more_content) =
569 MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content();
570
571 let link = if has_more_content {
572 let link = fmt::from_fn(|f| {
573 write!(
574 f,
575 " <a{}>Read more</a>",
576 assoc_href_attr(item, link, cx).maybe_display()
577 )
578 });
579
580 if let Some(idx) = summary_html.rfind("</p>") {
581 summary_html.insert_str(idx, &link.to_string());
582 None
583 } else {
584 Some(link)
585 }
586 } else {
587 None
588 }
589 .maybe_display();
590
591 write!(f, "<div class='docblock'>{summary_html}{link}</div>")?;
592 }
593 Ok(())
594 })
595}
596
597fn document_full_collapsible(
598 item: &clean::Item,
599 cx: &Context<'_>,
600 heading_offset: HeadingOffset,
601) -> impl fmt::Display {
602 document_full_inner(item, cx, true, heading_offset)
603}
604
605fn document_full(
606 item: &clean::Item,
607 cx: &Context<'_>,
608 heading_offset: HeadingOffset,
609) -> impl fmt::Display {
610 document_full_inner(item, cx, false, heading_offset)
611}
612
613fn document_full_inner(
614 item: &clean::Item,
615 cx: &Context<'_>,
616 is_collapsible: bool,
617 heading_offset: HeadingOffset,
618) -> impl fmt::Display {
619 fmt::from_fn(move |f| {
620 if let Some(s) = item.opt_doc_value() {
621 debug!("Doc block: =====\n{s}\n=====");
622 if is_collapsible {
623 write!(
624 f,
625 "<details class=\"toggle top-doc\" open>\
626 <summary class=\"hideme\">\
627 <span>Expand description</span>\
628 </summary>{}</details>",
629 render_markdown(cx, &s, item.links(cx), heading_offset)
630 )?;
631 } else {
632 write!(f, "{}", render_markdown(cx, &s, item.links(cx), heading_offset))?;
633 }
634 }
635
636 let kind = match &item.kind {
637 clean::ItemKind::StrippedItem(box kind) | kind => kind,
638 };
639
640 if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind {
641 render_call_locations(f, cx, item);
642 }
643 Ok(())
644 })
645}
646
647#[derive(Template)]
648#[template(path = "item_info.html")]
649struct ItemInfo {
650 items: Vec<ShortItemInfo>,
651}
652fn document_item_info(
658 cx: &Context<'_>,
659 item: &clean::Item,
660 parent: Option<&clean::Item>,
661) -> ItemInfo {
662 let items = short_item_info(item, cx, parent);
663 ItemInfo { items }
664}
665
666fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> {
667 let cfg = match (&item.cfg, parent.and_then(|p| p.cfg.as_ref())) {
668 (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
669 (cfg, _) => cfg.as_deref().cloned(),
670 };
671
672 debug!(
673 "Portability {name:?} {item_cfg:?} (parent: {parent:?}) - {parent_cfg:?} = {cfg:?}",
674 name = item.name,
675 item_cfg = item.cfg,
676 parent_cfg = parent.and_then(|p| p.cfg.as_ref()),
677 );
678
679 Some(cfg?.render_long_html())
680}
681
682#[derive(Template)]
683#[template(path = "short_item_info.html")]
684enum ShortItemInfo {
685 Deprecation {
687 message: String,
688 },
689 Unstable {
692 feature: String,
693 tracking: Option<(String, u32)>,
694 },
695 Portability {
696 message: String,
697 },
698}
699
700fn short_item_info(
703 item: &clean::Item,
704 cx: &Context<'_>,
705 parent: Option<&clean::Item>,
706) -> Vec<ShortItemInfo> {
707 let mut extra_info = vec![];
708
709 if let Some(depr @ Deprecation { note, since, suggestion: _ }) = item.deprecation(cx.tcx()) {
710 let mut message = match since {
713 DeprecatedSince::RustcVersion(version) => {
714 if depr.is_in_effect() {
715 format!("Deprecated since {version}")
716 } else {
717 format!("Deprecating in {version}")
718 }
719 }
720 DeprecatedSince::Future => String::from("Deprecating in a future version"),
721 DeprecatedSince::NonStandard(since) => {
722 format!("Deprecated since {}", Escape(since.as_str()))
723 }
724 DeprecatedSince::Unspecified | DeprecatedSince::Err => String::from("Deprecated"),
725 };
726
727 if let Some(note) = note {
728 let note = note.as_str();
729 let mut id_map = cx.id_map.borrow_mut();
730 let html = MarkdownItemInfo(note, &mut id_map);
731 message.push_str(": ");
732 message.push_str(&html.into_string());
733 }
734 extra_info.push(ShortItemInfo::Deprecation { message });
735 }
736
737 if let Some((StabilityLevel::Unstable { reason: _, issue, .. }, feature)) = item
740 .stability(cx.tcx())
741 .as_ref()
742 .filter(|stab| stab.feature != sym::rustc_private)
743 .map(|stab| (stab.level, stab.feature))
744 {
745 let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue)
746 {
747 Some((url.clone(), issue.get()))
748 } else {
749 None
750 };
751 extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking });
752 }
753
754 if let Some(message) = portability(item, parent) {
755 extra_info.push(ShortItemInfo::Portability { message });
756 }
757
758 extra_info
759}
760
761pub(crate) fn render_impls(
764 cx: &Context<'_>,
765 mut w: impl Write,
766 impls: &[&Impl],
767 containing_item: &clean::Item,
768 toggle_open_by_default: bool,
769) {
770 let mut rendered_impls = impls
771 .iter()
772 .map(|i| {
773 let did = i.trait_did().unwrap();
774 let provided_trait_methods = i.inner_impl().provided_trait_methods(cx.tcx());
775 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods);
776 let imp = render_impl(
777 cx,
778 i,
779 containing_item,
780 assoc_link,
781 RenderMode::Normal,
782 None,
783 &[],
784 ImplRenderingParameters {
785 show_def_docs: true,
786 show_default_items: true,
787 show_non_assoc_items: true,
788 toggle_open_by_default,
789 },
790 );
791 imp.to_string()
792 })
793 .collect::<Vec<_>>();
794 rendered_impls.sort();
795 w.write_str(&rendered_impls.join("")).unwrap();
796}
797
798fn assoc_href_attr(
800 it: &clean::Item,
801 link: AssocItemLink<'_>,
802 cx: &Context<'_>,
803) -> Option<impl fmt::Display> {
804 let name = it.name.unwrap();
805 let item_type = it.type_();
806
807 enum Href<'a> {
808 AnchorId(&'a str),
809 Anchor(ItemType),
810 Url(String, ItemType),
811 }
812
813 let href = match link {
814 AssocItemLink::Anchor(Some(id)) => Href::AnchorId(id),
815 AssocItemLink::Anchor(None) => Href::Anchor(item_type),
816 AssocItemLink::GotoSource(did, provided_methods) => {
817 let item_type = match item_type {
820 ItemType::Method | ItemType::TyMethod => {
824 if provided_methods.contains(&name) {
825 ItemType::Method
826 } else {
827 ItemType::TyMethod
828 }
829 }
830 item_type => item_type,
832 };
833
834 match href(did.expect_def_id(), cx) {
835 Ok((url, ..)) => Href::Url(url, item_type),
836 Err(HrefError::DocumentationNotBuilt) => return None,
848 Err(_) => Href::Anchor(item_type),
849 }
850 }
851 };
852
853 let href = fmt::from_fn(move |f| match &href {
854 Href::AnchorId(id) => write!(f, "#{id}"),
855 Href::Url(url, item_type) => {
856 write!(f, "{url}#{item_type}.{name}")
857 }
858 Href::Anchor(item_type) => {
859 write!(f, "#{item_type}.{name}")
860 }
861 });
862
863 Some(fmt::from_fn(move |f| write!(f, " href=\"{href}\"")))
866}
867
868#[derive(Debug)]
869enum AssocConstValue<'a> {
870 TraitDefault(&'a clean::ConstantKind),
874 Impl(&'a clean::ConstantKind),
876 None,
877}
878
879fn assoc_const(
880 it: &clean::Item,
881 generics: &clean::Generics,
882 ty: &clean::Type,
883 value: AssocConstValue<'_>,
884 link: AssocItemLink<'_>,
885 indent: usize,
886 cx: &Context<'_>,
887) -> impl fmt::Display {
888 let tcx = cx.tcx();
889 fmt::from_fn(move |w| {
890 write!(
891 w,
892 "{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
893 indent = " ".repeat(indent),
894 vis = visibility_print_with_space(it, cx),
895 href = assoc_href_attr(it, link, cx).maybe_display(),
896 name = it.name.as_ref().unwrap(),
897 generics = generics.print(cx),
898 ty = ty.print(cx),
899 )?;
900 if let AssocConstValue::TraitDefault(konst) | AssocConstValue::Impl(konst) = value {
901 let repr = konst.value(tcx).unwrap_or_else(|| konst.expr(tcx));
907 if match value {
908 AssocConstValue::TraitDefault(_) => true, AssocConstValue::Impl(_) => repr != "_", AssocConstValue::None => unreachable!(),
911 } {
912 write!(w, " = {}", Escape(&repr))?;
913 }
914 }
915 write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display())
916 })
917}
918
919fn assoc_type(
920 it: &clean::Item,
921 generics: &clean::Generics,
922 bounds: &[clean::GenericBound],
923 default: Option<&clean::Type>,
924 link: AssocItemLink<'_>,
925 indent: usize,
926 cx: &Context<'_>,
927) -> impl fmt::Display {
928 fmt::from_fn(move |w| {
929 write!(
930 w,
931 "{indent}{vis}type <a{href} class=\"associatedtype\">{name}</a>{generics}",
932 indent = " ".repeat(indent),
933 vis = visibility_print_with_space(it, cx),
934 href = assoc_href_attr(it, link, cx).maybe_display(),
935 name = it.name.as_ref().unwrap(),
936 generics = generics.print(cx),
937 )?;
938 if !bounds.is_empty() {
939 write!(w, ": {}", print_generic_bounds(bounds, cx))?;
940 }
941 if let Some(default) = default {
943 write!(w, " = {}", default.print(cx))?;
944 }
945 write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display())
946 })
947}
948
949fn assoc_method(
950 meth: &clean::Item,
951 g: &clean::Generics,
952 d: &clean::FnDecl,
953 link: AssocItemLink<'_>,
954 parent: ItemType,
955 cx: &Context<'_>,
956 render_mode: RenderMode,
957) -> impl fmt::Display {
958 let tcx = cx.tcx();
959 let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item");
960 let name = meth.name.as_ref().unwrap();
961 let vis = visibility_print_with_space(meth, cx).to_string();
962 let defaultness = print_default_space(meth.is_default());
963 let constness = match render_mode {
966 RenderMode::Normal => print_constness_with_space(
967 &header.constness,
968 meth.stable_since(tcx),
969 meth.const_stability(tcx),
970 ),
971 RenderMode::ForDeref { .. } => "",
972 };
973
974 fmt::from_fn(move |w| {
975 let asyncness = header.asyncness.print_with_space();
976 let safety = header.safety.print_with_space();
977 let abi = print_abi_with_space(header.abi).to_string();
978 let href = assoc_href_attr(meth, link, cx).maybe_display();
979
980 let generics_len = format!("{:#}", g.print(cx)).len();
982 let mut header_len = "fn ".len()
983 + vis.len()
984 + defaultness.len()
985 + constness.len()
986 + asyncness.len()
987 + safety.len()
988 + abi.len()
989 + name.as_str().len()
990 + generics_len;
991
992 let notable_traits = notable_traits_button(&d.output, cx).maybe_display();
993
994 let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
995 header_len += 4;
996 let indent_str = " ";
997 write!(w, "{}", render_attributes_in_pre(meth, indent_str, cx))?;
998 (4, indent_str, Ending::NoNewline)
999 } else {
1000 render_attributes_in_code(w, meth, cx);
1001 (0, "", Ending::Newline)
1002 };
1003 write!(
1004 w,
1005 "{indent}{vis}{defaultness}{constness}{asyncness}{safety}{abi}fn \
1006 <a{href} class=\"fn\">{name}</a>{generics}{decl}{notable_traits}{where_clause}",
1007 indent = indent_str,
1008 generics = g.print(cx),
1009 decl = d.full_print(header_len, indent, cx),
1010 where_clause = print_where_clause(g, cx, indent, end_newline).maybe_display(),
1011 )
1012 })
1013}
1014
1015fn render_stability_since_raw_with_extra(
1030 stable_version: Option<StableSince>,
1031 const_stability: Option<ConstStability>,
1032 extra_class: &str,
1033) -> Option<impl fmt::Display> {
1034 let mut title = String::new();
1035 let mut stability = String::new();
1036
1037 if let Some(version) = stable_version.and_then(|version| since_to_string(&version)) {
1038 stability.push_str(&version);
1039 title.push_str(&format!("Stable since Rust version {version}"));
1040 }
1041
1042 let const_title_and_stability = match const_stability {
1043 Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) => {
1044 since_to_string(&since)
1045 .map(|since| (format!("const since {since}"), format!("const: {since}")))
1046 }
1047 Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => {
1048 if stable_version.is_none() {
1049 None
1051 } else {
1052 let unstable = if let Some(n) = issue {
1053 format!(
1054 "<a \
1055 href=\"https://github.com/rust-lang/rust/issues/{n}\" \
1056 title=\"Tracking issue for {feature}\"\
1057 >unstable</a>"
1058 )
1059 } else {
1060 String::from("unstable")
1061 };
1062
1063 Some((String::from("const unstable"), format!("const: {unstable}")))
1064 }
1065 }
1066 _ => None,
1067 };
1068
1069 if let Some((const_title, const_stability)) = const_title_and_stability {
1070 if !title.is_empty() {
1071 title.push_str(&format!(", {const_title}"));
1072 } else {
1073 title.push_str(&const_title);
1074 }
1075
1076 if !stability.is_empty() {
1077 stability.push_str(&format!(" ({const_stability})"));
1078 } else {
1079 stability.push_str(&const_stability);
1080 }
1081 }
1082
1083 (!stability.is_empty()).then_some(fmt::from_fn(move |w| {
1084 write!(w, r#"<span class="since{extra_class}" title="{title}">{stability}</span>"#)
1085 }))
1086}
1087
1088fn since_to_string(since: &StableSince) -> Option<String> {
1089 match since {
1090 StableSince::Version(since) => Some(since.to_string()),
1091 StableSince::Current => Some(RustcVersion::CURRENT.to_string()),
1092 StableSince::Err => None,
1093 }
1094}
1095
1096#[inline]
1097fn render_stability_since_raw(
1098 ver: Option<StableSince>,
1099 const_stability: Option<ConstStability>,
1100) -> Option<impl fmt::Display> {
1101 render_stability_since_raw_with_extra(ver, const_stability, "")
1102}
1103
1104fn render_assoc_item(
1105 item: &clean::Item,
1106 link: AssocItemLink<'_>,
1107 parent: ItemType,
1108 cx: &Context<'_>,
1109 render_mode: RenderMode,
1110) -> impl fmt::Display {
1111 fmt::from_fn(move |f| match &item.kind {
1112 clean::StrippedItem(..) => Ok(()),
1113 clean::RequiredMethodItem(m) | clean::MethodItem(m, _) => {
1114 assoc_method(item, &m.generics, &m.decl, link, parent, cx, render_mode).fmt(f)
1115 }
1116 clean::RequiredAssocConstItem(generics, ty) => assoc_const(
1117 item,
1118 generics,
1119 ty,
1120 AssocConstValue::None,
1121 link,
1122 if parent == ItemType::Trait { 4 } else { 0 },
1123 cx,
1124 )
1125 .fmt(f),
1126 clean::ProvidedAssocConstItem(ci) => assoc_const(
1127 item,
1128 &ci.generics,
1129 &ci.type_,
1130 AssocConstValue::TraitDefault(&ci.kind),
1131 link,
1132 if parent == ItemType::Trait { 4 } else { 0 },
1133 cx,
1134 )
1135 .fmt(f),
1136 clean::ImplAssocConstItem(ci) => assoc_const(
1137 item,
1138 &ci.generics,
1139 &ci.type_,
1140 AssocConstValue::Impl(&ci.kind),
1141 link,
1142 if parent == ItemType::Trait { 4 } else { 0 },
1143 cx,
1144 )
1145 .fmt(f),
1146 clean::RequiredAssocTypeItem(generics, bounds) => assoc_type(
1147 item,
1148 generics,
1149 bounds,
1150 None,
1151 link,
1152 if parent == ItemType::Trait { 4 } else { 0 },
1153 cx,
1154 )
1155 .fmt(f),
1156 clean::AssocTypeItem(ty, bounds) => assoc_type(
1157 item,
1158 &ty.generics,
1159 bounds,
1160 Some(ty.item_type.as_ref().unwrap_or(&ty.type_)),
1161 link,
1162 if parent == ItemType::Trait { 4 } else { 0 },
1163 cx,
1164 )
1165 .fmt(f),
1166 _ => panic!("render_assoc_item called on non-associated-item"),
1167 })
1168}
1169
1170fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display {
1173 fmt::from_fn(move |f| {
1174 for a in it.attributes(cx.tcx(), cx.cache(), false) {
1175 writeln!(f, "{prefix}{a}")?;
1176 }
1177 Ok(())
1178 })
1179}
1180
1181fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) {
1184 for attr in it.attributes(cx.tcx(), cx.cache(), false) {
1185 write!(w, "<div class=\"code-attribute\">{attr}</div>").unwrap();
1186 }
1187}
1188
1189#[derive(Copy, Clone)]
1190enum AssocItemLink<'a> {
1191 Anchor(Option<&'a str>),
1192 GotoSource(ItemId, &'a FxIndexSet<Symbol>),
1193}
1194
1195impl<'a> AssocItemLink<'a> {
1196 fn anchor(&self, id: &'a str) -> Self {
1197 match *self {
1198 AssocItemLink::Anchor(_) => AssocItemLink::Anchor(Some(id)),
1199 ref other => *other,
1200 }
1201 }
1202}
1203
1204pub fn write_section_heading(
1205 title: &str,
1206 id: &str,
1207 extra_class: Option<&str>,
1208 extra: impl fmt::Display,
1209) -> impl fmt::Display {
1210 fmt::from_fn(move |w| {
1211 let (extra_class, whitespace) = match extra_class {
1212 Some(extra) => (extra, " "),
1213 None => ("", ""),
1214 };
1215 write!(
1216 w,
1217 "<h2 id=\"{id}\" class=\"{extra_class}{whitespace}section-header\">\
1218 {title}\
1219 <a href=\"#{id}\" class=\"anchor\">§</a>\
1220 </h2>{extra}",
1221 )
1222 })
1223}
1224
1225fn write_impl_section_heading(title: &str, id: &str) -> impl fmt::Display {
1226 write_section_heading(title, id, None, "")
1227}
1228
1229pub(crate) fn render_all_impls(
1230 mut w: impl Write,
1231 cx: &Context<'_>,
1232 containing_item: &clean::Item,
1233 concrete: &[&Impl],
1234 synthetic: &[&Impl],
1235 blanket_impl: &[&Impl],
1236) {
1237 let impls = {
1238 let mut buf = String::new();
1239 render_impls(cx, &mut buf, concrete, containing_item, true);
1240 buf
1241 };
1242 if !impls.is_empty() {
1243 write!(
1244 w,
1245 "{}<div id=\"trait-implementations-list\">{impls}</div>",
1246 write_impl_section_heading("Trait Implementations", "trait-implementations")
1247 )
1248 .unwrap();
1249 }
1250
1251 if !synthetic.is_empty() {
1252 write!(
1253 w,
1254 "{}<div id=\"synthetic-implementations-list\">",
1255 write_impl_section_heading("Auto Trait Implementations", "synthetic-implementations",)
1256 )
1257 .unwrap();
1258 render_impls(cx, &mut w, synthetic, containing_item, false);
1259 w.write_str("</div>").unwrap();
1260 }
1261
1262 if !blanket_impl.is_empty() {
1263 write!(
1264 w,
1265 "{}<div id=\"blanket-implementations-list\">",
1266 write_impl_section_heading("Blanket Implementations", "blanket-implementations")
1267 )
1268 .unwrap();
1269 render_impls(cx, &mut w, blanket_impl, containing_item, false);
1270 w.write_str("</div>").unwrap();
1271 }
1272}
1273
1274fn render_assoc_items(
1275 cx: &Context<'_>,
1276 containing_item: &clean::Item,
1277 it: DefId,
1278 what: AssocItemRender<'_>,
1279) -> impl fmt::Display {
1280 fmt::from_fn(move |f| {
1281 let mut derefs = DefIdSet::default();
1282 derefs.insert(it);
1283 render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
1284 Ok(())
1285 })
1286}
1287
1288fn render_assoc_items_inner(
1289 mut w: &mut dyn fmt::Write,
1290 cx: &Context<'_>,
1291 containing_item: &clean::Item,
1292 it: DefId,
1293 what: AssocItemRender<'_>,
1294 derefs: &mut DefIdSet,
1295) {
1296 info!("Documenting associated items of {:?}", containing_item.name);
1297 let cache = &cx.shared.cache;
1298 let Some(v) = cache.impls.get(&it) else { return };
1299 let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
1300 if !non_trait.is_empty() {
1301 let mut close_tags = <Vec<&str>>::with_capacity(1);
1302 let mut tmp_buf = String::new();
1303 let (render_mode, id, class_html) = match what {
1304 AssocItemRender::All => {
1305 write_str(
1306 &mut tmp_buf,
1307 format_args!(
1308 "{}",
1309 write_impl_section_heading("Implementations", "implementations")
1310 ),
1311 );
1312 (RenderMode::Normal, "implementations-list".to_owned(), "")
1313 }
1314 AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
1315 let id =
1316 cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
1317 let derived_id = cx.derive_id(&id);
1318 close_tags.push("</details>");
1319 write_str(
1320 &mut tmp_buf,
1321 format_args!(
1322 "<details class=\"toggle big-toggle\" open><summary>{}</summary>",
1323 write_impl_section_heading(
1324 &format!(
1325 "<span>Methods from {trait_}<Target = {type_}></span>",
1326 trait_ = trait_.print(cx),
1327 type_ = type_.print(cx),
1328 ),
1329 &id,
1330 )
1331 ),
1332 );
1333 if let Some(def_id) = type_.def_id(cx.cache()) {
1334 cx.deref_id_map.borrow_mut().insert(def_id, id);
1335 }
1336 (RenderMode::ForDeref { mut_: deref_mut_ }, derived_id, r#" class="impl-items""#)
1337 }
1338 };
1339 let mut impls_buf = String::new();
1340 for i in &non_trait {
1341 write_str(
1342 &mut impls_buf,
1343 format_args!(
1344 "{}",
1345 render_impl(
1346 cx,
1347 i,
1348 containing_item,
1349 AssocItemLink::Anchor(None),
1350 render_mode,
1351 None,
1352 &[],
1353 ImplRenderingParameters {
1354 show_def_docs: true,
1355 show_default_items: true,
1356 show_non_assoc_items: true,
1357 toggle_open_by_default: true,
1358 },
1359 )
1360 ),
1361 );
1362 }
1363 if !impls_buf.is_empty() {
1364 write!(w, "{tmp_buf}<div id=\"{id}\"{class_html}>{impls_buf}</div>").unwrap();
1365 for tag in close_tags.into_iter().rev() {
1366 w.write_str(tag).unwrap();
1367 }
1368 }
1369 }
1370
1371 if !traits.is_empty() {
1372 let deref_impl =
1373 traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait());
1374 if let Some(impl_) = deref_impl {
1375 let has_deref_mut =
1376 traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
1377 render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs);
1378 }
1379
1380 if let AssocItemRender::DerefFor { .. } = what {
1383 return;
1384 }
1385
1386 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
1387 traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
1388 let (blanket_impl, concrete): (Vec<&Impl>, _) =
1389 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
1390
1391 render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
1392 }
1393}
1394
1395fn render_deref_methods(
1396 mut w: impl Write,
1397 cx: &Context<'_>,
1398 impl_: &Impl,
1399 container_item: &clean::Item,
1400 deref_mut: bool,
1401 derefs: &mut DefIdSet,
1402) {
1403 let cache = cx.cache();
1404 let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
1405 let (target, real_target) = impl_
1406 .inner_impl()
1407 .items
1408 .iter()
1409 .find_map(|item| match item.kind {
1410 clean::AssocTypeItem(box ref t, _) => Some(match *t {
1411 clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
1412 _ => (&t.type_, &t.type_),
1413 }),
1414 _ => None,
1415 })
1416 .expect("Expected associated type binding");
1417 debug!(
1418 "Render deref methods for {for_:#?}, target {target:#?}",
1419 for_ = impl_.inner_impl().for_
1420 );
1421 let what =
1422 AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
1423 if let Some(did) = target.def_id(cache) {
1424 if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) {
1425 if did == type_did || !derefs.insert(did) {
1427 return;
1429 }
1430 }
1431 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
1432 } else if let Some(prim) = target.primitive_type() {
1433 if let Some(&did) = cache.primitive_locations.get(&prim) {
1434 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
1435 }
1436 }
1437}
1438
1439fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool {
1440 let self_type_opt = match item.kind {
1441 clean::MethodItem(ref method, _) => method.decl.receiver_type(),
1442 clean::RequiredMethodItem(ref method) => method.decl.receiver_type(),
1443 _ => None,
1444 };
1445
1446 if let Some(self_ty) = self_type_opt {
1447 let (by_mut_ref, by_box, by_value) = match *self_ty {
1448 clean::Type::BorrowedRef { mutability, .. } => {
1449 (mutability == Mutability::Mut, false, false)
1450 }
1451 clean::Type::Path { ref path } => {
1452 (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false)
1453 }
1454 clean::Type::SelfTy => (false, false, true),
1455 _ => (false, false, false),
1456 };
1457
1458 (deref_mut_ || !by_mut_ref) && !by_box && !by_value
1459 } else {
1460 false
1461 }
1462}
1463
1464pub(crate) fn notable_traits_button(
1465 ty: &clean::Type,
1466 cx: &Context<'_>,
1467) -> Option<impl fmt::Display> {
1468 if ty.is_unit() {
1469 return None;
1471 }
1472
1473 let did = ty.def_id(cx.cache())?;
1474
1475 if Some(did) == cx.tcx().lang_items().owned_box()
1480 || Some(did) == cx.tcx().lang_items().pin_type()
1481 {
1482 return None;
1483 }
1484
1485 let impls = cx.cache().impls.get(&did)?;
1486 let has_notable_trait = impls
1487 .iter()
1488 .map(Impl::inner_impl)
1489 .filter(|impl_| {
1490 impl_.polarity == ty::ImplPolarity::Positive
1491 && ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1494 })
1495 .filter_map(|impl_| impl_.trait_.as_ref())
1496 .filter_map(|trait_| cx.cache().traits.get(&trait_.def_id()))
1497 .any(|t| t.is_notable_trait(cx.tcx()));
1498
1499 has_notable_trait.then(|| {
1500 cx.types_with_notable_traits.borrow_mut().insert(ty.clone());
1501 fmt::from_fn(|f| {
1502 write!(
1503 f,
1504 " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>",
1505 ty = Escape(&format!("{:#}", ty.print(cx))),
1506 )
1507 })
1508 })
1509}
1510
1511fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
1512 let mut out = String::new();
1513
1514 let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
1515
1516 let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");
1517
1518 for i in impls {
1519 let impl_ = i.inner_impl();
1520 if impl_.polarity != ty::ImplPolarity::Positive {
1521 continue;
1522 }
1523
1524 if !ty.is_doc_subtype_of(&impl_.for_, cx.cache()) {
1525 continue;
1528 }
1529 if let Some(trait_) = &impl_.trait_ {
1530 let trait_did = trait_.def_id();
1531
1532 if cx.cache().traits.get(&trait_did).is_some_and(|t| t.is_notable_trait(cx.tcx())) {
1533 if out.is_empty() {
1534 write_str(
1535 &mut out,
1536 format_args!(
1537 "<h3>Notable traits for <code>{}</code></h3>\
1538 <pre><code>",
1539 impl_.for_.print(cx)
1540 ),
1541 );
1542 }
1543
1544 write_str(
1545 &mut out,
1546 format_args!("<div class=\"where\">{}</div>", impl_.print(false, cx)),
1547 );
1548 for it in &impl_.items {
1549 if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
1550 let empty_set = FxIndexSet::default();
1551 let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
1552 write_str(
1553 &mut out,
1554 format_args!(
1555 "<div class=\"where\"> {};</div>",
1556 assoc_type(
1557 it,
1558 &tydef.generics,
1559 &[], Some(&tydef.type_),
1561 src_link,
1562 0,
1563 cx,
1564 )
1565 ),
1566 );
1567 }
1568 }
1569 }
1570 }
1571 }
1572 if out.is_empty() {
1573 out.push_str("</code></pre>");
1574 }
1575
1576 (format!("{:#}", ty.print(cx)), out)
1577}
1578
1579pub(crate) fn notable_traits_json<'a>(
1580 tys: impl Iterator<Item = &'a clean::Type>,
1581 cx: &Context<'_>,
1582) -> String {
1583 let mut mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect();
1584 mp.sort_by(|(name1, _html1), (name2, _html2)| name1.cmp(name2));
1585 struct NotableTraitsMap(Vec<(String, String)>);
1586 impl Serialize for NotableTraitsMap {
1587 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1588 where
1589 S: Serializer,
1590 {
1591 let mut map = serializer.serialize_map(Some(self.0.len()))?;
1592 for item in &self.0 {
1593 map.serialize_entry(&item.0, &item.1)?;
1594 }
1595 map.end()
1596 }
1597 }
1598 serde_json::to_string(&NotableTraitsMap(mp))
1599 .expect("serialize (string, string) -> json object cannot fail")
1600}
1601
1602#[derive(Clone, Copy, Debug)]
1603struct ImplRenderingParameters {
1604 show_def_docs: bool,
1605 show_default_items: bool,
1606 show_non_assoc_items: bool,
1608 toggle_open_by_default: bool,
1609}
1610
1611fn render_impl(
1612 cx: &Context<'_>,
1613 i: &Impl,
1614 parent: &clean::Item,
1615 link: AssocItemLink<'_>,
1616 render_mode: RenderMode,
1617 use_absolute: Option<bool>,
1618 aliases: &[String],
1619 rendering_params: ImplRenderingParameters,
1620) -> impl fmt::Display {
1621 fmt::from_fn(move |w| {
1622 let cache = &cx.shared.cache;
1623 let traits = &cache.traits;
1624 let trait_ = i.trait_did().map(|did| &traits[&did]);
1625 let mut close_tags = <Vec<&str>>::with_capacity(2);
1626
1627 fn doc_impl_item(
1633 boring: &mut String,
1634 interesting: &mut String,
1635 cx: &Context<'_>,
1636 item: &clean::Item,
1637 parent: &clean::Item,
1638 link: AssocItemLink<'_>,
1639 render_mode: RenderMode,
1640 is_default_item: bool,
1641 trait_: Option<&clean::Trait>,
1642 rendering_params: ImplRenderingParameters,
1643 ) {
1644 let item_type = item.type_();
1645 let name = item.name.as_ref().unwrap();
1646
1647 let render_method_item = rendering_params.show_non_assoc_items
1648 && match render_mode {
1649 RenderMode::Normal => true,
1650 RenderMode::ForDeref { mut_: deref_mut_ } => {
1651 should_render_item(item, deref_mut_, cx.tcx())
1652 }
1653 };
1654
1655 let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
1656
1657 let mut doc_buffer = String::new();
1658 let mut info_buffer = String::new();
1659 let mut short_documented = true;
1660
1661 if render_method_item {
1662 if !is_default_item {
1663 if let Some(t) = trait_ {
1664 if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1667 if !item.doc_value().is_empty() {
1670 document_item_info(cx, it, Some(parent))
1671 .render_into(&mut info_buffer)
1672 .unwrap();
1673 write_str(
1674 &mut doc_buffer,
1675 format_args!("{}", document_full(item, cx, HeadingOffset::H5)),
1676 );
1677 short_documented = false;
1678 } else {
1679 write_str(
1682 &mut doc_buffer,
1683 format_args!(
1684 "{}",
1685 document_short(
1686 it,
1687 cx,
1688 link,
1689 parent,
1690 rendering_params.show_def_docs,
1691 )
1692 ),
1693 );
1694 }
1695 }
1696 } else {
1697 document_item_info(cx, item, Some(parent))
1698 .render_into(&mut info_buffer)
1699 .unwrap();
1700 if rendering_params.show_def_docs {
1701 write_str(
1702 &mut doc_buffer,
1703 format_args!("{}", document_full(item, cx, HeadingOffset::H5)),
1704 );
1705 short_documented = false;
1706 }
1707 }
1708 } else {
1709 write_str(
1710 &mut doc_buffer,
1711 format_args!(
1712 "{}",
1713 document_short(item, cx, link, parent, rendering_params.show_def_docs)
1714 ),
1715 );
1716 }
1717 }
1718 let w = if short_documented && trait_.is_some() { interesting } else { boring };
1719
1720 let toggled = !doc_buffer.is_empty();
1721 if toggled {
1722 let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
1723 write_str(
1724 w,
1725 format_args!("<details class=\"toggle{method_toggle_class}\" open><summary>"),
1726 );
1727 }
1728 match &item.kind {
1729 clean::MethodItem(..) | clean::RequiredMethodItem(_) => {
1730 if render_method_item {
1732 let id = cx.derive_id(format!("{item_type}.{name}"));
1733 let source_id = trait_
1734 .and_then(|trait_| {
1735 trait_
1736 .items
1737 .iter()
1738 .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
1739 })
1740 .map(|item| format!("{}.{name}", item.type_()));
1741 write_str(
1742 w,
1743 format_args!(
1744 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1745 {}",
1746 render_rightside(cx, item, render_mode)
1747 ),
1748 );
1749 if trait_.is_some() {
1750 write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
1752 }
1753 write_str(
1754 w,
1755 format_args!(
1756 "<h4 class=\"code-header\">{}</h4></section>",
1757 render_assoc_item(
1758 item,
1759 link.anchor(source_id.as_ref().unwrap_or(&id)),
1760 ItemType::Impl,
1761 cx,
1762 render_mode,
1763 ),
1764 ),
1765 );
1766 }
1767 }
1768 clean::RequiredAssocConstItem(generics, ty) => {
1769 let source_id = format!("{item_type}.{name}");
1770 let id = cx.derive_id(&source_id);
1771 write_str(
1772 w,
1773 format_args!(
1774 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1775 {}",
1776 render_rightside(cx, item, render_mode)
1777 ),
1778 );
1779 if trait_.is_some() {
1780 write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
1782 }
1783 write_str(
1784 w,
1785 format_args!(
1786 "<h4 class=\"code-header\">{}</h4></section>",
1787 assoc_const(
1788 item,
1789 generics,
1790 ty,
1791 AssocConstValue::None,
1792 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1793 0,
1794 cx,
1795 )
1796 ),
1797 );
1798 }
1799 clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
1800 let source_id = format!("{item_type}.{name}");
1801 let id = cx.derive_id(&source_id);
1802 write_str(
1803 w,
1804 format_args!(
1805 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1806 {}",
1807 render_rightside(cx, item, render_mode)
1808 ),
1809 );
1810 if trait_.is_some() {
1811 write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
1813 }
1814 write_str(
1815 w,
1816 format_args!(
1817 "<h4 class=\"code-header\">{}</h4></section>",
1818 assoc_const(
1819 item,
1820 &ci.generics,
1821 &ci.type_,
1822 match item.kind {
1823 clean::ProvidedAssocConstItem(_) =>
1824 AssocConstValue::TraitDefault(&ci.kind),
1825 clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
1826 _ => unreachable!(),
1827 },
1828 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1829 0,
1830 cx,
1831 )
1832 ),
1833 );
1834 }
1835 clean::RequiredAssocTypeItem(generics, bounds) => {
1836 let source_id = format!("{item_type}.{name}");
1837 let id = cx.derive_id(&source_id);
1838 write_str(
1839 w,
1840 format_args!(
1841 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1842 {}",
1843 render_rightside(cx, item, render_mode)
1844 ),
1845 );
1846 if trait_.is_some() {
1847 write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
1849 }
1850 write_str(
1851 w,
1852 format_args!(
1853 "<h4 class=\"code-header\">{}</h4></section>",
1854 assoc_type(
1855 item,
1856 generics,
1857 bounds,
1858 None,
1859 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1860 0,
1861 cx,
1862 )
1863 ),
1864 );
1865 }
1866 clean::AssocTypeItem(tydef, _bounds) => {
1867 let source_id = format!("{item_type}.{name}");
1868 let id = cx.derive_id(&source_id);
1869 write_str(
1870 w,
1871 format_args!(
1872 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1873 {}",
1874 render_rightside(cx, item, render_mode)
1875 ),
1876 );
1877 if trait_.is_some() {
1878 write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
1880 }
1881 write_str(
1882 w,
1883 format_args!(
1884 "<h4 class=\"code-header\">{}</h4></section>",
1885 assoc_type(
1886 item,
1887 &tydef.generics,
1888 &[], Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
1890 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1891 0,
1892 cx,
1893 )
1894 ),
1895 );
1896 }
1897 clean::StrippedItem(..) => return,
1898 _ => panic!("can't make docs for trait item with name {:?}", item.name),
1899 }
1900
1901 w.push_str(&info_buffer);
1902 if toggled {
1903 w.push_str("</summary>");
1904 w.push_str(&doc_buffer);
1905 w.push_str("</details>");
1906 }
1907 }
1908
1909 let mut impl_items = String::new();
1910 let mut default_impl_items = String::new();
1911 let impl_ = i.inner_impl();
1912
1913 let mut assoc_types = Vec::new();
1923 let mut methods = Vec::new();
1924
1925 if !impl_.is_negative_trait_impl() {
1926 for trait_item in &impl_.items {
1927 match trait_item.kind {
1928 clean::MethodItem(..) | clean::RequiredMethodItem(_) => {
1929 methods.push(trait_item)
1930 }
1931 clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => {
1932 assoc_types.push(trait_item)
1933 }
1934 clean::RequiredAssocConstItem(..)
1935 | clean::ProvidedAssocConstItem(_)
1936 | clean::ImplAssocConstItem(_) => {
1937 doc_impl_item(
1939 &mut default_impl_items,
1940 &mut impl_items,
1941 cx,
1942 trait_item,
1943 if trait_.is_some() { &i.impl_item } else { parent },
1944 link,
1945 render_mode,
1946 false,
1947 trait_,
1948 rendering_params,
1949 );
1950 }
1951 _ => {}
1952 }
1953 }
1954
1955 for assoc_type in assoc_types {
1956 doc_impl_item(
1957 &mut default_impl_items,
1958 &mut impl_items,
1959 cx,
1960 assoc_type,
1961 if trait_.is_some() { &i.impl_item } else { parent },
1962 link,
1963 render_mode,
1964 false,
1965 trait_,
1966 rendering_params,
1967 );
1968 }
1969 for method in methods {
1970 doc_impl_item(
1971 &mut default_impl_items,
1972 &mut impl_items,
1973 cx,
1974 method,
1975 if trait_.is_some() { &i.impl_item } else { parent },
1976 link,
1977 render_mode,
1978 false,
1979 trait_,
1980 rendering_params,
1981 );
1982 }
1983 }
1984
1985 fn render_default_items(
1986 boring: &mut String,
1987 interesting: &mut String,
1988 cx: &Context<'_>,
1989 t: &clean::Trait,
1990 i: &clean::Impl,
1991 parent: &clean::Item,
1992 render_mode: RenderMode,
1993 rendering_params: ImplRenderingParameters,
1994 ) {
1995 for trait_item in &t.items {
1996 if let Some(impl_def_id) = parent.item_id.as_def_id()
1999 && let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
2000 && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id))
2001 {
2002 continue;
2003 }
2004
2005 let n = trait_item.name;
2006 if i.items.iter().any(|m| m.name == n) {
2007 continue;
2008 }
2009 let did = i.trait_.as_ref().unwrap().def_id();
2010 let provided_methods = i.provided_trait_methods(cx.tcx());
2011 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
2012
2013 doc_impl_item(
2014 boring,
2015 interesting,
2016 cx,
2017 trait_item,
2018 parent,
2019 assoc_link,
2020 render_mode,
2021 true,
2022 Some(t),
2023 rendering_params,
2024 );
2025 }
2026 }
2027
2028 if rendering_params.show_default_items {
2033 if let Some(t) = trait_
2034 && !impl_.is_negative_trait_impl()
2035 {
2036 render_default_items(
2037 &mut default_impl_items,
2038 &mut impl_items,
2039 cx,
2040 t,
2041 impl_,
2042 &i.impl_item,
2043 render_mode,
2044 rendering_params,
2045 );
2046 }
2047 }
2048 if render_mode == RenderMode::Normal {
2049 let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
2050 if toggled {
2051 close_tags.push("</details>");
2052 write!(
2053 w,
2054 "<details class=\"toggle implementors-toggle\"{}>\
2055 <summary>",
2056 if rendering_params.toggle_open_by_default { " open" } else { "" }
2057 )?;
2058 }
2059
2060 let (before_dox, after_dox) = i
2061 .impl_item
2062 .opt_doc_value()
2063 .map(|dox| {
2064 Markdown {
2065 content: &dox,
2066 links: &i.impl_item.links(cx),
2067 ids: &mut cx.id_map.borrow_mut(),
2068 error_codes: cx.shared.codes,
2069 edition: cx.shared.edition(),
2070 playground: &cx.shared.playground,
2071 heading_offset: HeadingOffset::H4,
2072 }
2073 .split_summary_and_content()
2074 })
2075 .unwrap_or((None, None));
2076 write!(
2077 w,
2078 "{}",
2079 render_impl_summary(
2080 cx,
2081 i,
2082 parent,
2083 rendering_params.show_def_docs,
2084 use_absolute,
2085 aliases,
2086 before_dox.as_deref(),
2087 )
2088 )?;
2089 if toggled {
2090 w.write_str("</summary>")?;
2091 }
2092
2093 if before_dox.is_some() {
2094 if trait_.is_none() && impl_.items.is_empty() {
2095 w.write_str(
2096 "<div class=\"item-info\">\
2097 <div class=\"stab empty-impl\">This impl block contains no items.</div>\
2098 </div>",
2099 )?;
2100 }
2101 if let Some(after_dox) = after_dox {
2102 write!(w, "<div class=\"docblock\">{after_dox}</div>")?;
2103 }
2104 }
2105 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2106 w.write_str("<div class=\"impl-items\">")?;
2107 close_tags.push("</div>");
2108 }
2109 }
2110 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2111 w.write_str(&default_impl_items)?;
2112 w.write_str(&impl_items)?;
2113 }
2114 for tag in close_tags.into_iter().rev() {
2115 w.write_str(tag)?;
2116 }
2117 Ok(())
2118 })
2119}
2120
2121fn render_rightside(
2124 cx: &Context<'_>,
2125 item: &clean::Item,
2126 render_mode: RenderMode,
2127) -> impl fmt::Display {
2128 let tcx = cx.tcx();
2129
2130 fmt::from_fn(move |w| {
2131 let const_stability = match render_mode {
2134 RenderMode::Normal => item.const_stability(tcx),
2135 RenderMode::ForDeref { .. } => None,
2136 };
2137 let src_href = cx.src_href(item);
2138 let stability = render_stability_since_raw_with_extra(
2139 item.stable_since(tcx),
2140 const_stability,
2141 if src_href.is_some() { "" } else { " rightside" },
2142 );
2143
2144 match (stability, src_href) {
2145 (Some(stability), Some(link)) => {
2146 write!(
2147 w,
2148 "<span class=\"rightside\">{stability} · <a class=\"src\" href=\"{link}\">Source</a></span>",
2149 )
2150 }
2151 (Some(stability), None) => {
2152 write!(w, "{stability}")
2153 }
2154 (None, Some(link)) => {
2155 write!(w, "<a class=\"src rightside\" href=\"{link}\">Source</a>")
2156 }
2157 (None, None) => Ok(()),
2158 }
2159 })
2160}
2161
2162pub(crate) fn render_impl_summary(
2163 cx: &Context<'_>,
2164 i: &Impl,
2165 parent: &clean::Item,
2166 show_def_docs: bool,
2167 use_absolute: Option<bool>,
2168 aliases: &[String],
2171 doc: Option<&str>,
2172) -> impl fmt::Display {
2173 fmt::from_fn(move |w| {
2174 let inner_impl = i.inner_impl();
2175 let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
2176 let aliases = (!aliases.is_empty())
2177 .then_some(fmt::from_fn(|f| {
2178 write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f)))
2179 }))
2180 .maybe_display();
2181 write!(
2182 w,
2183 "<section id=\"{id}\" class=\"impl\"{aliases}>\
2184 {}\
2185 <a href=\"#{id}\" class=\"anchor\">§</a>\
2186 <h3 class=\"code-header\">",
2187 render_rightside(cx, &i.impl_item, RenderMode::Normal)
2188 )?;
2189
2190 if let Some(use_absolute) = use_absolute {
2191 write!(w, "{}", inner_impl.print(use_absolute, cx))?;
2192 if show_def_docs {
2193 for it in &inner_impl.items {
2194 if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
2195 write!(
2196 w,
2197 "<div class=\"where\"> {};</div>",
2198 assoc_type(
2199 it,
2200 &tydef.generics,
2201 &[], Some(&tydef.type_),
2203 AssocItemLink::Anchor(None),
2204 0,
2205 cx,
2206 )
2207 )?;
2208 }
2209 }
2210 }
2211 } else {
2212 write!(w, "{}", inner_impl.print(false, cx))?;
2213 }
2214 w.write_str("</h3>")?;
2215
2216 let is_trait = inner_impl.trait_.is_some();
2217 if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) {
2218 write!(
2219 w,
2220 "<span class=\"item-info\">\
2221 <div class=\"stab portability\">{portability}</div>\
2222 </span>",
2223 )?;
2224 }
2225
2226 if let Some(doc) = doc {
2227 write!(w, "<div class=\"docblock\">{doc}</div>")?;
2228 }
2229
2230 w.write_str("</section>")
2231 })
2232}
2233
2234pub(crate) fn small_url_encode(s: String) -> String {
2235 fn dont_escape(c: u8) -> bool {
2240 c.is_ascii_alphanumeric()
2241 || c == b'-'
2242 || c == b'_'
2243 || c == b'.'
2244 || c == b','
2245 || c == b'~'
2246 || c == b'!'
2247 || c == b'\''
2248 || c == b'('
2249 || c == b')'
2250 || c == b'*'
2251 || c == b'/'
2252 || c == b';'
2253 || c == b':'
2254 || c == b'?'
2255 || c == b'='
2259 }
2260 let mut st = String::new();
2261 let mut last_match = 0;
2262 for (idx, b) in s.bytes().enumerate() {
2263 if dont_escape(b) {
2264 continue;
2265 }
2266
2267 if last_match != idx {
2268 st += &s[last_match..idx];
2270 }
2271 if b == b' ' {
2272 st += "+";
2276 } else {
2277 write!(st, "%{b:02X}").unwrap();
2278 }
2279 last_match = idx + 1;
2285 }
2286
2287 if last_match != 0 {
2288 st += &s[last_match..];
2289 st
2290 } else {
2291 s
2292 }
2293}
2294
2295fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String {
2296 use rustc_middle::ty::print::with_forced_trimmed_paths;
2297 let (type_, trait_) = match impl_id {
2298 ItemId::Auto { trait_, for_ } => {
2299 let ty = tcx.type_of(for_).skip_binder();
2300 (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
2301 }
2302 ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
2303 match tcx.impl_subject(impl_id).skip_binder() {
2304 ty::ImplSubject::Trait(trait_ref) => {
2305 (trait_ref.args[0].expect_ty(), Some(trait_ref))
2306 }
2307 ty::ImplSubject::Inherent(ty) => (ty, None),
2308 }
2309 }
2310 };
2311 with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
2312 format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
2313 } else {
2314 format!("impl-{type_}")
2315 }))
2316}
2317
2318fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2319 match item.kind {
2320 clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
2321 Some((format!("{:#}", i.for_.print(cx)), get_id_for_impl(cx.tcx(), item.item_id)))
2324 }
2325 _ => None,
2326 }
2327}
2328
2329pub(crate) fn get_filtered_impls_for_reference<'a>(
2333 shared: &'a SharedContext<'_>,
2334 it: &clean::Item,
2335) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
2336 let def_id = it.item_id.expect_def_id();
2337 let Some(v) = shared.cache.impls.get(&def_id) else {
2339 return (Vec::new(), Vec::new(), Vec::new());
2340 };
2341 let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
2344 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
2345 traits.partition(|t| t.inner_impl().kind.is_auto());
2346
2347 let (blanket_impl, concrete): (Vec<&Impl>, _) =
2348 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
2349 let concrete: Vec<_> = concrete
2351 .into_iter()
2352 .filter(|t| match t.inner_impl().for_ {
2353 clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
2354 _ => false,
2355 })
2356 .collect();
2357
2358 (concrete, synthetic, blanket_impl)
2359}
2360
2361#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2362pub(crate) enum ItemSection {
2363 Reexports,
2364 PrimitiveTypes,
2365 Modules,
2366 Macros,
2367 Structs,
2368 Enums,
2369 Constants,
2370 Statics,
2371 Traits,
2372 Functions,
2373 TypeAliases,
2374 Unions,
2375 Implementations,
2376 TypeMethods,
2377 Methods,
2378 StructFields,
2379 Variants,
2380 AssociatedTypes,
2381 AssociatedConstants,
2382 ForeignTypes,
2383 Keywords,
2384 AttributeMacros,
2385 DeriveMacros,
2386 TraitAliases,
2387}
2388
2389impl ItemSection {
2390 const ALL: &'static [Self] = {
2391 use ItemSection::*;
2392 &[
2395 Reexports,
2396 PrimitiveTypes,
2397 Modules,
2398 Macros,
2399 Structs,
2400 Enums,
2401 Constants,
2402 Statics,
2403 Traits,
2404 Functions,
2405 TypeAliases,
2406 Unions,
2407 Implementations,
2408 TypeMethods,
2409 Methods,
2410 StructFields,
2411 Variants,
2412 AssociatedTypes,
2413 AssociatedConstants,
2414 ForeignTypes,
2415 Keywords,
2416 AttributeMacros,
2417 DeriveMacros,
2418 TraitAliases,
2419 ]
2420 };
2421
2422 fn id(self) -> &'static str {
2423 match self {
2424 Self::Reexports => "reexports",
2425 Self::Modules => "modules",
2426 Self::Structs => "structs",
2427 Self::Unions => "unions",
2428 Self::Enums => "enums",
2429 Self::Functions => "functions",
2430 Self::TypeAliases => "types",
2431 Self::Statics => "statics",
2432 Self::Constants => "constants",
2433 Self::Traits => "traits",
2434 Self::Implementations => "impls",
2435 Self::TypeMethods => "tymethods",
2436 Self::Methods => "methods",
2437 Self::StructFields => "fields",
2438 Self::Variants => "variants",
2439 Self::Macros => "macros",
2440 Self::PrimitiveTypes => "primitives",
2441 Self::AssociatedTypes => "associated-types",
2442 Self::AssociatedConstants => "associated-consts",
2443 Self::ForeignTypes => "foreign-types",
2444 Self::Keywords => "keywords",
2445 Self::AttributeMacros => "attributes",
2446 Self::DeriveMacros => "derives",
2447 Self::TraitAliases => "trait-aliases",
2448 }
2449 }
2450
2451 fn name(self) -> &'static str {
2452 match self {
2453 Self::Reexports => "Re-exports",
2454 Self::Modules => "Modules",
2455 Self::Structs => "Structs",
2456 Self::Unions => "Unions",
2457 Self::Enums => "Enums",
2458 Self::Functions => "Functions",
2459 Self::TypeAliases => "Type Aliases",
2460 Self::Statics => "Statics",
2461 Self::Constants => "Constants",
2462 Self::Traits => "Traits",
2463 Self::Implementations => "Implementations",
2464 Self::TypeMethods => "Type Methods",
2465 Self::Methods => "Methods",
2466 Self::StructFields => "Struct Fields",
2467 Self::Variants => "Variants",
2468 Self::Macros => "Macros",
2469 Self::PrimitiveTypes => "Primitive Types",
2470 Self::AssociatedTypes => "Associated Types",
2471 Self::AssociatedConstants => "Associated Constants",
2472 Self::ForeignTypes => "Foreign Types",
2473 Self::Keywords => "Keywords",
2474 Self::AttributeMacros => "Attribute Macros",
2475 Self::DeriveMacros => "Derive Macros",
2476 Self::TraitAliases => "Trait Aliases",
2477 }
2478 }
2479}
2480
2481fn item_ty_to_section(ty: ItemType) -> ItemSection {
2482 match ty {
2483 ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
2484 ItemType::Module => ItemSection::Modules,
2485 ItemType::Struct => ItemSection::Structs,
2486 ItemType::Union => ItemSection::Unions,
2487 ItemType::Enum => ItemSection::Enums,
2488 ItemType::Function => ItemSection::Functions,
2489 ItemType::TypeAlias => ItemSection::TypeAliases,
2490 ItemType::Static => ItemSection::Statics,
2491 ItemType::Constant => ItemSection::Constants,
2492 ItemType::Trait => ItemSection::Traits,
2493 ItemType::Impl => ItemSection::Implementations,
2494 ItemType::TyMethod => ItemSection::TypeMethods,
2495 ItemType::Method => ItemSection::Methods,
2496 ItemType::StructField => ItemSection::StructFields,
2497 ItemType::Variant => ItemSection::Variants,
2498 ItemType::Macro => ItemSection::Macros,
2499 ItemType::Primitive => ItemSection::PrimitiveTypes,
2500 ItemType::AssocType => ItemSection::AssociatedTypes,
2501 ItemType::AssocConst => ItemSection::AssociatedConstants,
2502 ItemType::ForeignType => ItemSection::ForeignTypes,
2503 ItemType::Keyword => ItemSection::Keywords,
2504 ItemType::ProcAttribute => ItemSection::AttributeMacros,
2505 ItemType::ProcDerive => ItemSection::DeriveMacros,
2506 ItemType::TraitAlias => ItemSection::TraitAliases,
2507 }
2508}
2509
2510fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
2517 let mut out = Vec::new();
2518 let mut visited = FxHashSet::default();
2519 let mut work = VecDeque::new();
2520
2521 let mut process_path = |did: DefId| {
2522 let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
2523 let fqp = cache.exact_paths.get(&did).or_else(get_extern);
2524
2525 if let Some(path) = fqp {
2526 out.push(join_with_double_colon(path));
2527 }
2528 };
2529
2530 work.push_back(first_ty);
2531
2532 while let Some(ty) = work.pop_front() {
2533 if !visited.insert(ty.clone()) {
2534 continue;
2535 }
2536
2537 match ty {
2538 clean::Type::Path { path } => process_path(path.def_id()),
2539 clean::Type::Tuple(tys) => {
2540 work.extend(tys.into_iter());
2541 }
2542 clean::Type::Slice(ty) => {
2543 work.push_back(*ty);
2544 }
2545 clean::Type::Array(ty, _) => {
2546 work.push_back(*ty);
2547 }
2548 clean::Type::RawPointer(_, ty) => {
2549 work.push_back(*ty);
2550 }
2551 clean::Type::BorrowedRef { type_, .. } => {
2552 work.push_back(*type_);
2553 }
2554 clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
2555 work.push_back(self_type);
2556 if let Some(trait_) = trait_ {
2557 process_path(trait_.def_id());
2558 }
2559 }
2560 _ => {}
2561 }
2562 }
2563 out
2564}
2565
2566const MAX_FULL_EXAMPLES: usize = 5;
2567const NUM_VISIBLE_LINES: usize = 10;
2568
2569fn render_call_locations<W: fmt::Write>(mut w: W, cx: &Context<'_>, item: &clean::Item) {
2571 let tcx = cx.tcx();
2572 let def_id = item.item_id.expect_def_id();
2573 let key = tcx.def_path_hash(def_id);
2574 let Some(call_locations) = cx.shared.call_locations.get(&key) else { return };
2575
2576 let id = cx.derive_id("scraped-examples");
2578 write!(
2579 &mut w,
2580 "<div class=\"docblock scraped-example-list\">\
2581 <span></span>\
2582 <h5 id=\"{id}\">\
2583 <a href=\"#{id}\">Examples found in repository</a>\
2584 <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
2585 </h5>",
2586 root_path = cx.root_path(),
2587 id = id
2588 )
2589 .unwrap();
2590
2591 let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
2593 let (line_lo, line_hi) = loc.call_expr.line_span;
2594 let (anchor, title) = if line_lo == line_hi {
2595 ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
2596 } else {
2597 (
2598 format!("{}-{}", line_lo + 1, line_hi + 1),
2599 format!("lines {}-{}", line_lo + 1, line_hi + 1),
2600 )
2601 };
2602 let url = format!("{}{}#{anchor}", cx.root_path(), call_data.url);
2603 (url, title)
2604 };
2605
2606 let write_example = |w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
2608 let contents = match fs::read_to_string(path) {
2609 Ok(contents) => contents,
2610 Err(err) => {
2611 let span = item.span(tcx).map_or(DUMMY_SP, |span| span.inner());
2612 tcx.dcx().span_err(span, format!("failed to read file {}: {err}", path.display()));
2613 return false;
2614 }
2615 };
2616
2617 assert!(!call_data.locations.is_empty());
2620 let min_loc =
2621 call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2622 let byte_min = min_loc.enclosing_item.byte_span.0;
2623 let line_min = min_loc.enclosing_item.line_span.0;
2624 let max_loc =
2625 call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
2626 let byte_max = max_loc.enclosing_item.byte_span.1;
2627 let line_max = max_loc.enclosing_item.line_span.1;
2628
2629 let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
2631
2632 let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
2635 .locations
2636 .iter()
2637 .map(|loc| {
2638 let (byte_lo, byte_hi) = loc.call_ident.byte_span;
2639 let (line_lo, line_hi) = loc.call_expr.line_span;
2640 let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
2641
2642 let line_range = (line_lo - line_min, line_hi - line_min);
2643 let (line_url, line_title) = link_to_loc(call_data, loc);
2644
2645 (byte_range, (line_range, line_url, line_title))
2646 })
2647 .unzip();
2648
2649 let (_, init_url, init_title) = &line_ranges[0];
2650 let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
2651 let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
2652
2653 let file_span = (|| {
2655 let source_map = tcx.sess.source_map();
2656 let crate_src = tcx.sess.local_crate_source_file()?.into_local_path()?;
2657 let abs_crate_src = crate_src.canonicalize().ok()?;
2658 let crate_root = abs_crate_src.parent()?.parent()?;
2659 let rel_path = path.strip_prefix(crate_root).ok()?;
2660 let files = source_map.files();
2661 let file = files.iter().find(|file| match &file.name {
2662 FileName::Real(RealFileName::LocalPath(other_path)) => rel_path == other_path,
2663 _ => false,
2664 })?;
2665 Some(rustc_span::Span::with_root_ctxt(
2666 file.start_pos + BytePos(byte_min),
2667 file.start_pos + BytePos(byte_max),
2668 ))
2669 })()
2670 .unwrap_or(DUMMY_SP);
2671
2672 let mut decoration_info = FxIndexMap::default();
2673 decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
2674 decoration_info.insert("highlight", byte_ranges);
2675
2676 sources::print_src(
2677 w,
2678 contents_subset,
2679 file_span,
2680 cx,
2681 &cx.root_path(),
2682 &highlight::DecorationInfo(decoration_info),
2683 &sources::SourceContext::Embedded(sources::ScrapedInfo {
2684 needs_expansion,
2685 offset: line_min,
2686 name: &call_data.display_name,
2687 url: init_url,
2688 title: init_title,
2689 locations: locations_encoded,
2690 }),
2691 );
2692
2693 true
2694 };
2695
2696 let ordered_locations = {
2708 fn sort_criterion<'a>(
2709 (_, call_data): &(&PathBuf, &'a CallData),
2710 ) -> (bool, u32, &'a String) {
2711 let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2713 (!call_data.is_bin, hi - lo, &call_data.display_name)
2714 }
2715
2716 let mut locs = call_locations.iter().collect::<Vec<_>>();
2717 locs.sort_by_key(sort_criterion);
2718 locs
2719 };
2720
2721 let mut it = ordered_locations.into_iter().peekable();
2722
2723 let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
2726 for example in it.by_ref() {
2727 if write_example(&mut *w, example) {
2728 break;
2729 }
2730 }
2731 };
2732
2733 write_and_skip_failure(&mut w, &mut it);
2735
2736 if it.peek().is_some() {
2738 write!(
2739 w,
2740 "<details class=\"toggle more-examples-toggle\">\
2741 <summary class=\"hideme\">\
2742 <span>More examples</span>\
2743 </summary>\
2744 <div class=\"hide-more\">Hide additional examples</div>\
2745 <div class=\"more-scraped-examples\">\
2746 <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
2747 )
2748 .unwrap();
2749
2750 for _ in 0..MAX_FULL_EXAMPLES {
2753 write_and_skip_failure(&mut w, &mut it);
2754 }
2755
2756 if it.peek().is_some() {
2758 w.write_str(
2759 r#"<div class="example-links">Additional examples can be found in:<br><ul>"#,
2760 )
2761 .unwrap();
2762 it.for_each(|(_, call_data)| {
2763 let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
2764 write!(
2765 w,
2766 r#"<li><a href="{url}">{name}</a></li>"#,
2767 url = url,
2768 name = call_data.display_name
2769 )
2770 .unwrap();
2771 });
2772 w.write_str("</ul></div>").unwrap();
2773 }
2774
2775 w.write_str("</div></details>").unwrap();
2776 }
2777
2778 w.write_str("</div>").unwrap();
2779}