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