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