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