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