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, 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::captures::Captures;
51use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
52use rustc_hir::Mutability;
53use rustc_hir::def_id::{DefId, DefIdSet};
54use rustc_middle::ty::print::PrintTraitRefExt;
55use rustc_middle::ty::{self, TyCtxt};
56use rustc_span::symbol::{Symbol, sym};
57use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName};
58use serde::ser::SerializeMap;
59use serde::{Serialize, Serializer};
60use tracing::{debug, info};
61
62pub(crate) use self::context::*;
63pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources};
64pub(crate) use self::write_shared::*;
65use crate::clean::{self, ItemId, RenderedLink};
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<'a, 'cx: 'a>(
508 cx: &'a Context<'cx>,
509 item: &'a clean::Item,
510 parent: Option<&'a clean::Item>,
511 heading_offset: HeadingOffset,
512) -> impl fmt::Display + 'a + Captures<'cx> {
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<'a, 'cx: 'a>(
529 cx: &'a Context<'cx>,
530 md_text: &'a str,
531 links: Vec<RenderedLink>,
532 heading_offset: HeadingOffset,
533) -> impl fmt::Display + 'a + Captures<'cx> {
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<'a, 'cx: 'a>(
555 item: &'a clean::Item,
556 cx: &'a Context<'cx>,
557 link: AssocItemLink<'a>,
558 parent: &'a clean::Item,
559 show_def_docs: bool,
560) -> impl fmt::Display + 'a + Captures<'cx> {
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 if has_more_content {
572 let link = format!(" <a{}>Read more</a>", assoc_href_attr(item, link, cx));
573
574 if let Some(idx) = summary_html.rfind("</p>") {
575 summary_html.insert_str(idx, &link);
576 } else {
577 summary_html.push_str(&link);
578 }
579 }
580
581 write!(f, "<div class='docblock'>{summary_html}</div>")?;
582 }
583 Ok(())
584 })
585}
586
587fn document_full_collapsible<'a, 'cx: 'a>(
588 item: &'a clean::Item,
589 cx: &'a Context<'cx>,
590 heading_offset: HeadingOffset,
591) -> impl fmt::Display + 'a + Captures<'cx> {
592 document_full_inner(item, cx, true, heading_offset)
593}
594
595fn document_full<'a, 'cx: 'a>(
596 item: &'a clean::Item,
597 cx: &'a Context<'cx>,
598 heading_offset: HeadingOffset,
599) -> impl fmt::Display + 'a + Captures<'cx> {
600 document_full_inner(item, cx, false, heading_offset)
601}
602
603fn document_full_inner<'a, 'cx: 'a>(
604 item: &'a clean::Item,
605 cx: &'a Context<'cx>,
606 is_collapsible: bool,
607 heading_offset: HeadingOffset,
608) -> impl fmt::Display + 'a + Captures<'cx> {
609 fmt::from_fn(move |f| {
610 if let Some(s) = item.opt_doc_value() {
611 debug!("Doc block: =====\n{s}\n=====");
612 if is_collapsible {
613 write!(
614 f,
615 "<details class=\"toggle top-doc\" open>\
616 <summary class=\"hideme\">\
617 <span>Expand description</span>\
618 </summary>{}</details>",
619 render_markdown(cx, &s, item.links(cx), heading_offset)
620 )?;
621 } else {
622 write!(f, "{}", render_markdown(cx, &s, item.links(cx), heading_offset))?;
623 }
624 }
625
626 let kind = match &item.kind {
627 clean::ItemKind::StrippedItem(box kind) | kind => kind,
628 };
629
630 if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind {
631 render_call_locations(f, cx, item);
632 }
633 Ok(())
634 })
635}
636
637#[derive(Template)]
638#[template(path = "item_info.html")]
639struct ItemInfo {
640 items: Vec<ShortItemInfo>,
641}
642fn document_item_info(
648 cx: &Context<'_>,
649 item: &clean::Item,
650 parent: Option<&clean::Item>,
651) -> ItemInfo {
652 let items = short_item_info(item, cx, parent);
653 ItemInfo { items }
654}
655
656fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> {
657 let cfg = match (&item.cfg, parent.and_then(|p| p.cfg.as_ref())) {
658 (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
659 (cfg, _) => cfg.as_deref().cloned(),
660 };
661
662 debug!(
663 "Portability {name:?} {item_cfg:?} (parent: {parent:?}) - {parent_cfg:?} = {cfg:?}",
664 name = item.name,
665 item_cfg = item.cfg,
666 parent_cfg = parent.and_then(|p| p.cfg.as_ref()),
667 );
668
669 Some(cfg?.render_long_html())
670}
671
672#[derive(Template)]
673#[template(path = "short_item_info.html")]
674enum ShortItemInfo {
675 Deprecation {
677 message: String,
678 },
679 Unstable {
682 feature: String,
683 tracking: Option<(String, u32)>,
684 },
685 Portability {
686 message: String,
687 },
688}
689
690fn short_item_info(
693 item: &clean::Item,
694 cx: &Context<'_>,
695 parent: Option<&clean::Item>,
696) -> Vec<ShortItemInfo> {
697 let mut extra_info = vec![];
698
699 if let Some(depr @ Deprecation { note, since, suggestion: _ }) = item.deprecation(cx.tcx()) {
700 let mut message = match since {
703 DeprecatedSince::RustcVersion(version) => {
704 if depr.is_in_effect() {
705 format!("Deprecated since {version}")
706 } else {
707 format!("Deprecating in {version}")
708 }
709 }
710 DeprecatedSince::Future => String::from("Deprecating in a future version"),
711 DeprecatedSince::NonStandard(since) => {
712 format!("Deprecated since {}", Escape(since.as_str()))
713 }
714 DeprecatedSince::Unspecified | DeprecatedSince::Err => String::from("Deprecated"),
715 };
716
717 if let Some(note) = note {
718 let note = note.as_str();
719 let mut id_map = cx.id_map.borrow_mut();
720 let html = MarkdownItemInfo(note, &mut id_map);
721 message.push_str(": ");
722 message.push_str(&html.into_string());
723 }
724 extra_info.push(ShortItemInfo::Deprecation { message });
725 }
726
727 if let Some((StabilityLevel::Unstable { reason: _, issue, .. }, feature)) = item
730 .stability(cx.tcx())
731 .as_ref()
732 .filter(|stab| stab.feature != sym::rustc_private)
733 .map(|stab| (stab.level, stab.feature))
734 {
735 let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue)
736 {
737 Some((url.clone(), issue.get()))
738 } else {
739 None
740 };
741 extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking });
742 }
743
744 if let Some(message) = portability(item, parent) {
745 extra_info.push(ShortItemInfo::Portability { message });
746 }
747
748 extra_info
749}
750
751pub(crate) fn render_impls(
754 cx: &Context<'_>,
755 mut w: impl Write,
756 impls: &[&Impl],
757 containing_item: &clean::Item,
758 toggle_open_by_default: bool,
759) {
760 let mut rendered_impls = impls
761 .iter()
762 .map(|i| {
763 let did = i.trait_did().unwrap();
764 let provided_trait_methods = i.inner_impl().provided_trait_methods(cx.tcx());
765 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods);
766 let mut buffer = String::new();
767 render_impl(
768 &mut buffer,
769 cx,
770 i,
771 containing_item,
772 assoc_link,
773 RenderMode::Normal,
774 None,
775 &[],
776 ImplRenderingParameters {
777 show_def_docs: true,
778 show_default_items: true,
779 show_non_assoc_items: true,
780 toggle_open_by_default,
781 },
782 );
783 buffer
784 })
785 .collect::<Vec<_>>();
786 rendered_impls.sort();
787 w.write_str(&rendered_impls.join("")).unwrap();
788}
789
790fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) -> String {
792 let name = it.name.unwrap();
793 let item_type = it.type_();
794
795 let href = match link {
796 AssocItemLink::Anchor(Some(ref id)) => Some(format!("#{id}")),
797 AssocItemLink::Anchor(None) => Some(format!("#{item_type}.{name}")),
798 AssocItemLink::GotoSource(did, provided_methods) => {
799 let item_type = match item_type {
802 ItemType::Method | ItemType::TyMethod => {
806 if provided_methods.contains(&name) {
807 ItemType::Method
808 } else {
809 ItemType::TyMethod
810 }
811 }
812 item_type => item_type,
814 };
815
816 match href(did.expect_def_id(), cx) {
817 Ok((url, ..)) => Some(format!("{url}#{item_type}.{name}")),
818 Err(HrefError::DocumentationNotBuilt) => None,
830 Err(_) => Some(format!("#{item_type}.{name}")),
831 }
832 }
833 };
834
835 href.map(|href| format!(" href=\"{href}\"")).unwrap_or_default()
838}
839
840#[derive(Debug)]
841enum AssocConstValue<'a> {
842 TraitDefault(&'a clean::ConstantKind),
846 Impl(&'a clean::ConstantKind),
848 None,
849}
850
851fn assoc_const(
852 w: &mut String,
853 it: &clean::Item,
854 generics: &clean::Generics,
855 ty: &clean::Type,
856 value: AssocConstValue<'_>,
857 link: AssocItemLink<'_>,
858 indent: usize,
859 cx: &Context<'_>,
860) {
861 let tcx = cx.tcx();
862 write_str(
863 w,
864 format_args!(
865 "{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
866 indent = " ".repeat(indent),
867 vis = visibility_print_with_space(it, cx),
868 href = assoc_href_attr(it, link, cx),
869 name = it.name.as_ref().unwrap(),
870 generics = generics.print(cx),
871 ty = ty.print(cx),
872 ),
873 );
874 if let AssocConstValue::TraitDefault(konst) | AssocConstValue::Impl(konst) = value {
875 let repr = konst.value(tcx).unwrap_or_else(|| konst.expr(tcx));
881 if match value {
882 AssocConstValue::TraitDefault(_) => true, AssocConstValue::Impl(_) => repr != "_", AssocConstValue::None => unreachable!(),
885 } {
886 write_str(w, format_args!(" = {}", Escape(&repr)));
887 }
888 }
889 write_str(w, format_args!("{}", print_where_clause(generics, cx, indent, Ending::NoNewline)));
890}
891
892fn assoc_type(
893 w: &mut String,
894 it: &clean::Item,
895 generics: &clean::Generics,
896 bounds: &[clean::GenericBound],
897 default: Option<&clean::Type>,
898 link: AssocItemLink<'_>,
899 indent: usize,
900 cx: &Context<'_>,
901) {
902 write_str(
903 w,
904 format_args!(
905 "{indent}{vis}type <a{href} class=\"associatedtype\">{name}</a>{generics}",
906 indent = " ".repeat(indent),
907 vis = visibility_print_with_space(it, cx),
908 href = assoc_href_attr(it, link, cx),
909 name = it.name.as_ref().unwrap(),
910 generics = generics.print(cx),
911 ),
912 );
913 if !bounds.is_empty() {
914 write_str(w, format_args!(": {}", print_generic_bounds(bounds, cx)));
915 }
916 if let Some(default) = default {
918 write_str(w, format_args!(" = {}", default.print(cx)));
919 }
920 write_str(w, format_args!("{}", print_where_clause(generics, cx, indent, Ending::NoNewline)));
921}
922
923fn assoc_method(
924 w: &mut String,
925 meth: &clean::Item,
926 g: &clean::Generics,
927 d: &clean::FnDecl,
928 link: AssocItemLink<'_>,
929 parent: ItemType,
930 cx: &Context<'_>,
931 render_mode: RenderMode,
932) {
933 let tcx = cx.tcx();
934 let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item");
935 let name = meth.name.as_ref().unwrap();
936 let vis = visibility_print_with_space(meth, cx).to_string();
937 let defaultness = print_default_space(meth.is_default());
938 let constness = match render_mode {
941 RenderMode::Normal => print_constness_with_space(
942 &header.constness,
943 meth.stable_since(tcx),
944 meth.const_stability(tcx),
945 ),
946 RenderMode::ForDeref { .. } => "",
947 };
948 let asyncness = header.asyncness.print_with_space();
949 let safety = header.safety.print_with_space();
950 let abi = print_abi_with_space(header.abi).to_string();
951 let href = assoc_href_attr(meth, link, cx);
952
953 let generics_len = format!("{:#}", g.print(cx)).len();
955 let mut header_len = "fn ".len()
956 + vis.len()
957 + defaultness.len()
958 + constness.len()
959 + asyncness.len()
960 + safety.len()
961 + abi.len()
962 + name.as_str().len()
963 + generics_len;
964
965 let notable_traits = notable_traits_button(&d.output, cx);
966
967 let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
968 header_len += 4;
969 let indent_str = " ";
970 write_str(w, format_args!("{}", render_attributes_in_pre(meth, indent_str, cx)));
971 (4, indent_str, Ending::NoNewline)
972 } else {
973 render_attributes_in_code(w, meth, cx);
974 (0, "", Ending::Newline)
975 };
976 w.reserve(header_len + "<a href=\"\" class=\"fn\">{".len() + "</a>".len());
977 write_str(
978 w,
979 format_args!(
980 "{indent}{vis}{defaultness}{constness}{asyncness}{safety}{abi}fn \
981 <a{href} class=\"fn\">{name}</a>{generics}{decl}{notable_traits}{where_clause}",
982 indent = indent_str,
983 vis = vis,
984 defaultness = defaultness,
985 constness = constness,
986 asyncness = asyncness,
987 safety = safety,
988 abi = abi,
989 href = href,
990 name = name,
991 generics = g.print(cx),
992 decl = d.full_print(header_len, indent, cx),
993 notable_traits = notable_traits.unwrap_or_default(),
994 where_clause = print_where_clause(g, cx, indent, end_newline),
995 ),
996 );
997}
998
999fn render_stability_since_raw_with_extra(
1014 w: &mut String,
1015 stable_version: Option<StableSince>,
1016 const_stability: Option<ConstStability>,
1017 extra_class: &str,
1018) -> bool {
1019 let mut title = String::new();
1020 let mut stability = String::new();
1021
1022 if let Some(version) = stable_version.and_then(|version| since_to_string(&version)) {
1023 stability.push_str(&version);
1024 title.push_str(&format!("Stable since Rust version {version}"));
1025 }
1026
1027 let const_title_and_stability = match const_stability {
1028 Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) => {
1029 since_to_string(&since)
1030 .map(|since| (format!("const since {since}"), format!("const: {since}")))
1031 }
1032 Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => {
1033 if stable_version.is_none() {
1034 None
1036 } else {
1037 let unstable = if let Some(n) = issue {
1038 format!(
1039 "<a \
1040 href=\"https://github.com/rust-lang/rust/issues/{n}\" \
1041 title=\"Tracking issue for {feature}\"\
1042 >unstable</a>"
1043 )
1044 } else {
1045 String::from("unstable")
1046 };
1047
1048 Some((String::from("const unstable"), format!("const: {unstable}")))
1049 }
1050 }
1051 _ => None,
1052 };
1053
1054 if let Some((const_title, const_stability)) = const_title_and_stability {
1055 if !title.is_empty() {
1056 title.push_str(&format!(", {const_title}"));
1057 } else {
1058 title.push_str(&const_title);
1059 }
1060
1061 if !stability.is_empty() {
1062 stability.push_str(&format!(" ({const_stability})"));
1063 } else {
1064 stability.push_str(&const_stability);
1065 }
1066 }
1067
1068 if !stability.is_empty() {
1069 write_str(
1070 w,
1071 format_args!(r#"<span class="since{extra_class}" title="{title}">{stability}</span>"#),
1072 );
1073 }
1074
1075 !stability.is_empty()
1076}
1077
1078fn since_to_string(since: &StableSince) -> Option<String> {
1079 match since {
1080 StableSince::Version(since) => Some(since.to_string()),
1081 StableSince::Current => Some(RustcVersion::CURRENT.to_string()),
1082 StableSince::Err => None,
1083 }
1084}
1085
1086#[inline]
1087fn render_stability_since_raw(
1088 w: &mut String,
1089 ver: Option<StableSince>,
1090 const_stability: Option<ConstStability>,
1091) -> bool {
1092 render_stability_since_raw_with_extra(w, ver, const_stability, "")
1093}
1094
1095fn render_assoc_item(
1096 w: &mut String,
1097 item: &clean::Item,
1098 link: AssocItemLink<'_>,
1099 parent: ItemType,
1100 cx: &Context<'_>,
1101 render_mode: RenderMode,
1102) {
1103 match &item.kind {
1104 clean::StrippedItem(..) => {}
1105 clean::RequiredMethodItem(m) => {
1106 assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode)
1107 }
1108 clean::MethodItem(m, _) => {
1109 assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode)
1110 }
1111 clean::RequiredAssocConstItem(generics, ty) => assoc_const(
1112 w,
1113 item,
1114 generics,
1115 ty,
1116 AssocConstValue::None,
1117 link,
1118 if parent == ItemType::Trait { 4 } else { 0 },
1119 cx,
1120 ),
1121 clean::ProvidedAssocConstItem(ci) => assoc_const(
1122 w,
1123 item,
1124 &ci.generics,
1125 &ci.type_,
1126 AssocConstValue::TraitDefault(&ci.kind),
1127 link,
1128 if parent == ItemType::Trait { 4 } else { 0 },
1129 cx,
1130 ),
1131 clean::ImplAssocConstItem(ci) => assoc_const(
1132 w,
1133 item,
1134 &ci.generics,
1135 &ci.type_,
1136 AssocConstValue::Impl(&ci.kind),
1137 link,
1138 if parent == ItemType::Trait { 4 } else { 0 },
1139 cx,
1140 ),
1141 clean::RequiredAssocTypeItem(ref generics, ref bounds) => assoc_type(
1142 w,
1143 item,
1144 generics,
1145 bounds,
1146 None,
1147 link,
1148 if parent == ItemType::Trait { 4 } else { 0 },
1149 cx,
1150 ),
1151 clean::AssocTypeItem(ref ty, ref bounds) => assoc_type(
1152 w,
1153 item,
1154 &ty.generics,
1155 bounds,
1156 Some(ty.item_type.as_ref().unwrap_or(&ty.type_)),
1157 link,
1158 if parent == ItemType::Trait { 4 } else { 0 },
1159 cx,
1160 ),
1161 _ => panic!("render_assoc_item called on non-associated-item"),
1162 }
1163}
1164
1165fn render_attributes_in_pre<'a, 'tcx: 'a>(
1168 it: &'a clean::Item,
1169 prefix: &'a str,
1170 cx: &'a Context<'tcx>,
1171) -> impl fmt::Display + Captures<'a> + Captures<'tcx> {
1172 fmt::from_fn(move |f| {
1173 for a in it.attributes(cx.tcx(), cx.cache(), false) {
1174 writeln!(f, "{prefix}{a}")?;
1175 }
1176 Ok(())
1177 })
1178}
1179
1180fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) {
1183 for attr in it.attributes(cx.tcx(), cx.cache(), false) {
1184 write!(w, "<div class=\"code-attribute\">{attr}</div>").unwrap();
1185 }
1186}
1187
1188#[derive(Copy, Clone)]
1189enum AssocItemLink<'a> {
1190 Anchor(Option<&'a str>),
1191 GotoSource(ItemId, &'a FxIndexSet<Symbol>),
1192}
1193
1194impl<'a> AssocItemLink<'a> {
1195 fn anchor(&self, id: &'a str) -> Self {
1196 match *self {
1197 AssocItemLink::Anchor(_) => AssocItemLink::Anchor(Some(id)),
1198 ref other => *other,
1199 }
1200 }
1201}
1202
1203pub fn write_section_heading(
1204 w: &mut impl fmt::Write,
1205 title: &str,
1206 id: &str,
1207 extra_class: Option<&str>,
1208 extra: impl fmt::Display,
1209) {
1210 let (extra_class, whitespace) = match extra_class {
1211 Some(extra) => (extra, " "),
1212 None => ("", ""),
1213 };
1214 write!(
1215 w,
1216 "<h2 id=\"{id}\" class=\"{extra_class}{whitespace}section-header\">\
1217 {title}\
1218 <a href=\"#{id}\" class=\"anchor\">ยง</a>\
1219 </h2>{extra}",
1220 )
1221 .unwrap();
1222}
1223
1224fn write_impl_section_heading(w: &mut impl fmt::Write, title: &str, id: &str) {
1225 write_section_heading(w, title, id, None, "")
1226}
1227
1228pub(crate) fn render_all_impls(
1229 mut w: impl Write,
1230 cx: &Context<'_>,
1231 containing_item: &clean::Item,
1232 concrete: &[&Impl],
1233 synthetic: &[&Impl],
1234 blanket_impl: &[&Impl],
1235) {
1236 let impls = {
1237 let mut buf = String::new();
1238 render_impls(cx, &mut buf, concrete, containing_item, true);
1239 buf
1240 };
1241 if !impls.is_empty() {
1242 write_impl_section_heading(&mut w, "Trait Implementations", "trait-implementations");
1243 write!(w, "<div id=\"trait-implementations-list\">{impls}</div>").unwrap();
1244 }
1245
1246 if !synthetic.is_empty() {
1247 write_impl_section_heading(
1248 &mut w,
1249 "Auto Trait Implementations",
1250 "synthetic-implementations",
1251 );
1252 w.write_str("<div id=\"synthetic-implementations-list\">").unwrap();
1253 render_impls(cx, &mut w, synthetic, containing_item, false);
1254 w.write_str("</div>").unwrap();
1255 }
1256
1257 if !blanket_impl.is_empty() {
1258 write_impl_section_heading(&mut w, "Blanket Implementations", "blanket-implementations");
1259 w.write_str("<div id=\"blanket-implementations-list\">").unwrap();
1260 render_impls(cx, &mut w, blanket_impl, containing_item, false);
1261 w.write_str("</div>").unwrap();
1262 }
1263}
1264
1265fn render_assoc_items<'a, 'cx: 'a>(
1266 cx: &'a Context<'cx>,
1267 containing_item: &'a clean::Item,
1268 it: DefId,
1269 what: AssocItemRender<'a>,
1270) -> impl fmt::Display + 'a + Captures<'cx> {
1271 fmt::from_fn(move |f| {
1272 let mut derefs = DefIdSet::default();
1273 derefs.insert(it);
1274 render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
1275 Ok(())
1276 })
1277}
1278
1279fn render_assoc_items_inner(
1280 mut w: &mut dyn fmt::Write,
1281 cx: &Context<'_>,
1282 containing_item: &clean::Item,
1283 it: DefId,
1284 what: AssocItemRender<'_>,
1285 derefs: &mut DefIdSet,
1286) {
1287 info!("Documenting associated items of {:?}", containing_item.name);
1288 let cache = &cx.shared.cache;
1289 let Some(v) = cache.impls.get(&it) else { return };
1290 let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
1291 if !non_trait.is_empty() {
1292 let mut close_tags = <Vec<&str>>::with_capacity(1);
1293 let mut tmp_buf = String::new();
1294 let (render_mode, id, class_html) = match what {
1295 AssocItemRender::All => {
1296 write_impl_section_heading(&mut tmp_buf, "Implementations", "implementations");
1297 (RenderMode::Normal, "implementations-list".to_owned(), "")
1298 }
1299 AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
1300 let id =
1301 cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
1302 let derived_id = cx.derive_id(&id);
1303 tmp_buf.push_str("<details class=\"toggle big-toggle\" open><summary>");
1304 close_tags.push("</details>");
1305 write_impl_section_heading(
1306 &mut tmp_buf,
1307 &format!(
1308 "<span>Methods from {trait_}<Target = {type_}></span>",
1309 trait_ = trait_.print(cx),
1310 type_ = type_.print(cx),
1311 ),
1312 &id,
1313 );
1314 tmp_buf.push_str("</summary>");
1315 if let Some(def_id) = type_.def_id(cx.cache()) {
1316 cx.deref_id_map.borrow_mut().insert(def_id, id);
1317 }
1318 (RenderMode::ForDeref { mut_: deref_mut_ }, derived_id, r#" class="impl-items""#)
1319 }
1320 };
1321 let mut impls_buf = String::new();
1322 for i in &non_trait {
1323 render_impl(
1324 &mut impls_buf,
1325 cx,
1326 i,
1327 containing_item,
1328 AssocItemLink::Anchor(None),
1329 render_mode,
1330 None,
1331 &[],
1332 ImplRenderingParameters {
1333 show_def_docs: true,
1334 show_default_items: true,
1335 show_non_assoc_items: true,
1336 toggle_open_by_default: true,
1337 },
1338 );
1339 }
1340 if !impls_buf.is_empty() {
1341 write!(w, "{tmp_buf}<div id=\"{id}\"{class_html}>{impls_buf}</div>").unwrap();
1342 for tag in close_tags.into_iter().rev() {
1343 w.write_str(tag).unwrap();
1344 }
1345 }
1346 }
1347
1348 if !traits.is_empty() {
1349 let deref_impl =
1350 traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait());
1351 if let Some(impl_) = deref_impl {
1352 let has_deref_mut =
1353 traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
1354 render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs);
1355 }
1356
1357 if let AssocItemRender::DerefFor { .. } = what {
1360 return;
1361 }
1362
1363 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
1364 traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
1365 let (blanket_impl, concrete): (Vec<&Impl>, _) =
1366 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
1367
1368 render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
1369 }
1370}
1371
1372fn render_deref_methods(
1373 mut w: impl Write,
1374 cx: &Context<'_>,
1375 impl_: &Impl,
1376 container_item: &clean::Item,
1377 deref_mut: bool,
1378 derefs: &mut DefIdSet,
1379) {
1380 let cache = cx.cache();
1381 let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
1382 let (target, real_target) = impl_
1383 .inner_impl()
1384 .items
1385 .iter()
1386 .find_map(|item| match item.kind {
1387 clean::AssocTypeItem(box ref t, _) => Some(match *t {
1388 clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
1389 _ => (&t.type_, &t.type_),
1390 }),
1391 _ => None,
1392 })
1393 .expect("Expected associated type binding");
1394 debug!(
1395 "Render deref methods for {for_:#?}, target {target:#?}",
1396 for_ = impl_.inner_impl().for_
1397 );
1398 let what =
1399 AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
1400 if let Some(did) = target.def_id(cache) {
1401 if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) {
1402 if did == type_did || !derefs.insert(did) {
1404 return;
1406 }
1407 }
1408 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
1409 } else if let Some(prim) = target.primitive_type() {
1410 if let Some(&did) = cache.primitive_locations.get(&prim) {
1411 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
1412 }
1413 }
1414}
1415
1416fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool {
1417 let self_type_opt = match item.kind {
1418 clean::MethodItem(ref method, _) => method.decl.receiver_type(),
1419 clean::RequiredMethodItem(ref method) => method.decl.receiver_type(),
1420 _ => None,
1421 };
1422
1423 if let Some(self_ty) = self_type_opt {
1424 let (by_mut_ref, by_box, by_value) = match *self_ty {
1425 clean::Type::BorrowedRef { mutability, .. } => {
1426 (mutability == Mutability::Mut, false, false)
1427 }
1428 clean::Type::Path { ref path } => {
1429 (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false)
1430 }
1431 clean::Type::SelfTy => (false, false, true),
1432 _ => (false, false, false),
1433 };
1434
1435 (deref_mut_ || !by_mut_ref) && !by_box && !by_value
1436 } else {
1437 false
1438 }
1439}
1440
1441pub(crate) fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option<String> {
1442 let mut has_notable_trait = false;
1443
1444 if ty.is_unit() {
1445 return None;
1447 }
1448
1449 let did = ty.def_id(cx.cache())?;
1450
1451 if Some(did) == cx.tcx().lang_items().owned_box()
1456 || Some(did) == cx.tcx().lang_items().pin_type()
1457 {
1458 return None;
1459 }
1460
1461 if let Some(impls) = cx.cache().impls.get(&did) {
1462 for i in impls {
1463 let impl_ = i.inner_impl();
1464 if impl_.polarity != ty::ImplPolarity::Positive {
1465 continue;
1466 }
1467
1468 if !ty.is_doc_subtype_of(&impl_.for_, cx.cache()) {
1469 continue;
1472 }
1473 if let Some(trait_) = &impl_.trait_ {
1474 let trait_did = trait_.def_id();
1475
1476 if cx.cache().traits.get(&trait_did).is_some_and(|t| t.is_notable_trait(cx.tcx())) {
1477 has_notable_trait = true;
1478 }
1479 }
1480 }
1481 }
1482
1483 if has_notable_trait {
1484 cx.types_with_notable_traits.borrow_mut().insert(ty.clone());
1485 Some(format!(
1486 " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">โ</a>",
1487 ty = Escape(&format!("{:#}", ty.print(cx))),
1488 ))
1489 } else {
1490 None
1491 }
1492}
1493
1494fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
1495 let mut out = String::new();
1496
1497 let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
1498
1499 let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");
1500
1501 for i in impls {
1502 let impl_ = i.inner_impl();
1503 if impl_.polarity != ty::ImplPolarity::Positive {
1504 continue;
1505 }
1506
1507 if !ty.is_doc_subtype_of(&impl_.for_, cx.cache()) {
1508 continue;
1511 }
1512 if let Some(trait_) = &impl_.trait_ {
1513 let trait_did = trait_.def_id();
1514
1515 if cx.cache().traits.get(&trait_did).is_some_and(|t| t.is_notable_trait(cx.tcx())) {
1516 if out.is_empty() {
1517 write_str(
1518 &mut out,
1519 format_args!(
1520 "<h3>Notable traits for <code>{}</code></h3>\
1521 <pre><code>",
1522 impl_.for_.print(cx)
1523 ),
1524 );
1525 }
1526
1527 write_str(
1528 &mut out,
1529 format_args!("<div class=\"where\">{}</div>", impl_.print(false, cx)),
1530 );
1531 for it in &impl_.items {
1532 if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
1533 out.push_str("<div class=\"where\"> ");
1534 let empty_set = FxIndexSet::default();
1535 let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
1536 assoc_type(
1537 &mut out,
1538 it,
1539 &tydef.generics,
1540 &[], Some(&tydef.type_),
1542 src_link,
1543 0,
1544 cx,
1545 );
1546 out.push_str(";</div>");
1547 }
1548 }
1549 }
1550 }
1551 }
1552 if out.is_empty() {
1553 out.push_str("</code></pre>");
1554 }
1555
1556 (format!("{:#}", ty.print(cx)), out)
1557}
1558
1559pub(crate) fn notable_traits_json<'a>(
1560 tys: impl Iterator<Item = &'a clean::Type>,
1561 cx: &Context<'_>,
1562) -> String {
1563 let mut mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect();
1564 mp.sort_by(|(name1, _html1), (name2, _html2)| name1.cmp(name2));
1565 struct NotableTraitsMap(Vec<(String, String)>);
1566 impl Serialize for NotableTraitsMap {
1567 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1568 where
1569 S: Serializer,
1570 {
1571 let mut map = serializer.serialize_map(Some(self.0.len()))?;
1572 for item in &self.0 {
1573 map.serialize_entry(&item.0, &item.1)?;
1574 }
1575 map.end()
1576 }
1577 }
1578 serde_json::to_string(&NotableTraitsMap(mp))
1579 .expect("serialize (string, string) -> json object cannot fail")
1580}
1581
1582#[derive(Clone, Copy, Debug)]
1583struct ImplRenderingParameters {
1584 show_def_docs: bool,
1585 show_default_items: bool,
1586 show_non_assoc_items: bool,
1588 toggle_open_by_default: bool,
1589}
1590
1591fn render_impl(
1592 w: &mut String,
1593 cx: &Context<'_>,
1594 i: &Impl,
1595 parent: &clean::Item,
1596 link: AssocItemLink<'_>,
1597 render_mode: RenderMode,
1598 use_absolute: Option<bool>,
1599 aliases: &[String],
1600 rendering_params: ImplRenderingParameters,
1601) {
1602 let cache = &cx.shared.cache;
1603 let traits = &cache.traits;
1604 let trait_ = i.trait_did().map(|did| &traits[&did]);
1605 let mut close_tags = <Vec<&str>>::with_capacity(2);
1606
1607 fn doc_impl_item(
1613 boring: &mut String,
1614 interesting: &mut String,
1615 cx: &Context<'_>,
1616 item: &clean::Item,
1617 parent: &clean::Item,
1618 link: AssocItemLink<'_>,
1619 render_mode: RenderMode,
1620 is_default_item: bool,
1621 trait_: Option<&clean::Trait>,
1622 rendering_params: ImplRenderingParameters,
1623 ) {
1624 let item_type = item.type_();
1625 let name = item.name.as_ref().unwrap();
1626
1627 let render_method_item = rendering_params.show_non_assoc_items
1628 && match render_mode {
1629 RenderMode::Normal => true,
1630 RenderMode::ForDeref { mut_: deref_mut_ } => {
1631 should_render_item(item, deref_mut_, cx.tcx())
1632 }
1633 };
1634
1635 let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
1636
1637 let mut doc_buffer = String::new();
1638 let mut info_buffer = String::new();
1639 let mut short_documented = true;
1640
1641 if render_method_item {
1642 if !is_default_item {
1643 if let Some(t) = trait_ {
1644 if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1647 if !item.doc_value().is_empty() {
1650 document_item_info(cx, it, Some(parent))
1651 .render_into(&mut info_buffer)
1652 .unwrap();
1653 write_str(
1654 &mut doc_buffer,
1655 format_args!("{}", document_full(item, cx, HeadingOffset::H5)),
1656 );
1657 short_documented = false;
1658 } else {
1659 write_str(
1662 &mut doc_buffer,
1663 format_args!(
1664 "{}",
1665 document_short(
1666 it,
1667 cx,
1668 link,
1669 parent,
1670 rendering_params.show_def_docs,
1671 )
1672 ),
1673 );
1674 }
1675 }
1676 } else {
1677 document_item_info(cx, item, Some(parent))
1678 .render_into(&mut info_buffer)
1679 .unwrap();
1680 if rendering_params.show_def_docs {
1681 write_str(
1682 &mut doc_buffer,
1683 format_args!("{}", document_full(item, cx, HeadingOffset::H5)),
1684 );
1685 short_documented = false;
1686 }
1687 }
1688 } else {
1689 write_str(
1690 &mut doc_buffer,
1691 format_args!(
1692 "{}",
1693 document_short(item, cx, link, parent, rendering_params.show_def_docs)
1694 ),
1695 );
1696 }
1697 }
1698 let w = if short_documented && trait_.is_some() { interesting } else { boring };
1699
1700 let toggled = !doc_buffer.is_empty();
1701 if toggled {
1702 let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
1703 write_str(
1704 w,
1705 format_args!("<details class=\"toggle{method_toggle_class}\" open><summary>"),
1706 );
1707 }
1708 match &item.kind {
1709 clean::MethodItem(..) | clean::RequiredMethodItem(_) => {
1710 if render_method_item {
1712 let id = cx.derive_id(format!("{item_type}.{name}"));
1713 let source_id = trait_
1714 .and_then(|trait_| {
1715 trait_
1716 .items
1717 .iter()
1718 .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
1719 })
1720 .map(|item| format!("{}.{name}", item.type_()));
1721 write_str(
1722 w,
1723 format_args!("<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"),
1724 );
1725 render_rightside(w, cx, item, render_mode);
1726 if trait_.is_some() {
1727 write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">ยง</a>"));
1729 }
1730 w.push_str("<h4 class=\"code-header\">");
1731 render_assoc_item(
1732 w,
1733 item,
1734 link.anchor(source_id.as_ref().unwrap_or(&id)),
1735 ItemType::Impl,
1736 cx,
1737 render_mode,
1738 );
1739 w.push_str("</h4></section>");
1740 }
1741 }
1742 clean::RequiredAssocConstItem(ref generics, ref ty) => {
1743 let source_id = format!("{item_type}.{name}");
1744 let id = cx.derive_id(&source_id);
1745 write_str(
1746 w,
1747 format_args!("<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"),
1748 );
1749 render_rightside(w, cx, item, render_mode);
1750 if trait_.is_some() {
1751 write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">ยง</a>"));
1753 }
1754 w.push_str("<h4 class=\"code-header\">");
1755 assoc_const(
1756 w,
1757 item,
1758 generics,
1759 ty,
1760 AssocConstValue::None,
1761 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1762 0,
1763 cx,
1764 );
1765 w.push_str("</h4></section>");
1766 }
1767 clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
1768 let source_id = format!("{item_type}.{name}");
1769 let id = cx.derive_id(&source_id);
1770 write_str(
1771 w,
1772 format_args!("<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"),
1773 );
1774 render_rightside(w, cx, item, render_mode);
1775 if trait_.is_some() {
1776 write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">ยง</a>"));
1778 }
1779 w.push_str("<h4 class=\"code-header\">");
1780 assoc_const(
1781 w,
1782 item,
1783 &ci.generics,
1784 &ci.type_,
1785 match item.kind {
1786 clean::ProvidedAssocConstItem(_) => AssocConstValue::TraitDefault(&ci.kind),
1787 clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
1788 _ => unreachable!(),
1789 },
1790 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1791 0,
1792 cx,
1793 );
1794 w.push_str("</h4></section>");
1795 }
1796 clean::RequiredAssocTypeItem(ref generics, ref bounds) => {
1797 let source_id = format!("{item_type}.{name}");
1798 let id = cx.derive_id(&source_id);
1799 write_str(
1800 w,
1801 format_args!("<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"),
1802 );
1803 render_rightside(w, cx, item, render_mode);
1804 if trait_.is_some() {
1805 write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">ยง</a>"));
1807 }
1808 w.push_str("<h4 class=\"code-header\">");
1809 assoc_type(
1810 w,
1811 item,
1812 generics,
1813 bounds,
1814 None,
1815 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1816 0,
1817 cx,
1818 );
1819 w.push_str("</h4></section>");
1820 }
1821 clean::AssocTypeItem(tydef, _bounds) => {
1822 let source_id = format!("{item_type}.{name}");
1823 let id = cx.derive_id(&source_id);
1824 write_str(
1825 w,
1826 format_args!("<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"),
1827 );
1828 render_rightside(w, cx, item, render_mode);
1829 if trait_.is_some() {
1830 write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">ยง</a>"));
1832 }
1833 w.push_str("<h4 class=\"code-header\">");
1834 assoc_type(
1835 w,
1836 item,
1837 &tydef.generics,
1838 &[], Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
1840 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1841 0,
1842 cx,
1843 );
1844 w.push_str("</h4></section>");
1845 }
1846 clean::StrippedItem(..) => return,
1847 _ => panic!("can't make docs for trait item with name {:?}", item.name),
1848 }
1849
1850 w.push_str(&info_buffer);
1851 if toggled {
1852 w.push_str("</summary>");
1853 w.push_str(&doc_buffer);
1854 w.push_str("</details>");
1855 }
1856 }
1857
1858 let mut impl_items = String::new();
1859 let mut default_impl_items = String::new();
1860 let impl_ = i.inner_impl();
1861
1862 let mut assoc_types = Vec::new();
1872 let mut methods = Vec::new();
1873
1874 if !impl_.is_negative_trait_impl() {
1875 for trait_item in &impl_.items {
1876 match trait_item.kind {
1877 clean::MethodItem(..) | clean::RequiredMethodItem(_) => methods.push(trait_item),
1878 clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => {
1879 assoc_types.push(trait_item)
1880 }
1881 clean::RequiredAssocConstItem(..)
1882 | clean::ProvidedAssocConstItem(_)
1883 | clean::ImplAssocConstItem(_) => {
1884 doc_impl_item(
1886 &mut default_impl_items,
1887 &mut impl_items,
1888 cx,
1889 trait_item,
1890 if trait_.is_some() { &i.impl_item } else { parent },
1891 link,
1892 render_mode,
1893 false,
1894 trait_,
1895 rendering_params,
1896 );
1897 }
1898 _ => {}
1899 }
1900 }
1901
1902 for assoc_type in assoc_types {
1903 doc_impl_item(
1904 &mut default_impl_items,
1905 &mut impl_items,
1906 cx,
1907 assoc_type,
1908 if trait_.is_some() { &i.impl_item } else { parent },
1909 link,
1910 render_mode,
1911 false,
1912 trait_,
1913 rendering_params,
1914 );
1915 }
1916 for method in methods {
1917 doc_impl_item(
1918 &mut default_impl_items,
1919 &mut impl_items,
1920 cx,
1921 method,
1922 if trait_.is_some() { &i.impl_item } else { parent },
1923 link,
1924 render_mode,
1925 false,
1926 trait_,
1927 rendering_params,
1928 );
1929 }
1930 }
1931
1932 fn render_default_items(
1933 boring: &mut String,
1934 interesting: &mut String,
1935 cx: &Context<'_>,
1936 t: &clean::Trait,
1937 i: &clean::Impl,
1938 parent: &clean::Item,
1939 render_mode: RenderMode,
1940 rendering_params: ImplRenderingParameters,
1941 ) {
1942 for trait_item in &t.items {
1943 if let Some(impl_def_id) = parent.item_id.as_def_id()
1946 && let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
1947 && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id))
1948 {
1949 continue;
1950 }
1951
1952 let n = trait_item.name;
1953 if i.items.iter().any(|m| m.name == n) {
1954 continue;
1955 }
1956 let did = i.trait_.as_ref().unwrap().def_id();
1957 let provided_methods = i.provided_trait_methods(cx.tcx());
1958 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
1959
1960 doc_impl_item(
1961 boring,
1962 interesting,
1963 cx,
1964 trait_item,
1965 parent,
1966 assoc_link,
1967 render_mode,
1968 true,
1969 Some(t),
1970 rendering_params,
1971 );
1972 }
1973 }
1974
1975 if rendering_params.show_default_items {
1980 if let Some(t) = trait_
1981 && !impl_.is_negative_trait_impl()
1982 {
1983 render_default_items(
1984 &mut default_impl_items,
1985 &mut impl_items,
1986 cx,
1987 t,
1988 impl_,
1989 &i.impl_item,
1990 render_mode,
1991 rendering_params,
1992 );
1993 }
1994 }
1995 if render_mode == RenderMode::Normal {
1996 let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
1997 if toggled {
1998 close_tags.push("</details>");
1999 write_str(
2000 w,
2001 format_args!(
2002 "<details class=\"toggle implementors-toggle\"{}>\
2003 <summary>",
2004 if rendering_params.toggle_open_by_default { " open" } else { "" }
2005 ),
2006 );
2007 }
2008
2009 let (before_dox, after_dox) = i
2010 .impl_item
2011 .opt_doc_value()
2012 .map(|dox| {
2013 Markdown {
2014 content: &dox,
2015 links: &i.impl_item.links(cx),
2016 ids: &mut cx.id_map.borrow_mut(),
2017 error_codes: cx.shared.codes,
2018 edition: cx.shared.edition(),
2019 playground: &cx.shared.playground,
2020 heading_offset: HeadingOffset::H4,
2021 }
2022 .split_summary_and_content()
2023 })
2024 .unwrap_or((None, None));
2025 render_impl_summary(
2026 w,
2027 cx,
2028 i,
2029 parent,
2030 rendering_params.show_def_docs,
2031 use_absolute,
2032 aliases,
2033 &before_dox,
2034 );
2035 if toggled {
2036 w.push_str("</summary>");
2037 }
2038
2039 if before_dox.is_some() {
2040 if trait_.is_none() && impl_.items.is_empty() {
2041 w.push_str(
2042 "<div class=\"item-info\">\
2043 <div class=\"stab empty-impl\">This impl block contains no items.</div>\
2044 </div>",
2045 );
2046 }
2047 if let Some(after_dox) = after_dox {
2048 write_str(w, format_args!("<div class=\"docblock\">{after_dox}</div>"));
2049 }
2050 }
2051 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2052 w.push_str("<div class=\"impl-items\">");
2053 close_tags.push("</div>");
2054 }
2055 }
2056 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2057 w.push_str(&default_impl_items);
2058 w.push_str(&impl_items);
2059 }
2060 for tag in close_tags.into_iter().rev() {
2061 w.push_str(tag);
2062 }
2063}
2064
2065fn render_rightside(w: &mut String, cx: &Context<'_>, item: &clean::Item, render_mode: RenderMode) {
2068 let tcx = cx.tcx();
2069
2070 let const_stability = match render_mode {
2073 RenderMode::Normal => item.const_stability(tcx),
2074 RenderMode::ForDeref { .. } => None,
2075 };
2076 let src_href = cx.src_href(item);
2077 let has_src_ref = src_href.is_some();
2078
2079 let mut rightside = String::new();
2080 let has_stability = render_stability_since_raw_with_extra(
2081 &mut rightside,
2082 item.stable_since(tcx),
2083 const_stability,
2084 if has_src_ref { "" } else { " rightside" },
2085 );
2086 if let Some(link) = src_href {
2087 if has_stability {
2088 write_str(
2089 &mut rightside,
2090 format_args!(" ยท <a class=\"src\" href=\"{link}\">Source</a>"),
2091 );
2092 } else {
2093 write_str(
2094 &mut rightside,
2095 format_args!("<a class=\"src rightside\" href=\"{link}\">Source</a>"),
2096 );
2097 }
2098 }
2099 if has_stability && has_src_ref {
2100 write_str(w, format_args!("<span class=\"rightside\">{rightside}</span>"));
2101 } else {
2102 w.push_str(&rightside);
2103 }
2104}
2105
2106pub(crate) fn render_impl_summary(
2107 w: &mut String,
2108 cx: &Context<'_>,
2109 i: &Impl,
2110 parent: &clean::Item,
2111 show_def_docs: bool,
2112 use_absolute: Option<bool>,
2113 aliases: &[String],
2116 doc: &Option<String>,
2117) {
2118 let inner_impl = i.inner_impl();
2119 let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
2120 let aliases = if aliases.is_empty() {
2121 String::new()
2122 } else {
2123 format!(" data-aliases=\"{}\"", aliases.join(","))
2124 };
2125 write_str(w, format_args!("<section id=\"{id}\" class=\"impl\"{aliases}>"));
2126 render_rightside(w, cx, &i.impl_item, RenderMode::Normal);
2127 write_str(
2128 w,
2129 format_args!(
2130 "<a href=\"#{id}\" class=\"anchor\">ยง</a>\
2131 <h3 class=\"code-header\">"
2132 ),
2133 );
2134
2135 if let Some(use_absolute) = use_absolute {
2136 write_str(w, format_args!("{}", inner_impl.print(use_absolute, cx)));
2137 if show_def_docs {
2138 for it in &inner_impl.items {
2139 if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
2140 w.push_str("<div class=\"where\"> ");
2141 assoc_type(
2142 w,
2143 it,
2144 &tydef.generics,
2145 &[], Some(&tydef.type_),
2147 AssocItemLink::Anchor(None),
2148 0,
2149 cx,
2150 );
2151 w.push_str(";</div>");
2152 }
2153 }
2154 }
2155 } else {
2156 write_str(w, format_args!("{}", inner_impl.print(false, cx)));
2157 }
2158 w.push_str("</h3>");
2159
2160 let is_trait = inner_impl.trait_.is_some();
2161 if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) {
2162 write_str(
2163 w,
2164 format_args!(
2165 "<span class=\"item-info\">\
2166 <div class=\"stab portability\">{portability}</div>\
2167 </span>",
2168 ),
2169 );
2170 }
2171
2172 if let Some(doc) = doc {
2173 write_str(w, format_args!("<div class=\"docblock\">{doc}</div>"));
2174 }
2175
2176 w.push_str("</section>");
2177}
2178
2179pub(crate) fn small_url_encode(s: String) -> String {
2180 fn dont_escape(c: u8) -> bool {
2185 c.is_ascii_alphanumeric()
2186 || c == b'-'
2187 || c == b'_'
2188 || c == b'.'
2189 || c == b','
2190 || c == b'~'
2191 || c == b'!'
2192 || c == b'\''
2193 || c == b'('
2194 || c == b')'
2195 || c == b'*'
2196 || c == b'/'
2197 || c == b';'
2198 || c == b':'
2199 || c == b'?'
2200 || c == b'='
2204 }
2205 let mut st = String::new();
2206 let mut last_match = 0;
2207 for (idx, b) in s.bytes().enumerate() {
2208 if dont_escape(b) {
2209 continue;
2210 }
2211
2212 if last_match != idx {
2213 st += &s[last_match..idx];
2215 }
2216 if b == b' ' {
2217 st += "+";
2221 } else {
2222 write!(st, "%{b:02X}").unwrap();
2223 }
2224 last_match = idx + 1;
2230 }
2231
2232 if last_match != 0 {
2233 st += &s[last_match..];
2234 st
2235 } else {
2236 s
2237 }
2238}
2239
2240fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String {
2241 use rustc_middle::ty::print::with_forced_trimmed_paths;
2242 let (type_, trait_) = match impl_id {
2243 ItemId::Auto { trait_, for_ } => {
2244 let ty = tcx.type_of(for_).skip_binder();
2245 (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
2246 }
2247 ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
2248 match tcx.impl_subject(impl_id).skip_binder() {
2249 ty::ImplSubject::Trait(trait_ref) => {
2250 (trait_ref.args[0].expect_ty(), Some(trait_ref))
2251 }
2252 ty::ImplSubject::Inherent(ty) => (ty, None),
2253 }
2254 }
2255 };
2256 with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
2257 format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
2258 } else {
2259 format!("impl-{type_}")
2260 }))
2261}
2262
2263fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2264 match item.kind {
2265 clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
2266 Some((format!("{:#}", i.for_.print(cx)), get_id_for_impl(cx.tcx(), item.item_id)))
2269 }
2270 _ => None,
2271 }
2272}
2273
2274pub(crate) fn get_filtered_impls_for_reference<'a>(
2278 shared: &'a SharedContext<'_>,
2279 it: &clean::Item,
2280) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
2281 let def_id = it.item_id.expect_def_id();
2282 let Some(v) = shared.cache.impls.get(&def_id) else {
2284 return (Vec::new(), Vec::new(), Vec::new());
2285 };
2286 let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
2289 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
2290 traits.partition(|t| t.inner_impl().kind.is_auto());
2291
2292 let (blanket_impl, concrete): (Vec<&Impl>, _) =
2293 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
2294 let concrete: Vec<_> = concrete
2296 .into_iter()
2297 .filter(|t| match t.inner_impl().for_ {
2298 clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
2299 _ => false,
2300 })
2301 .collect();
2302
2303 (concrete, synthetic, blanket_impl)
2304}
2305
2306#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2307pub(crate) enum ItemSection {
2308 Reexports,
2309 PrimitiveTypes,
2310 Modules,
2311 Macros,
2312 Structs,
2313 Enums,
2314 Constants,
2315 Statics,
2316 Traits,
2317 Functions,
2318 TypeAliases,
2319 Unions,
2320 Implementations,
2321 TypeMethods,
2322 Methods,
2323 StructFields,
2324 Variants,
2325 AssociatedTypes,
2326 AssociatedConstants,
2327 ForeignTypes,
2328 Keywords,
2329 AttributeMacros,
2330 DeriveMacros,
2331 TraitAliases,
2332}
2333
2334impl ItemSection {
2335 const ALL: &'static [Self] = {
2336 use ItemSection::*;
2337 &[
2340 Reexports,
2341 PrimitiveTypes,
2342 Modules,
2343 Macros,
2344 Structs,
2345 Enums,
2346 Constants,
2347 Statics,
2348 Traits,
2349 Functions,
2350 TypeAliases,
2351 Unions,
2352 Implementations,
2353 TypeMethods,
2354 Methods,
2355 StructFields,
2356 Variants,
2357 AssociatedTypes,
2358 AssociatedConstants,
2359 ForeignTypes,
2360 Keywords,
2361 AttributeMacros,
2362 DeriveMacros,
2363 TraitAliases,
2364 ]
2365 };
2366
2367 fn id(self) -> &'static str {
2368 match self {
2369 Self::Reexports => "reexports",
2370 Self::Modules => "modules",
2371 Self::Structs => "structs",
2372 Self::Unions => "unions",
2373 Self::Enums => "enums",
2374 Self::Functions => "functions",
2375 Self::TypeAliases => "types",
2376 Self::Statics => "statics",
2377 Self::Constants => "constants",
2378 Self::Traits => "traits",
2379 Self::Implementations => "impls",
2380 Self::TypeMethods => "tymethods",
2381 Self::Methods => "methods",
2382 Self::StructFields => "fields",
2383 Self::Variants => "variants",
2384 Self::Macros => "macros",
2385 Self::PrimitiveTypes => "primitives",
2386 Self::AssociatedTypes => "associated-types",
2387 Self::AssociatedConstants => "associated-consts",
2388 Self::ForeignTypes => "foreign-types",
2389 Self::Keywords => "keywords",
2390 Self::AttributeMacros => "attributes",
2391 Self::DeriveMacros => "derives",
2392 Self::TraitAliases => "trait-aliases",
2393 }
2394 }
2395
2396 fn name(self) -> &'static str {
2397 match self {
2398 Self::Reexports => "Re-exports",
2399 Self::Modules => "Modules",
2400 Self::Structs => "Structs",
2401 Self::Unions => "Unions",
2402 Self::Enums => "Enums",
2403 Self::Functions => "Functions",
2404 Self::TypeAliases => "Type Aliases",
2405 Self::Statics => "Statics",
2406 Self::Constants => "Constants",
2407 Self::Traits => "Traits",
2408 Self::Implementations => "Implementations",
2409 Self::TypeMethods => "Type Methods",
2410 Self::Methods => "Methods",
2411 Self::StructFields => "Struct Fields",
2412 Self::Variants => "Variants",
2413 Self::Macros => "Macros",
2414 Self::PrimitiveTypes => "Primitive Types",
2415 Self::AssociatedTypes => "Associated Types",
2416 Self::AssociatedConstants => "Associated Constants",
2417 Self::ForeignTypes => "Foreign Types",
2418 Self::Keywords => "Keywords",
2419 Self::AttributeMacros => "Attribute Macros",
2420 Self::DeriveMacros => "Derive Macros",
2421 Self::TraitAliases => "Trait Aliases",
2422 }
2423 }
2424}
2425
2426fn item_ty_to_section(ty: ItemType) -> ItemSection {
2427 match ty {
2428 ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
2429 ItemType::Module => ItemSection::Modules,
2430 ItemType::Struct => ItemSection::Structs,
2431 ItemType::Union => ItemSection::Unions,
2432 ItemType::Enum => ItemSection::Enums,
2433 ItemType::Function => ItemSection::Functions,
2434 ItemType::TypeAlias => ItemSection::TypeAliases,
2435 ItemType::Static => ItemSection::Statics,
2436 ItemType::Constant => ItemSection::Constants,
2437 ItemType::Trait => ItemSection::Traits,
2438 ItemType::Impl => ItemSection::Implementations,
2439 ItemType::TyMethod => ItemSection::TypeMethods,
2440 ItemType::Method => ItemSection::Methods,
2441 ItemType::StructField => ItemSection::StructFields,
2442 ItemType::Variant => ItemSection::Variants,
2443 ItemType::Macro => ItemSection::Macros,
2444 ItemType::Primitive => ItemSection::PrimitiveTypes,
2445 ItemType::AssocType => ItemSection::AssociatedTypes,
2446 ItemType::AssocConst => ItemSection::AssociatedConstants,
2447 ItemType::ForeignType => ItemSection::ForeignTypes,
2448 ItemType::Keyword => ItemSection::Keywords,
2449 ItemType::ProcAttribute => ItemSection::AttributeMacros,
2450 ItemType::ProcDerive => ItemSection::DeriveMacros,
2451 ItemType::TraitAlias => ItemSection::TraitAliases,
2452 }
2453}
2454
2455fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
2462 let mut out = Vec::new();
2463 let mut visited = FxHashSet::default();
2464 let mut work = VecDeque::new();
2465
2466 let mut process_path = |did: DefId| {
2467 let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
2468 let fqp = cache.exact_paths.get(&did).or_else(get_extern);
2469
2470 if let Some(path) = fqp {
2471 out.push(join_with_double_colon(path));
2472 }
2473 };
2474
2475 work.push_back(first_ty);
2476
2477 while let Some(ty) = work.pop_front() {
2478 if !visited.insert(ty.clone()) {
2479 continue;
2480 }
2481
2482 match ty {
2483 clean::Type::Path { path } => process_path(path.def_id()),
2484 clean::Type::Tuple(tys) => {
2485 work.extend(tys.into_iter());
2486 }
2487 clean::Type::Slice(ty) => {
2488 work.push_back(*ty);
2489 }
2490 clean::Type::Array(ty, _) => {
2491 work.push_back(*ty);
2492 }
2493 clean::Type::RawPointer(_, ty) => {
2494 work.push_back(*ty);
2495 }
2496 clean::Type::BorrowedRef { type_, .. } => {
2497 work.push_back(*type_);
2498 }
2499 clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
2500 work.push_back(self_type);
2501 if let Some(trait_) = trait_ {
2502 process_path(trait_.def_id());
2503 }
2504 }
2505 _ => {}
2506 }
2507 }
2508 out
2509}
2510
2511const MAX_FULL_EXAMPLES: usize = 5;
2512const NUM_VISIBLE_LINES: usize = 10;
2513
2514fn render_call_locations<W: fmt::Write>(mut w: W, cx: &Context<'_>, item: &clean::Item) {
2516 let tcx = cx.tcx();
2517 let def_id = item.item_id.expect_def_id();
2518 let key = tcx.def_path_hash(def_id);
2519 let Some(call_locations) = cx.shared.call_locations.get(&key) else { return };
2520
2521 let id = cx.derive_id("scraped-examples");
2523 write!(
2524 &mut w,
2525 "<div class=\"docblock scraped-example-list\">\
2526 <span></span>\
2527 <h5 id=\"{id}\">\
2528 <a href=\"#{id}\">Examples found in repository</a>\
2529 <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
2530 </h5>",
2531 root_path = cx.root_path(),
2532 id = id
2533 )
2534 .unwrap();
2535
2536 let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
2538 let (line_lo, line_hi) = loc.call_expr.line_span;
2539 let (anchor, title) = if line_lo == line_hi {
2540 ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
2541 } else {
2542 (
2543 format!("{}-{}", line_lo + 1, line_hi + 1),
2544 format!("lines {}-{}", line_lo + 1, line_hi + 1),
2545 )
2546 };
2547 let url = format!("{}{}#{anchor}", cx.root_path(), call_data.url);
2548 (url, title)
2549 };
2550
2551 let write_example = |w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
2553 let contents = match fs::read_to_string(path) {
2554 Ok(contents) => contents,
2555 Err(err) => {
2556 let span = item.span(tcx).map_or(DUMMY_SP, |span| span.inner());
2557 tcx.dcx().span_err(span, format!("failed to read file {}: {err}", path.display()));
2558 return false;
2559 }
2560 };
2561
2562 assert!(!call_data.locations.is_empty());
2565 let min_loc =
2566 call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2567 let byte_min = min_loc.enclosing_item.byte_span.0;
2568 let line_min = min_loc.enclosing_item.line_span.0;
2569 let max_loc =
2570 call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
2571 let byte_max = max_loc.enclosing_item.byte_span.1;
2572 let line_max = max_loc.enclosing_item.line_span.1;
2573
2574 let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
2576
2577 let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
2580 .locations
2581 .iter()
2582 .map(|loc| {
2583 let (byte_lo, byte_hi) = loc.call_ident.byte_span;
2584 let (line_lo, line_hi) = loc.call_expr.line_span;
2585 let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
2586
2587 let line_range = (line_lo - line_min, line_hi - line_min);
2588 let (line_url, line_title) = link_to_loc(call_data, loc);
2589
2590 (byte_range, (line_range, line_url, line_title))
2591 })
2592 .unzip();
2593
2594 let (_, init_url, init_title) = &line_ranges[0];
2595 let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
2596 let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
2597
2598 let file_span = (|| {
2600 let source_map = tcx.sess.source_map();
2601 let crate_src = tcx.sess.local_crate_source_file()?.into_local_path()?;
2602 let abs_crate_src = crate_src.canonicalize().ok()?;
2603 let crate_root = abs_crate_src.parent()?.parent()?;
2604 let rel_path = path.strip_prefix(crate_root).ok()?;
2605 let files = source_map.files();
2606 let file = files.iter().find(|file| match &file.name {
2607 FileName::Real(RealFileName::LocalPath(other_path)) => rel_path == other_path,
2608 _ => false,
2609 })?;
2610 Some(rustc_span::Span::with_root_ctxt(
2611 file.start_pos + BytePos(byte_min),
2612 file.start_pos + BytePos(byte_max),
2613 ))
2614 })()
2615 .unwrap_or(DUMMY_SP);
2616
2617 let mut decoration_info = FxIndexMap::default();
2618 decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
2619 decoration_info.insert("highlight", byte_ranges);
2620
2621 sources::print_src(
2622 w,
2623 contents_subset,
2624 file_span,
2625 cx,
2626 &cx.root_path(),
2627 &highlight::DecorationInfo(decoration_info),
2628 &sources::SourceContext::Embedded(sources::ScrapedInfo {
2629 needs_expansion,
2630 offset: line_min,
2631 name: &call_data.display_name,
2632 url: init_url,
2633 title: init_title,
2634 locations: locations_encoded,
2635 }),
2636 );
2637
2638 true
2639 };
2640
2641 let ordered_locations = {
2653 fn sort_criterion<'a>(
2654 (_, call_data): &(&PathBuf, &'a CallData),
2655 ) -> (bool, u32, &'a String) {
2656 let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2658 (!call_data.is_bin, hi - lo, &call_data.display_name)
2659 }
2660
2661 let mut locs = call_locations.iter().collect::<Vec<_>>();
2662 locs.sort_by_key(sort_criterion);
2663 locs
2664 };
2665
2666 let mut it = ordered_locations.into_iter().peekable();
2667
2668 let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
2671 for example in it.by_ref() {
2672 if write_example(&mut *w, example) {
2673 break;
2674 }
2675 }
2676 };
2677
2678 write_and_skip_failure(&mut w, &mut it);
2680
2681 if it.peek().is_some() {
2683 write!(
2684 w,
2685 "<details class=\"toggle more-examples-toggle\">\
2686 <summary class=\"hideme\">\
2687 <span>More examples</span>\
2688 </summary>\
2689 <div class=\"hide-more\">Hide additional examples</div>\
2690 <div class=\"more-scraped-examples\">\
2691 <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
2692 )
2693 .unwrap();
2694
2695 for _ in 0..MAX_FULL_EXAMPLES {
2698 write_and_skip_failure(&mut w, &mut it);
2699 }
2700
2701 if it.peek().is_some() {
2703 w.write_str(
2704 r#"<div class="example-links">Additional examples can be found in:<br><ul>"#,
2705 )
2706 .unwrap();
2707 it.for_each(|(_, call_data)| {
2708 let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
2709 write!(
2710 w,
2711 r#"<li><a href="{url}">{name}</a></li>"#,
2712 url = url,
2713 name = call_data.display_name
2714 )
2715 .unwrap();
2716 });
2717 w.write_str("</ul></div>").unwrap();
2718 }
2719
2720 w.write_str("</div></details>").unwrap();
2721 }
2722
2723 w.write_str("</div>").unwrap();
2724}