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