rustdoc/html/render/
mod.rs

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