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