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