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