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, Debug)]
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 (mut non_trait, traits): (Vec<_>, _) =
1300 v.iter().partition(|i| i.inner_impl().trait_.is_none());
1301 if !non_trait.is_empty() {
1302 let mut close_tags = <Vec<&str>>::with_capacity(1);
1303 let mut tmp_buf = String::new();
1304 let (render_mode, id, class_html) = match what {
1305 AssocItemRender::All => {
1306 write_str(
1307 &mut tmp_buf,
1308 format_args!(
1309 "{}",
1310 write_impl_section_heading("Implementations", "implementations")
1311 ),
1312 );
1313 (RenderMode::Normal, "implementations-list".to_owned(), "")
1314 }
1315 AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
1316 let id =
1317 cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
1318 non_trait.retain(|impl_| {
1326 type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache)
1327 });
1328 let derived_id = cx.derive_id(&id);
1329 close_tags.push("</details>");
1330 write_str(
1331 &mut tmp_buf,
1332 format_args!(
1333 "<details class=\"toggle big-toggle\" open><summary>{}</summary>",
1334 write_impl_section_heading(
1335 &format!(
1336 "<span>Methods from {trait_}<Target = {type_}></span>",
1337 trait_ = trait_.print(cx),
1338 type_ = type_.print(cx),
1339 ),
1340 &id,
1341 )
1342 ),
1343 );
1344 if let Some(def_id) = type_.def_id(cx.cache()) {
1345 cx.deref_id_map.borrow_mut().insert(def_id, id);
1346 }
1347 (RenderMode::ForDeref { mut_: deref_mut_ }, derived_id, r#" class="impl-items""#)
1348 }
1349 };
1350 let mut impls_buf = String::new();
1351 for i in &non_trait {
1352 write_str(
1353 &mut impls_buf,
1354 format_args!(
1355 "{}",
1356 render_impl(
1357 cx,
1358 i,
1359 containing_item,
1360 AssocItemLink::Anchor(None),
1361 render_mode,
1362 None,
1363 &[],
1364 ImplRenderingParameters {
1365 show_def_docs: true,
1366 show_default_items: true,
1367 show_non_assoc_items: true,
1368 toggle_open_by_default: true,
1369 },
1370 )
1371 ),
1372 );
1373 }
1374 if !impls_buf.is_empty() {
1375 write!(w, "{tmp_buf}<div id=\"{id}\"{class_html}>{impls_buf}</div>").unwrap();
1376 for tag in close_tags.into_iter().rev() {
1377 w.write_str(tag).unwrap();
1378 }
1379 }
1380 }
1381
1382 if !traits.is_empty() {
1383 let deref_impl =
1384 traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait());
1385 if let Some(impl_) = deref_impl {
1386 let has_deref_mut =
1387 traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
1388 render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs);
1389 }
1390
1391 if let AssocItemRender::DerefFor { .. } = what {
1394 return;
1395 }
1396
1397 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
1398 traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
1399 let (blanket_impl, concrete): (Vec<&Impl>, _) =
1400 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
1401
1402 render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
1403 }
1404}
1405
1406fn render_deref_methods(
1408 mut w: impl Write,
1409 cx: &Context<'_>,
1410 impl_: &Impl,
1411 container_item: &clean::Item,
1412 deref_mut: bool,
1413 derefs: &mut DefIdSet,
1414) {
1415 let cache = cx.cache();
1416 let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
1417 let (target, real_target) = impl_
1418 .inner_impl()
1419 .items
1420 .iter()
1421 .find_map(|item| match item.kind {
1422 clean::AssocTypeItem(box ref t, _) => Some(match *t {
1423 clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
1424 _ => (&t.type_, &t.type_),
1425 }),
1426 _ => None,
1427 })
1428 .expect("Expected associated type binding");
1429 debug!(
1430 "Render deref methods for {for_:#?}, target {target:#?}",
1431 for_ = impl_.inner_impl().for_
1432 );
1433 let what =
1434 AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
1435 if let Some(did) = target.def_id(cache) {
1436 if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) {
1437 if did == type_did || !derefs.insert(did) {
1439 return;
1441 }
1442 }
1443 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
1444 } else if let Some(prim) = target.primitive_type() {
1445 if let Some(&did) = cache.primitive_locations.get(&prim) {
1446 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
1447 }
1448 }
1449}
1450
1451fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool {
1452 let self_type_opt = match item.kind {
1453 clean::MethodItem(ref method, _) => method.decl.receiver_type(),
1454 clean::RequiredMethodItem(ref method) => method.decl.receiver_type(),
1455 _ => None,
1456 };
1457
1458 if let Some(self_ty) = self_type_opt {
1459 let (by_mut_ref, by_box, by_value) = match *self_ty {
1460 clean::Type::BorrowedRef { mutability, .. } => {
1461 (mutability == Mutability::Mut, false, false)
1462 }
1463 clean::Type::Path { ref path } => {
1464 (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false)
1465 }
1466 clean::Type::SelfTy => (false, false, true),
1467 _ => (false, false, false),
1468 };
1469
1470 (deref_mut_ || !by_mut_ref) && !by_box && !by_value
1471 } else {
1472 false
1473 }
1474}
1475
1476pub(crate) fn notable_traits_button(
1477 ty: &clean::Type,
1478 cx: &Context<'_>,
1479) -> Option<impl fmt::Display> {
1480 if ty.is_unit() {
1481 return None;
1483 }
1484
1485 let did = ty.def_id(cx.cache())?;
1486
1487 if Some(did) == cx.tcx().lang_items().owned_box()
1492 || Some(did) == cx.tcx().lang_items().pin_type()
1493 {
1494 return None;
1495 }
1496
1497 let impls = cx.cache().impls.get(&did)?;
1498 let has_notable_trait = impls
1499 .iter()
1500 .map(Impl::inner_impl)
1501 .filter(|impl_| {
1502 impl_.polarity == ty::ImplPolarity::Positive
1503 && ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1506 })
1507 .filter_map(|impl_| impl_.trait_.as_ref())
1508 .filter_map(|trait_| cx.cache().traits.get(&trait_.def_id()))
1509 .any(|t| t.is_notable_trait(cx.tcx()));
1510
1511 has_notable_trait.then(|| {
1512 cx.types_with_notable_traits.borrow_mut().insert(ty.clone());
1513 fmt::from_fn(|f| {
1514 write!(
1515 f,
1516 " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>",
1517 ty = Escape(&format!("{:#}", ty.print(cx))),
1518 )
1519 })
1520 })
1521}
1522
1523fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
1524 let mut out = String::new();
1525
1526 let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
1527
1528 let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");
1529
1530 for i in impls {
1531 let impl_ = i.inner_impl();
1532 if impl_.polarity != ty::ImplPolarity::Positive {
1533 continue;
1534 }
1535
1536 if !ty.is_doc_subtype_of(&impl_.for_, cx.cache()) {
1537 continue;
1540 }
1541 if let Some(trait_) = &impl_.trait_ {
1542 let trait_did = trait_.def_id();
1543
1544 if cx.cache().traits.get(&trait_did).is_some_and(|t| t.is_notable_trait(cx.tcx())) {
1545 if out.is_empty() {
1546 write_str(
1547 &mut out,
1548 format_args!(
1549 "<h3>Notable traits for <code>{}</code></h3>\
1550 <pre><code>",
1551 impl_.for_.print(cx)
1552 ),
1553 );
1554 }
1555
1556 write_str(
1557 &mut out,
1558 format_args!("<div class=\"where\">{}</div>", impl_.print(false, cx)),
1559 );
1560 for it in &impl_.items {
1561 if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
1562 let empty_set = FxIndexSet::default();
1563 let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
1564 write_str(
1565 &mut out,
1566 format_args!(
1567 "<div class=\"where\"> {};</div>",
1568 assoc_type(
1569 it,
1570 &tydef.generics,
1571 &[], Some(&tydef.type_),
1573 src_link,
1574 0,
1575 cx,
1576 )
1577 ),
1578 );
1579 }
1580 }
1581 }
1582 }
1583 }
1584 if out.is_empty() {
1585 out.push_str("</code></pre>");
1586 }
1587
1588 (format!("{:#}", ty.print(cx)), out)
1589}
1590
1591pub(crate) fn notable_traits_json<'a>(
1592 tys: impl Iterator<Item = &'a clean::Type>,
1593 cx: &Context<'_>,
1594) -> String {
1595 let mut mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect();
1596 mp.sort_by(|(name1, _html1), (name2, _html2)| name1.cmp(name2));
1597 struct NotableTraitsMap(Vec<(String, String)>);
1598 impl Serialize for NotableTraitsMap {
1599 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1600 where
1601 S: Serializer,
1602 {
1603 let mut map = serializer.serialize_map(Some(self.0.len()))?;
1604 for item in &self.0 {
1605 map.serialize_entry(&item.0, &item.1)?;
1606 }
1607 map.end()
1608 }
1609 }
1610 serde_json::to_string(&NotableTraitsMap(mp))
1611 .expect("serialize (string, string) -> json object cannot fail")
1612}
1613
1614#[derive(Clone, Copy, Debug)]
1615struct ImplRenderingParameters {
1616 show_def_docs: bool,
1617 show_default_items: bool,
1618 show_non_assoc_items: bool,
1620 toggle_open_by_default: bool,
1621}
1622
1623fn render_impl(
1624 cx: &Context<'_>,
1625 i: &Impl,
1626 parent: &clean::Item,
1627 link: AssocItemLink<'_>,
1628 render_mode: RenderMode,
1629 use_absolute: Option<bool>,
1630 aliases: &[String],
1631 rendering_params: ImplRenderingParameters,
1632) -> impl fmt::Display {
1633 fmt::from_fn(move |w| {
1634 let cache = &cx.shared.cache;
1635 let traits = &cache.traits;
1636 let trait_ = i.trait_did().map(|did| &traits[&did]);
1637 let mut close_tags = <Vec<&str>>::with_capacity(2);
1638
1639 fn doc_impl_item(
1645 boring: &mut String,
1646 interesting: &mut String,
1647 cx: &Context<'_>,
1648 item: &clean::Item,
1649 parent: &clean::Item,
1650 link: AssocItemLink<'_>,
1651 render_mode: RenderMode,
1652 is_default_item: bool,
1653 trait_: Option<&clean::Trait>,
1654 rendering_params: ImplRenderingParameters,
1655 ) {
1656 let item_type = item.type_();
1657 let name = item.name.as_ref().unwrap();
1658
1659 let render_method_item = rendering_params.show_non_assoc_items
1660 && match render_mode {
1661 RenderMode::Normal => true,
1662 RenderMode::ForDeref { mut_: deref_mut_ } => {
1663 should_render_item(item, deref_mut_, cx.tcx())
1664 }
1665 };
1666
1667 let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
1668
1669 let mut doc_buffer = String::new();
1670 let mut info_buffer = String::new();
1671 let mut short_documented = true;
1672
1673 if render_method_item {
1674 if !is_default_item {
1675 if let Some(t) = trait_ {
1676 if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1679 if !item.doc_value().is_empty() {
1682 document_item_info(cx, it, Some(parent))
1683 .render_into(&mut info_buffer)
1684 .unwrap();
1685 write_str(
1686 &mut doc_buffer,
1687 format_args!("{}", document_full(item, cx, HeadingOffset::H5)),
1688 );
1689 short_documented = false;
1690 } else {
1691 write_str(
1694 &mut doc_buffer,
1695 format_args!(
1696 "{}",
1697 document_short(
1698 it,
1699 cx,
1700 link,
1701 parent,
1702 rendering_params.show_def_docs,
1703 )
1704 ),
1705 );
1706 }
1707 }
1708 } else {
1709 document_item_info(cx, item, Some(parent))
1710 .render_into(&mut info_buffer)
1711 .unwrap();
1712 if rendering_params.show_def_docs {
1713 write_str(
1714 &mut doc_buffer,
1715 format_args!("{}", document_full(item, cx, HeadingOffset::H5)),
1716 );
1717 short_documented = false;
1718 }
1719 }
1720 } else {
1721 write_str(
1722 &mut doc_buffer,
1723 format_args!(
1724 "{}",
1725 document_short(item, cx, link, parent, rendering_params.show_def_docs)
1726 ),
1727 );
1728 }
1729 }
1730 let w = if short_documented && trait_.is_some() { interesting } else { boring };
1731
1732 let toggled = !doc_buffer.is_empty();
1733 if toggled {
1734 let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
1735 write_str(
1736 w,
1737 format_args!("<details class=\"toggle{method_toggle_class}\" open><summary>"),
1738 );
1739 }
1740 match &item.kind {
1741 clean::MethodItem(..) | clean::RequiredMethodItem(_) => {
1742 if render_method_item {
1744 let id = cx.derive_id(format!("{item_type}.{name}"));
1745 let source_id = trait_
1746 .and_then(|trait_| {
1747 trait_
1748 .items
1749 .iter()
1750 .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
1751 })
1752 .map(|item| format!("{}.{name}", item.type_()));
1753 write_str(
1754 w,
1755 format_args!(
1756 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1757 {}",
1758 render_rightside(cx, item, render_mode)
1759 ),
1760 );
1761 if trait_.is_some() {
1762 write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
1764 }
1765 write_str(
1766 w,
1767 format_args!(
1768 "<h4 class=\"code-header\">{}</h4></section>",
1769 render_assoc_item(
1770 item,
1771 link.anchor(source_id.as_ref().unwrap_or(&id)),
1772 ItemType::Impl,
1773 cx,
1774 render_mode,
1775 ),
1776 ),
1777 );
1778 }
1779 }
1780 clean::RequiredAssocConstItem(generics, ty) => {
1781 let source_id = format!("{item_type}.{name}");
1782 let id = cx.derive_id(&source_id);
1783 write_str(
1784 w,
1785 format_args!(
1786 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1787 {}",
1788 render_rightside(cx, item, render_mode)
1789 ),
1790 );
1791 if trait_.is_some() {
1792 write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
1794 }
1795 write_str(
1796 w,
1797 format_args!(
1798 "<h4 class=\"code-header\">{}</h4></section>",
1799 assoc_const(
1800 item,
1801 generics,
1802 ty,
1803 AssocConstValue::None,
1804 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1805 0,
1806 cx,
1807 )
1808 ),
1809 );
1810 }
1811 clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
1812 let source_id = format!("{item_type}.{name}");
1813 let id = cx.derive_id(&source_id);
1814 write_str(
1815 w,
1816 format_args!(
1817 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1818 {}",
1819 render_rightside(cx, item, render_mode)
1820 ),
1821 );
1822 if trait_.is_some() {
1823 write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
1825 }
1826 write_str(
1827 w,
1828 format_args!(
1829 "<h4 class=\"code-header\">{}</h4></section>",
1830 assoc_const(
1831 item,
1832 &ci.generics,
1833 &ci.type_,
1834 match item.kind {
1835 clean::ProvidedAssocConstItem(_) =>
1836 AssocConstValue::TraitDefault(&ci.kind),
1837 clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
1838 _ => unreachable!(),
1839 },
1840 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1841 0,
1842 cx,
1843 )
1844 ),
1845 );
1846 }
1847 clean::RequiredAssocTypeItem(generics, bounds) => {
1848 let source_id = format!("{item_type}.{name}");
1849 let id = cx.derive_id(&source_id);
1850 write_str(
1851 w,
1852 format_args!(
1853 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1854 {}",
1855 render_rightside(cx, item, render_mode)
1856 ),
1857 );
1858 if trait_.is_some() {
1859 write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
1861 }
1862 write_str(
1863 w,
1864 format_args!(
1865 "<h4 class=\"code-header\">{}</h4></section>",
1866 assoc_type(
1867 item,
1868 generics,
1869 bounds,
1870 None,
1871 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1872 0,
1873 cx,
1874 )
1875 ),
1876 );
1877 }
1878 clean::AssocTypeItem(tydef, _bounds) => {
1879 let source_id = format!("{item_type}.{name}");
1880 let id = cx.derive_id(&source_id);
1881 write_str(
1882 w,
1883 format_args!(
1884 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1885 {}",
1886 render_rightside(cx, item, render_mode)
1887 ),
1888 );
1889 if trait_.is_some() {
1890 write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
1892 }
1893 write_str(
1894 w,
1895 format_args!(
1896 "<h4 class=\"code-header\">{}</h4></section>",
1897 assoc_type(
1898 item,
1899 &tydef.generics,
1900 &[], Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
1902 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1903 0,
1904 cx,
1905 )
1906 ),
1907 );
1908 }
1909 clean::StrippedItem(..) => return,
1910 _ => panic!("can't make docs for trait item with name {:?}", item.name),
1911 }
1912
1913 w.push_str(&info_buffer);
1914 if toggled {
1915 w.push_str("</summary>");
1916 w.push_str(&doc_buffer);
1917 w.push_str("</details>");
1918 }
1919 }
1920
1921 let mut impl_items = String::new();
1922 let mut default_impl_items = String::new();
1923 let impl_ = i.inner_impl();
1924
1925 let mut assoc_types = Vec::new();
1935 let mut methods = Vec::new();
1936
1937 if !impl_.is_negative_trait_impl() {
1938 for trait_item in &impl_.items {
1939 match trait_item.kind {
1940 clean::MethodItem(..) | clean::RequiredMethodItem(_) => {
1941 methods.push(trait_item)
1942 }
1943 clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => {
1944 assoc_types.push(trait_item)
1945 }
1946 clean::RequiredAssocConstItem(..)
1947 | clean::ProvidedAssocConstItem(_)
1948 | clean::ImplAssocConstItem(_) => {
1949 doc_impl_item(
1951 &mut default_impl_items,
1952 &mut impl_items,
1953 cx,
1954 trait_item,
1955 if trait_.is_some() { &i.impl_item } else { parent },
1956 link,
1957 render_mode,
1958 false,
1959 trait_,
1960 rendering_params,
1961 );
1962 }
1963 _ => {}
1964 }
1965 }
1966
1967 for assoc_type in assoc_types {
1968 doc_impl_item(
1969 &mut default_impl_items,
1970 &mut impl_items,
1971 cx,
1972 assoc_type,
1973 if trait_.is_some() { &i.impl_item } else { parent },
1974 link,
1975 render_mode,
1976 false,
1977 trait_,
1978 rendering_params,
1979 );
1980 }
1981 for method in methods {
1982 doc_impl_item(
1983 &mut default_impl_items,
1984 &mut impl_items,
1985 cx,
1986 method,
1987 if trait_.is_some() { &i.impl_item } else { parent },
1988 link,
1989 render_mode,
1990 false,
1991 trait_,
1992 rendering_params,
1993 );
1994 }
1995 }
1996
1997 fn render_default_items(
1998 boring: &mut String,
1999 interesting: &mut String,
2000 cx: &Context<'_>,
2001 t: &clean::Trait,
2002 i: &clean::Impl,
2003 parent: &clean::Item,
2004 render_mode: RenderMode,
2005 rendering_params: ImplRenderingParameters,
2006 ) {
2007 for trait_item in &t.items {
2008 if let Some(impl_def_id) = parent.item_id.as_def_id()
2011 && let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
2012 && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id))
2013 {
2014 continue;
2015 }
2016
2017 let n = trait_item.name;
2018 if i.items.iter().any(|m| m.name == n) {
2019 continue;
2020 }
2021 let did = i.trait_.as_ref().unwrap().def_id();
2022 let provided_methods = i.provided_trait_methods(cx.tcx());
2023 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
2024
2025 doc_impl_item(
2026 boring,
2027 interesting,
2028 cx,
2029 trait_item,
2030 parent,
2031 assoc_link,
2032 render_mode,
2033 true,
2034 Some(t),
2035 rendering_params,
2036 );
2037 }
2038 }
2039
2040 if rendering_params.show_default_items {
2045 if let Some(t) = trait_
2046 && !impl_.is_negative_trait_impl()
2047 {
2048 render_default_items(
2049 &mut default_impl_items,
2050 &mut impl_items,
2051 cx,
2052 t,
2053 impl_,
2054 &i.impl_item,
2055 render_mode,
2056 rendering_params,
2057 );
2058 }
2059 }
2060 if render_mode == RenderMode::Normal {
2061 let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
2062 if toggled {
2063 close_tags.push("</details>");
2064 write!(
2065 w,
2066 "<details class=\"toggle implementors-toggle\"{}>\
2067 <summary>",
2068 if rendering_params.toggle_open_by_default { " open" } else { "" }
2069 )?;
2070 }
2071
2072 let (before_dox, after_dox) = i
2073 .impl_item
2074 .opt_doc_value()
2075 .map(|dox| {
2076 Markdown {
2077 content: &dox,
2078 links: &i.impl_item.links(cx),
2079 ids: &mut cx.id_map.borrow_mut(),
2080 error_codes: cx.shared.codes,
2081 edition: cx.shared.edition(),
2082 playground: &cx.shared.playground,
2083 heading_offset: HeadingOffset::H4,
2084 }
2085 .split_summary_and_content()
2086 })
2087 .unwrap_or((None, None));
2088 write!(
2089 w,
2090 "{}",
2091 render_impl_summary(
2092 cx,
2093 i,
2094 parent,
2095 rendering_params.show_def_docs,
2096 use_absolute,
2097 aliases,
2098 before_dox.as_deref(),
2099 )
2100 )?;
2101 if toggled {
2102 w.write_str("</summary>")?;
2103 }
2104
2105 if before_dox.is_some() {
2106 if trait_.is_none() && impl_.items.is_empty() {
2107 w.write_str(
2108 "<div class=\"item-info\">\
2109 <div class=\"stab empty-impl\">This impl block contains no items.</div>\
2110 </div>",
2111 )?;
2112 }
2113 if let Some(after_dox) = after_dox {
2114 write!(w, "<div class=\"docblock\">{after_dox}</div>")?;
2115 }
2116 }
2117 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2118 w.write_str("<div class=\"impl-items\">")?;
2119 close_tags.push("</div>");
2120 }
2121 }
2122 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2123 w.write_str(&default_impl_items)?;
2124 w.write_str(&impl_items)?;
2125 }
2126 for tag in close_tags.into_iter().rev() {
2127 w.write_str(tag)?;
2128 }
2129 Ok(())
2130 })
2131}
2132
2133fn render_rightside(
2136 cx: &Context<'_>,
2137 item: &clean::Item,
2138 render_mode: RenderMode,
2139) -> impl fmt::Display {
2140 let tcx = cx.tcx();
2141
2142 fmt::from_fn(move |w| {
2143 let const_stability = match render_mode {
2146 RenderMode::Normal => item.const_stability(tcx),
2147 RenderMode::ForDeref { .. } => None,
2148 };
2149 let src_href = cx.src_href(item);
2150 let stability = render_stability_since_raw_with_extra(
2151 item.stable_since(tcx),
2152 const_stability,
2153 if src_href.is_some() { "" } else { " rightside" },
2154 );
2155
2156 match (stability, src_href) {
2157 (Some(stability), Some(link)) => {
2158 write!(
2159 w,
2160 "<span class=\"rightside\">{stability} · <a class=\"src\" href=\"{link}\">Source</a></span>",
2161 )
2162 }
2163 (Some(stability), None) => {
2164 write!(w, "{stability}")
2165 }
2166 (None, Some(link)) => {
2167 write!(w, "<a class=\"src rightside\" href=\"{link}\">Source</a>")
2168 }
2169 (None, None) => Ok(()),
2170 }
2171 })
2172}
2173
2174pub(crate) fn render_impl_summary(
2175 cx: &Context<'_>,
2176 i: &Impl,
2177 parent: &clean::Item,
2178 show_def_docs: bool,
2179 use_absolute: Option<bool>,
2180 aliases: &[String],
2183 doc: Option<&str>,
2184) -> impl fmt::Display {
2185 fmt::from_fn(move |w| {
2186 let inner_impl = i.inner_impl();
2187 let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
2188 let aliases = (!aliases.is_empty())
2189 .then_some(fmt::from_fn(|f| {
2190 write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f)))
2191 }))
2192 .maybe_display();
2193 write!(
2194 w,
2195 "<section id=\"{id}\" class=\"impl\"{aliases}>\
2196 {}\
2197 <a href=\"#{id}\" class=\"anchor\">§</a>\
2198 <h3 class=\"code-header\">",
2199 render_rightside(cx, &i.impl_item, RenderMode::Normal)
2200 )?;
2201
2202 if let Some(use_absolute) = use_absolute {
2203 write!(w, "{}", inner_impl.print(use_absolute, cx))?;
2204 if show_def_docs {
2205 for it in &inner_impl.items {
2206 if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
2207 write!(
2208 w,
2209 "<div class=\"where\"> {};</div>",
2210 assoc_type(
2211 it,
2212 &tydef.generics,
2213 &[], Some(&tydef.type_),
2215 AssocItemLink::Anchor(None),
2216 0,
2217 cx,
2218 )
2219 )?;
2220 }
2221 }
2222 }
2223 } else {
2224 write!(w, "{}", inner_impl.print(false, cx))?;
2225 }
2226 w.write_str("</h3>")?;
2227
2228 let is_trait = inner_impl.trait_.is_some();
2229 if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) {
2230 write!(
2231 w,
2232 "<span class=\"item-info\">\
2233 <div class=\"stab portability\">{portability}</div>\
2234 </span>",
2235 )?;
2236 }
2237
2238 if let Some(doc) = doc {
2239 write!(w, "<div class=\"docblock\">{doc}</div>")?;
2240 }
2241
2242 w.write_str("</section>")
2243 })
2244}
2245
2246pub(crate) fn small_url_encode(s: String) -> String {
2247 fn dont_escape(c: u8) -> bool {
2252 c.is_ascii_alphanumeric()
2253 || c == b'-'
2254 || c == b'_'
2255 || c == b'.'
2256 || c == b','
2257 || c == b'~'
2258 || c == b'!'
2259 || c == b'\''
2260 || c == b'('
2261 || c == b')'
2262 || c == b'*'
2263 || c == b'/'
2264 || c == b';'
2265 || c == b':'
2266 || c == b'?'
2267 || c == b'='
2271 }
2272 let mut st = String::new();
2273 let mut last_match = 0;
2274 for (idx, b) in s.bytes().enumerate() {
2275 if dont_escape(b) {
2276 continue;
2277 }
2278
2279 if last_match != idx {
2280 st += &s[last_match..idx];
2282 }
2283 if b == b' ' {
2284 st += "+";
2288 } else {
2289 write!(st, "%{b:02X}").unwrap();
2290 }
2291 last_match = idx + 1;
2297 }
2298
2299 if last_match != 0 {
2300 st += &s[last_match..];
2301 st
2302 } else {
2303 s
2304 }
2305}
2306
2307fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String {
2308 use rustc_middle::ty::print::with_forced_trimmed_paths;
2309 let (type_, trait_) = match impl_id {
2310 ItemId::Auto { trait_, for_ } => {
2311 let ty = tcx.type_of(for_).skip_binder();
2312 (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
2313 }
2314 ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
2315 match tcx.impl_subject(impl_id).skip_binder() {
2316 ty::ImplSubject::Trait(trait_ref) => {
2317 (trait_ref.args[0].expect_ty(), Some(trait_ref))
2318 }
2319 ty::ImplSubject::Inherent(ty) => (ty, None),
2320 }
2321 }
2322 };
2323 with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
2324 format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
2325 } else {
2326 format!("impl-{type_}")
2327 }))
2328}
2329
2330fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2331 match item.kind {
2332 clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
2333 Some((format!("{:#}", i.for_.print(cx)), get_id_for_impl(cx.tcx(), item.item_id)))
2336 }
2337 _ => None,
2338 }
2339}
2340
2341pub(crate) fn get_filtered_impls_for_reference<'a>(
2345 shared: &'a SharedContext<'_>,
2346 it: &clean::Item,
2347) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
2348 let def_id = it.item_id.expect_def_id();
2349 let Some(v) = shared.cache.impls.get(&def_id) else {
2351 return (Vec::new(), Vec::new(), Vec::new());
2352 };
2353 let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
2356 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
2357 traits.partition(|t| t.inner_impl().kind.is_auto());
2358
2359 let (blanket_impl, concrete): (Vec<&Impl>, _) =
2360 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
2361 let concrete: Vec<_> = concrete
2363 .into_iter()
2364 .filter(|t| match t.inner_impl().for_ {
2365 clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
2366 _ => false,
2367 })
2368 .collect();
2369
2370 (concrete, synthetic, blanket_impl)
2371}
2372
2373#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2374pub(crate) enum ItemSection {
2375 Reexports,
2376 PrimitiveTypes,
2377 Modules,
2378 Macros,
2379 Structs,
2380 Enums,
2381 Constants,
2382 Statics,
2383 Traits,
2384 Functions,
2385 TypeAliases,
2386 Unions,
2387 Implementations,
2388 TypeMethods,
2389 Methods,
2390 StructFields,
2391 Variants,
2392 AssociatedTypes,
2393 AssociatedConstants,
2394 ForeignTypes,
2395 Keywords,
2396 AttributeMacros,
2397 DeriveMacros,
2398 TraitAliases,
2399}
2400
2401impl ItemSection {
2402 const ALL: &'static [Self] = {
2403 use ItemSection::*;
2404 &[
2407 Reexports,
2408 PrimitiveTypes,
2409 Modules,
2410 Macros,
2411 Structs,
2412 Enums,
2413 Constants,
2414 Statics,
2415 Traits,
2416 Functions,
2417 TypeAliases,
2418 Unions,
2419 Implementations,
2420 TypeMethods,
2421 Methods,
2422 StructFields,
2423 Variants,
2424 AssociatedTypes,
2425 AssociatedConstants,
2426 ForeignTypes,
2427 Keywords,
2428 AttributeMacros,
2429 DeriveMacros,
2430 TraitAliases,
2431 ]
2432 };
2433
2434 fn id(self) -> &'static str {
2435 match self {
2436 Self::Reexports => "reexports",
2437 Self::Modules => "modules",
2438 Self::Structs => "structs",
2439 Self::Unions => "unions",
2440 Self::Enums => "enums",
2441 Self::Functions => "functions",
2442 Self::TypeAliases => "types",
2443 Self::Statics => "statics",
2444 Self::Constants => "constants",
2445 Self::Traits => "traits",
2446 Self::Implementations => "impls",
2447 Self::TypeMethods => "tymethods",
2448 Self::Methods => "methods",
2449 Self::StructFields => "fields",
2450 Self::Variants => "variants",
2451 Self::Macros => "macros",
2452 Self::PrimitiveTypes => "primitives",
2453 Self::AssociatedTypes => "associated-types",
2454 Self::AssociatedConstants => "associated-consts",
2455 Self::ForeignTypes => "foreign-types",
2456 Self::Keywords => "keywords",
2457 Self::AttributeMacros => "attributes",
2458 Self::DeriveMacros => "derives",
2459 Self::TraitAliases => "trait-aliases",
2460 }
2461 }
2462
2463 fn name(self) -> &'static str {
2464 match self {
2465 Self::Reexports => "Re-exports",
2466 Self::Modules => "Modules",
2467 Self::Structs => "Structs",
2468 Self::Unions => "Unions",
2469 Self::Enums => "Enums",
2470 Self::Functions => "Functions",
2471 Self::TypeAliases => "Type Aliases",
2472 Self::Statics => "Statics",
2473 Self::Constants => "Constants",
2474 Self::Traits => "Traits",
2475 Self::Implementations => "Implementations",
2476 Self::TypeMethods => "Type Methods",
2477 Self::Methods => "Methods",
2478 Self::StructFields => "Struct Fields",
2479 Self::Variants => "Variants",
2480 Self::Macros => "Macros",
2481 Self::PrimitiveTypes => "Primitive Types",
2482 Self::AssociatedTypes => "Associated Types",
2483 Self::AssociatedConstants => "Associated Constants",
2484 Self::ForeignTypes => "Foreign Types",
2485 Self::Keywords => "Keywords",
2486 Self::AttributeMacros => "Attribute Macros",
2487 Self::DeriveMacros => "Derive Macros",
2488 Self::TraitAliases => "Trait Aliases",
2489 }
2490 }
2491}
2492
2493fn item_ty_to_section(ty: ItemType) -> ItemSection {
2494 match ty {
2495 ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
2496 ItemType::Module => ItemSection::Modules,
2497 ItemType::Struct => ItemSection::Structs,
2498 ItemType::Union => ItemSection::Unions,
2499 ItemType::Enum => ItemSection::Enums,
2500 ItemType::Function => ItemSection::Functions,
2501 ItemType::TypeAlias => ItemSection::TypeAliases,
2502 ItemType::Static => ItemSection::Statics,
2503 ItemType::Constant => ItemSection::Constants,
2504 ItemType::Trait => ItemSection::Traits,
2505 ItemType::Impl => ItemSection::Implementations,
2506 ItemType::TyMethod => ItemSection::TypeMethods,
2507 ItemType::Method => ItemSection::Methods,
2508 ItemType::StructField => ItemSection::StructFields,
2509 ItemType::Variant => ItemSection::Variants,
2510 ItemType::Macro => ItemSection::Macros,
2511 ItemType::Primitive => ItemSection::PrimitiveTypes,
2512 ItemType::AssocType => ItemSection::AssociatedTypes,
2513 ItemType::AssocConst => ItemSection::AssociatedConstants,
2514 ItemType::ForeignType => ItemSection::ForeignTypes,
2515 ItemType::Keyword => ItemSection::Keywords,
2516 ItemType::ProcAttribute => ItemSection::AttributeMacros,
2517 ItemType::ProcDerive => ItemSection::DeriveMacros,
2518 ItemType::TraitAlias => ItemSection::TraitAliases,
2519 }
2520}
2521
2522fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
2529 let mut out = Vec::new();
2530 let mut visited = FxHashSet::default();
2531 let mut work = VecDeque::new();
2532
2533 let mut process_path = |did: DefId| {
2534 let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
2535 let fqp = cache.exact_paths.get(&did).or_else(get_extern);
2536
2537 if let Some(path) = fqp {
2538 out.push(join_with_double_colon(path));
2539 }
2540 };
2541
2542 work.push_back(first_ty);
2543
2544 while let Some(ty) = work.pop_front() {
2545 if !visited.insert(ty.clone()) {
2546 continue;
2547 }
2548
2549 match ty {
2550 clean::Type::Path { path } => process_path(path.def_id()),
2551 clean::Type::Tuple(tys) => {
2552 work.extend(tys.into_iter());
2553 }
2554 clean::Type::Slice(ty) => {
2555 work.push_back(*ty);
2556 }
2557 clean::Type::Array(ty, _) => {
2558 work.push_back(*ty);
2559 }
2560 clean::Type::RawPointer(_, ty) => {
2561 work.push_back(*ty);
2562 }
2563 clean::Type::BorrowedRef { type_, .. } => {
2564 work.push_back(*type_);
2565 }
2566 clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
2567 work.push_back(self_type);
2568 if let Some(trait_) = trait_ {
2569 process_path(trait_.def_id());
2570 }
2571 }
2572 _ => {}
2573 }
2574 }
2575 out
2576}
2577
2578const MAX_FULL_EXAMPLES: usize = 5;
2579const NUM_VISIBLE_LINES: usize = 10;
2580
2581fn render_call_locations<W: fmt::Write>(mut w: W, cx: &Context<'_>, item: &clean::Item) {
2583 let tcx = cx.tcx();
2584 let def_id = item.item_id.expect_def_id();
2585 let key = tcx.def_path_hash(def_id);
2586 let Some(call_locations) = cx.shared.call_locations.get(&key) else { return };
2587
2588 let id = cx.derive_id("scraped-examples");
2590 write!(
2591 &mut w,
2592 "<div class=\"docblock scraped-example-list\">\
2593 <span></span>\
2594 <h5 id=\"{id}\">\
2595 <a href=\"#{id}\">Examples found in repository</a>\
2596 <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
2597 </h5>",
2598 root_path = cx.root_path(),
2599 id = id
2600 )
2601 .unwrap();
2602
2603 let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
2605 let (line_lo, line_hi) = loc.call_expr.line_span;
2606 let (anchor, title) = if line_lo == line_hi {
2607 ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
2608 } else {
2609 (
2610 format!("{}-{}", line_lo + 1, line_hi + 1),
2611 format!("lines {}-{}", line_lo + 1, line_hi + 1),
2612 )
2613 };
2614 let url = format!("{}{}#{anchor}", cx.root_path(), call_data.url);
2615 (url, title)
2616 };
2617
2618 let write_example = |w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
2620 let contents = match fs::read_to_string(path) {
2621 Ok(contents) => contents,
2622 Err(err) => {
2623 let span = item.span(tcx).map_or(DUMMY_SP, |span| span.inner());
2624 tcx.dcx().span_err(span, format!("failed to read file {}: {err}", path.display()));
2625 return false;
2626 }
2627 };
2628
2629 assert!(!call_data.locations.is_empty());
2632 let min_loc =
2633 call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2634 let byte_min = min_loc.enclosing_item.byte_span.0;
2635 let line_min = min_loc.enclosing_item.line_span.0;
2636 let max_loc =
2637 call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
2638 let byte_max = max_loc.enclosing_item.byte_span.1;
2639 let line_max = max_loc.enclosing_item.line_span.1;
2640
2641 let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
2643
2644 let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
2647 .locations
2648 .iter()
2649 .map(|loc| {
2650 let (byte_lo, byte_hi) = loc.call_ident.byte_span;
2651 let (line_lo, line_hi) = loc.call_expr.line_span;
2652 let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
2653
2654 let line_range = (line_lo - line_min, line_hi - line_min);
2655 let (line_url, line_title) = link_to_loc(call_data, loc);
2656
2657 (byte_range, (line_range, line_url, line_title))
2658 })
2659 .unzip();
2660
2661 let (_, init_url, init_title) = &line_ranges[0];
2662 let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
2663 let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
2664
2665 let file_span = (|| {
2667 let source_map = tcx.sess.source_map();
2668 let crate_src = tcx.sess.local_crate_source_file()?.into_local_path()?;
2669 let abs_crate_src = crate_src.canonicalize().ok()?;
2670 let crate_root = abs_crate_src.parent()?.parent()?;
2671 let rel_path = path.strip_prefix(crate_root).ok()?;
2672 let files = source_map.files();
2673 let file = files.iter().find(|file| match &file.name {
2674 FileName::Real(RealFileName::LocalPath(other_path)) => rel_path == other_path,
2675 _ => false,
2676 })?;
2677 Some(rustc_span::Span::with_root_ctxt(
2678 file.start_pos + BytePos(byte_min),
2679 file.start_pos + BytePos(byte_max),
2680 ))
2681 })()
2682 .unwrap_or(DUMMY_SP);
2683
2684 let mut decoration_info = FxIndexMap::default();
2685 decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
2686 decoration_info.insert("highlight", byte_ranges);
2687
2688 sources::print_src(
2689 w,
2690 contents_subset,
2691 file_span,
2692 cx,
2693 &cx.root_path(),
2694 &highlight::DecorationInfo(decoration_info),
2695 &sources::SourceContext::Embedded(sources::ScrapedInfo {
2696 needs_expansion,
2697 offset: line_min,
2698 name: &call_data.display_name,
2699 url: init_url,
2700 title: init_title,
2701 locations: locations_encoded,
2702 }),
2703 );
2704
2705 true
2706 };
2707
2708 let ordered_locations = {
2720 fn sort_criterion<'a>(
2721 (_, call_data): &(&PathBuf, &'a CallData),
2722 ) -> (bool, u32, &'a String) {
2723 let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2725 (!call_data.is_bin, hi - lo, &call_data.display_name)
2726 }
2727
2728 let mut locs = call_locations.iter().collect::<Vec<_>>();
2729 locs.sort_by_key(sort_criterion);
2730 locs
2731 };
2732
2733 let mut it = ordered_locations.into_iter().peekable();
2734
2735 let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
2738 for example in it.by_ref() {
2739 if write_example(&mut *w, example) {
2740 break;
2741 }
2742 }
2743 };
2744
2745 write_and_skip_failure(&mut w, &mut it);
2747
2748 if it.peek().is_some() {
2750 write!(
2751 w,
2752 "<details class=\"toggle more-examples-toggle\">\
2753 <summary class=\"hideme\">\
2754 <span>More examples</span>\
2755 </summary>\
2756 <div class=\"hide-more\">Hide additional examples</div>\
2757 <div class=\"more-scraped-examples\">\
2758 <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
2759 )
2760 .unwrap();
2761
2762 for _ in 0..MAX_FULL_EXAMPLES {
2765 write_and_skip_failure(&mut w, &mut it);
2766 }
2767
2768 if it.peek().is_some() {
2770 w.write_str(
2771 r#"<div class="example-links">Additional examples can be found in:<br><ul>"#,
2772 )
2773 .unwrap();
2774 it.for_each(|(_, call_data)| {
2775 let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
2776 write!(
2777 w,
2778 r#"<li><a href="{url}">{name}</a></li>"#,
2779 url = url,
2780 name = call_data.display_name
2781 )
2782 .unwrap();
2783 });
2784 w.write_str("</ul></div>").unwrap();
2785 }
2786
2787 w.write_str("</div></details>").unwrap();
2788 }
2789
2790 w.write_str("</div>").unwrap();
2791}