rustdoc/html/render/
mod.rs

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