rustdoc/html/render/
mod.rs

1//! Rustdoc's HTML rendering module.
2//!
3//! This modules contains the bulk of the logic necessary for rendering a
4//! rustdoc `clean::Crate` instance to a set of static HTML pages. This
5//! rendering process is largely driven by the `format!` syntax extension to
6//! perform all I/O into files and streams.
7//!
8//! The rendering process is largely driven by the `Context` and `Cache`
9//! structures. The cache is pre-populated by crawling the crate in question,
10//! and then it is shared among the various rendering threads. The cache is meant
11//! to be a fairly large structure not implementing `Clone` (because it's shared
12//! among threads). The context, however, should be a lightweight structure. This
13//! is cloned per-thread and contains information about what is currently being
14//! rendered.
15//!
16//! The main entry point to the rendering system is the implementation of
17//! `FormatRenderer` on `Context`.
18//!
19//! In order to speed up rendering (mostly because of markdown rendering), the
20//! rendering process has been parallelized. This parallelization is only
21//! exposed through the `crate` method on the context, and then also from the
22//! fact that the shared cache is stored in TLS (and must be accessed as such).
23//!
24//! In addition to rendering the crate itself, this module is also responsible
25//! for creating the corresponding search index and source file renderings.
26//! These threads are not parallelized (they haven't been a bottleneck yet), and
27//! both occur before the crate is rendered.
28
29pub(crate) mod search_index;
30
31#[cfg(test)]
32mod tests;
33
34mod context;
35mod ordered_json;
36mod print_item;
37pub(crate) mod sidebar;
38mod sorted_template;
39mod span_map;
40mod type_layout;
41mod write_shared;
42
43use std::borrow::Cow;
44use std::collections::VecDeque;
45use std::fmt::{self, Display as _, Write};
46use std::iter::Peekable;
47use std::path::PathBuf;
48use std::{fs, str};
49
50use askama::Template;
51use itertools::Either;
52use rustc_ast::join_path_syms;
53use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
54use rustc_hir::attrs::{DeprecatedSince, Deprecation};
55use rustc_hir::def_id::{DefId, DefIdSet};
56use rustc_hir::{ConstStability, Mutability, RustcVersion, StabilityLevel, StableSince};
57use rustc_middle::ty::print::PrintTraitRefExt;
58use rustc_middle::ty::{self, TyCtxt};
59use rustc_span::symbol::{Symbol, sym};
60use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName};
61use serde::ser::SerializeMap;
62use serde::{Serialize, Serializer};
63use tracing::{debug, info};
64
65pub(crate) use self::context::*;
66pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources};
67pub(crate) use self::write_shared::*;
68use crate::clean::{self, ItemId, RenderedLink};
69use crate::display::{Joined as _, MaybeDisplay as _};
70use crate::error::Error;
71use crate::formats::Impl;
72use crate::formats::cache::Cache;
73use crate::formats::item_type::ItemType;
74use crate::html::escape::Escape;
75use crate::html::format::{
76    Ending, HrefError, PrintWithSpace, href, print_abi_with_space, print_constness_with_space,
77    print_default_space, print_generic_bounds, print_where_clause, visibility_print_with_space,
78    write_str,
79};
80use crate::html::markdown::{
81    HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine,
82};
83use crate::html::static_files::SCRAPE_EXAMPLES_HELP_MD;
84use crate::html::{highlight, sources};
85use crate::scrape_examples::{CallData, CallLocation};
86use crate::{DOC_RUST_LANG_ORG_VERSION, try_none};
87
88pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display {
89    fmt::from_fn(move |f| {
90        if !v.ends_with('/') && !v.is_empty() { write!(f, "{v}/") } else { f.write_str(v) }
91    })
92}
93
94/// Specifies whether rendering directly implemented trait items or ones from a certain Deref
95/// impl.
96#[derive(Copy, Clone, Debug)]
97enum AssocItemRender<'a> {
98    All,
99    DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool },
100}
101
102impl AssocItemRender<'_> {
103    fn render_mode(&self) -> RenderMode {
104        match self {
105            Self::All => RenderMode::Normal,
106            &Self::DerefFor { deref_mut_, .. } => RenderMode::ForDeref { mut_: deref_mut_ },
107        }
108    }
109
110    fn class(&self) -> Option<&'static str> {
111        if let Self::DerefFor { .. } = self { Some("impl-items") } else { None }
112    }
113}
114
115/// For different handling of associated items from the Deref target of a type rather than the type
116/// itself.
117#[derive(Copy, Clone, PartialEq)]
118enum RenderMode {
119    Normal,
120    ForDeref { mut_: bool },
121}
122
123// Helper structs for rendering items/sidebars and carrying along contextual
124// information
125
126/// Struct representing one entry in the JS search index. These are all emitted
127/// by hand to a large JS file at the end of cache-creation.
128#[derive(Debug)]
129pub(crate) struct IndexItem {
130    pub(crate) ty: ItemType,
131    pub(crate) defid: Option<DefId>,
132    pub(crate) name: Symbol,
133    pub(crate) module_path: Vec<Symbol>,
134    pub(crate) desc: String,
135    pub(crate) parent: Option<DefId>,
136    pub(crate) parent_idx: Option<usize>,
137    pub(crate) exact_module_path: Option<Vec<Symbol>>,
138    pub(crate) impl_id: Option<DefId>,
139    pub(crate) search_type: Option<IndexItemFunctionType>,
140    pub(crate) aliases: Box<[Symbol]>,
141    pub(crate) deprecation: Option<Deprecation>,
142}
143
144/// A type used for the search index.
145#[derive(Debug, Eq, PartialEq)]
146struct RenderType {
147    id: Option<RenderTypeId>,
148    generics: Option<Vec<RenderType>>,
149    bindings: Option<Vec<(RenderTypeId, Vec<RenderType>)>>,
150}
151
152impl RenderType {
153    fn size(&self) -> usize {
154        let mut size = 1;
155        if let Some(generics) = &self.generics {
156            size += generics.iter().map(RenderType::size).sum::<usize>();
157        }
158        if let Some(bindings) = &self.bindings {
159            for (_, constraints) in bindings.iter() {
160                size += 1;
161                size += constraints.iter().map(RenderType::size).sum::<usize>();
162            }
163        }
164        size
165    }
166    // Types are rendered as lists of lists, because that's pretty compact.
167    // The contents of the lists are always integers in self-terminating hex
168    // form, handled by `RenderTypeId::write_to_string`, so no commas are
169    // needed to separate the items.
170    fn write_to_string(&self, string: &mut String) {
171        fn write_optional_id(id: Option<RenderTypeId>, string: &mut String) {
172            // 0 is a sentinel, everything else is one-indexed
173            match id {
174                Some(id) => id.write_to_string(string),
175                None => string.push('`'),
176            }
177        }
178        // Either just the type id, or `{type, generics, bindings?}`
179        // where generics is a list of types,
180        // and bindings is a list of `{id, typelist}` pairs.
181        if self.generics.is_some() || self.bindings.is_some() {
182            string.push('{');
183            write_optional_id(self.id, string);
184            string.push('{');
185            for generic in self.generics.as_deref().unwrap_or_default() {
186                generic.write_to_string(string);
187            }
188            string.push('}');
189            if self.bindings.is_some() {
190                string.push('{');
191                for binding in self.bindings.as_deref().unwrap_or_default() {
192                    string.push('{');
193                    binding.0.write_to_string(string);
194                    string.push('{');
195                    for constraint in &binding.1[..] {
196                        constraint.write_to_string(string);
197                    }
198                    string.push_str("}}");
199                }
200                string.push('}');
201            }
202            string.push('}');
203        } else {
204            write_optional_id(self.id, string);
205        }
206    }
207    fn read_from_bytes(string: &[u8]) -> (RenderType, usize) {
208        let mut i = 0;
209        if string[i] == b'{' {
210            i += 1;
211            let (id, offset) = RenderTypeId::read_from_bytes(&string[i..]);
212            i += offset;
213            let generics = if string[i] == b'{' {
214                i += 1;
215                let mut generics = Vec::new();
216                while string[i] != b'}' {
217                    let (ty, offset) = RenderType::read_from_bytes(&string[i..]);
218                    i += offset;
219                    generics.push(ty);
220                }
221                assert!(string[i] == b'}');
222                i += 1;
223                Some(generics)
224            } else {
225                None
226            };
227            let bindings = if string[i] == b'{' {
228                i += 1;
229                let mut bindings = Vec::new();
230                while string[i] == b'{' {
231                    i += 1;
232                    let (binding, boffset) = RenderTypeId::read_from_bytes(&string[i..]);
233                    i += boffset;
234                    let mut bconstraints = Vec::new();
235                    assert!(string[i] == b'{');
236                    i += 1;
237                    while string[i] != b'}' {
238                        let (constraint, coffset) = RenderType::read_from_bytes(&string[i..]);
239                        i += coffset;
240                        bconstraints.push(constraint);
241                    }
242                    assert!(string[i] == b'}');
243                    i += 1;
244                    bindings.push((binding.unwrap(), bconstraints));
245                    assert!(string[i] == b'}');
246                    i += 1;
247                }
248                assert!(string[i] == b'}');
249                i += 1;
250                Some(bindings)
251            } else {
252                None
253            };
254            assert!(string[i] == b'}');
255            i += 1;
256            (RenderType { id, generics, bindings }, i)
257        } else {
258            let (id, offset) = RenderTypeId::read_from_bytes(string);
259            i += offset;
260            (RenderType { id, generics: None, bindings: None }, i)
261        }
262    }
263}
264
265#[derive(Clone, Copy, Debug, Eq, PartialEq)]
266enum RenderTypeId {
267    DefId(DefId),
268    Primitive(clean::PrimitiveType),
269    AssociatedType(Symbol),
270    Index(isize),
271    Mut,
272}
273
274impl RenderTypeId {
275    fn write_to_string(&self, string: &mut String) {
276        let id: i32 = match &self {
277            // 0 is a sentinel, everything else is one-indexed
278            // concrete type
279            RenderTypeId::Index(idx) if *idx >= 0 => (idx + 1isize).try_into().unwrap(),
280            // generic type parameter
281            RenderTypeId::Index(idx) => (*idx).try_into().unwrap(),
282            _ => panic!("must convert render types to indexes before serializing"),
283        };
284        search_index::encode::write_signed_vlqhex_to_string(id, string);
285    }
286    fn read_from_bytes(string: &[u8]) -> (Option<RenderTypeId>, usize) {
287        let Some((value, offset)) = search_index::encode::read_signed_vlqhex_from_string(string)
288        else {
289            return (None, 0);
290        };
291        let value = isize::try_from(value).unwrap();
292        let ty = match value {
293            ..0 => Some(RenderTypeId::Index(value)),
294            0 => None,
295            1.. => Some(RenderTypeId::Index(value - 1)),
296        };
297        (ty, offset)
298    }
299}
300
301/// Full type of functions/methods in the search index.
302#[derive(Debug, Eq, PartialEq)]
303pub(crate) struct IndexItemFunctionType {
304    inputs: Vec<RenderType>,
305    output: Vec<RenderType>,
306    where_clause: Vec<Vec<RenderType>>,
307    param_names: Vec<Option<Symbol>>,
308}
309
310impl IndexItemFunctionType {
311    fn size(&self) -> usize {
312        self.inputs.iter().map(RenderType::size).sum::<usize>()
313            + self.output.iter().map(RenderType::size).sum::<usize>()
314            + self
315                .where_clause
316                .iter()
317                .map(|constraints| constraints.iter().map(RenderType::size).sum::<usize>())
318                .sum::<usize>()
319    }
320    fn read_from_string_without_param_names(string: &[u8]) -> (IndexItemFunctionType, usize) {
321        let mut i = 0;
322        if string[i] == b'`' {
323            return (
324                IndexItemFunctionType {
325                    inputs: Vec::new(),
326                    output: Vec::new(),
327                    where_clause: Vec::new(),
328                    param_names: Vec::new(),
329                },
330                1,
331            );
332        }
333        assert_eq!(b'{', string[i]);
334        i += 1;
335        fn read_args_from_string(string: &[u8]) -> (Vec<RenderType>, usize) {
336            let mut i = 0;
337            let mut params = Vec::new();
338            if string[i] == b'{' {
339                // multiple params
340                i += 1;
341                while string[i] != b'}' {
342                    let (ty, offset) = RenderType::read_from_bytes(&string[i..]);
343                    i += offset;
344                    params.push(ty);
345                }
346                i += 1;
347            } else if string[i] != b'}' {
348                let (tyid, offset) = RenderTypeId::read_from_bytes(&string[i..]);
349                params.push(RenderType { id: tyid, generics: None, bindings: None });
350                i += offset;
351            }
352            (params, i)
353        }
354        let (inputs, offset) = read_args_from_string(&string[i..]);
355        i += offset;
356        let (output, offset) = read_args_from_string(&string[i..]);
357        i += offset;
358        let mut where_clause = Vec::new();
359        while string[i] != b'}' {
360            let (constraint, offset) = read_args_from_string(&string[i..]);
361            i += offset;
362            where_clause.push(constraint);
363        }
364        assert_eq!(b'}', string[i], "{} {}", String::from_utf8_lossy(&string), i);
365        i += 1;
366        (IndexItemFunctionType { inputs, output, where_clause, param_names: Vec::new() }, i)
367    }
368    fn write_to_string_without_param_names<'a>(&'a self, string: &mut String) {
369        // If we couldn't figure out a type, just write 0,
370        // which is encoded as `` ` `` (see RenderTypeId::write_to_string).
371        let has_missing = self
372            .inputs
373            .iter()
374            .chain(self.output.iter())
375            .any(|i| i.id.is_none() && i.generics.is_none());
376        if has_missing {
377            string.push('`');
378        } else {
379            string.push('{');
380            match &self.inputs[..] {
381                [one] if one.generics.is_none() && one.bindings.is_none() => {
382                    one.write_to_string(string);
383                }
384                _ => {
385                    string.push('{');
386                    for item in &self.inputs[..] {
387                        item.write_to_string(string);
388                    }
389                    string.push('}');
390                }
391            }
392            match &self.output[..] {
393                [] if self.where_clause.is_empty() => {}
394                [one] if one.generics.is_none() && one.bindings.is_none() => {
395                    one.write_to_string(string);
396                }
397                _ => {
398                    string.push('{');
399                    for item in &self.output[..] {
400                        item.write_to_string(string);
401                    }
402                    string.push('}');
403                }
404            }
405            for constraint in &self.where_clause {
406                if let [one] = &constraint[..]
407                    && one.generics.is_none()
408                    && one.bindings.is_none()
409                {
410                    one.write_to_string(string);
411                } else {
412                    string.push('{');
413                    for item in &constraint[..] {
414                        item.write_to_string(string);
415                    }
416                    string.push('}');
417                }
418            }
419            string.push('}');
420        }
421    }
422}
423
424#[derive(Debug, Clone)]
425pub(crate) struct StylePath {
426    /// The path to the theme
427    pub(crate) path: PathBuf,
428}
429
430impl StylePath {
431    pub(crate) fn basename(&self) -> Result<String, Error> {
432        Ok(try_none!(try_none!(self.path.file_stem(), &self.path).to_str(), &self.path).to_string())
433    }
434}
435
436#[derive(Debug, Eq, PartialEq, Hash)]
437struct ItemEntry {
438    url: String,
439    name: String,
440}
441
442impl ItemEntry {
443    fn new(mut url: String, name: String) -> ItemEntry {
444        while url.starts_with('/') {
445            url.remove(0);
446        }
447        ItemEntry { url, name }
448    }
449}
450
451impl ItemEntry {
452    fn print(&self) -> impl fmt::Display {
453        fmt::from_fn(move |f| write!(f, "<a href=\"{}\">{}</a>", self.url, Escape(&self.name)))
454    }
455}
456
457impl PartialOrd for ItemEntry {
458    fn partial_cmp(&self, other: &ItemEntry) -> Option<::std::cmp::Ordering> {
459        Some(self.cmp(other))
460    }
461}
462
463impl Ord for ItemEntry {
464    fn cmp(&self, other: &ItemEntry) -> ::std::cmp::Ordering {
465        self.name.cmp(&other.name)
466    }
467}
468
469#[derive(Debug)]
470struct AllTypes {
471    structs: FxIndexSet<ItemEntry>,
472    enums: FxIndexSet<ItemEntry>,
473    unions: FxIndexSet<ItemEntry>,
474    primitives: FxIndexSet<ItemEntry>,
475    traits: FxIndexSet<ItemEntry>,
476    macros: FxIndexSet<ItemEntry>,
477    functions: FxIndexSet<ItemEntry>,
478    type_aliases: FxIndexSet<ItemEntry>,
479    statics: FxIndexSet<ItemEntry>,
480    constants: FxIndexSet<ItemEntry>,
481    attribute_macros: FxIndexSet<ItemEntry>,
482    derive_macros: FxIndexSet<ItemEntry>,
483    trait_aliases: FxIndexSet<ItemEntry>,
484}
485
486impl AllTypes {
487    fn new() -> AllTypes {
488        let new_set = |cap| FxIndexSet::with_capacity_and_hasher(cap, Default::default());
489        AllTypes {
490            structs: new_set(100),
491            enums: new_set(100),
492            unions: new_set(100),
493            primitives: new_set(26),
494            traits: new_set(100),
495            macros: new_set(100),
496            functions: new_set(100),
497            type_aliases: new_set(100),
498            statics: new_set(100),
499            constants: new_set(100),
500            attribute_macros: new_set(100),
501            derive_macros: new_set(100),
502            trait_aliases: new_set(100),
503        }
504    }
505
506    fn append(&mut self, item_name: String, item_type: &ItemType) {
507        let mut url: Vec<_> = item_name.split("::").skip(1).collect();
508        if let Some(name) = url.pop() {
509            let new_url = format!("{}/{item_type}.{name}.html", url.join("/"));
510            url.push(name);
511            let name = url.join("::");
512            match *item_type {
513                ItemType::Struct => self.structs.insert(ItemEntry::new(new_url, name)),
514                ItemType::Enum => self.enums.insert(ItemEntry::new(new_url, name)),
515                ItemType::Union => self.unions.insert(ItemEntry::new(new_url, name)),
516                ItemType::Primitive => self.primitives.insert(ItemEntry::new(new_url, name)),
517                ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)),
518                ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)),
519                ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)),
520                ItemType::TypeAlias => self.type_aliases.insert(ItemEntry::new(new_url, name)),
521                ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)),
522                ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)),
523                ItemType::ProcAttribute => {
524                    self.attribute_macros.insert(ItemEntry::new(new_url, name))
525                }
526                ItemType::ProcDerive => self.derive_macros.insert(ItemEntry::new(new_url, name)),
527                ItemType::TraitAlias => self.trait_aliases.insert(ItemEntry::new(new_url, name)),
528                _ => true,
529            };
530        }
531    }
532
533    fn item_sections(&self) -> FxHashSet<ItemSection> {
534        let mut sections = FxHashSet::default();
535
536        if !self.structs.is_empty() {
537            sections.insert(ItemSection::Structs);
538        }
539        if !self.enums.is_empty() {
540            sections.insert(ItemSection::Enums);
541        }
542        if !self.unions.is_empty() {
543            sections.insert(ItemSection::Unions);
544        }
545        if !self.primitives.is_empty() {
546            sections.insert(ItemSection::PrimitiveTypes);
547        }
548        if !self.traits.is_empty() {
549            sections.insert(ItemSection::Traits);
550        }
551        if !self.macros.is_empty() {
552            sections.insert(ItemSection::Macros);
553        }
554        if !self.functions.is_empty() {
555            sections.insert(ItemSection::Functions);
556        }
557        if !self.type_aliases.is_empty() {
558            sections.insert(ItemSection::TypeAliases);
559        }
560        if !self.statics.is_empty() {
561            sections.insert(ItemSection::Statics);
562        }
563        if !self.constants.is_empty() {
564            sections.insert(ItemSection::Constants);
565        }
566        if !self.attribute_macros.is_empty() {
567            sections.insert(ItemSection::AttributeMacros);
568        }
569        if !self.derive_macros.is_empty() {
570            sections.insert(ItemSection::DeriveMacros);
571        }
572        if !self.trait_aliases.is_empty() {
573            sections.insert(ItemSection::TraitAliases);
574        }
575
576        sections
577    }
578
579    fn print(&self) -> impl fmt::Display {
580        fn print_entries(e: &FxIndexSet<ItemEntry>, kind: ItemSection) -> impl fmt::Display {
581            fmt::from_fn(move |f| {
582                if e.is_empty() {
583                    return Ok(());
584                }
585
586                let mut e: Vec<&ItemEntry> = e.iter().collect();
587                e.sort();
588                write!(
589                    f,
590                    "<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">",
591                    id = kind.id(),
592                    title = kind.name(),
593                )?;
594
595                for s in e.iter() {
596                    write!(f, "<li>{}</li>", s.print())?;
597                }
598
599                f.write_str("</ul>")
600            })
601        }
602
603        fmt::from_fn(|f| {
604            f.write_str("<h1>List of all items</h1>")?;
605            // Note: print_entries does not escape the title, because we know the current set of titles
606            // doesn't require escaping.
607            print_entries(&self.structs, ItemSection::Structs).fmt(f)?;
608            print_entries(&self.enums, ItemSection::Enums).fmt(f)?;
609            print_entries(&self.unions, ItemSection::Unions).fmt(f)?;
610            print_entries(&self.primitives, ItemSection::PrimitiveTypes).fmt(f)?;
611            print_entries(&self.traits, ItemSection::Traits).fmt(f)?;
612            print_entries(&self.macros, ItemSection::Macros).fmt(f)?;
613            print_entries(&self.attribute_macros, ItemSection::AttributeMacros).fmt(f)?;
614            print_entries(&self.derive_macros, ItemSection::DeriveMacros).fmt(f)?;
615            print_entries(&self.functions, ItemSection::Functions).fmt(f)?;
616            print_entries(&self.type_aliases, ItemSection::TypeAliases).fmt(f)?;
617            print_entries(&self.trait_aliases, ItemSection::TraitAliases).fmt(f)?;
618            print_entries(&self.statics, ItemSection::Statics).fmt(f)?;
619            print_entries(&self.constants, ItemSection::Constants).fmt(f)?;
620            Ok(())
621        })
622    }
623}
624
625fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
626    let mut content = SCRAPE_EXAMPLES_HELP_MD.to_owned();
627    content.push_str(&format!(
628        "## More information\n\n\
629      If you want more information about this feature, please read the [corresponding chapter in \
630      the Rustdoc book]({DOC_RUST_LANG_ORG_VERSION}/rustdoc/scraped-examples.html)."
631    ));
632
633    format!(
634        "<div class=\"main-heading\">\
635             <h1>About scraped examples</h1>\
636         </div>\
637         <div>{}</div>",
638        fmt::from_fn(|f| Markdown {
639            content: &content,
640            links: &[],
641            ids: &mut IdMap::default(),
642            error_codes: shared.codes,
643            edition: shared.edition(),
644            playground: &shared.playground,
645            heading_offset: HeadingOffset::H1,
646        }
647        .write_into(f))
648    )
649}
650
651fn document(
652    cx: &Context<'_>,
653    item: &clean::Item,
654    parent: Option<&clean::Item>,
655    heading_offset: HeadingOffset,
656) -> impl fmt::Display {
657    if let Some(ref name) = item.name {
658        info!("Documenting {name}");
659    }
660
661    fmt::from_fn(move |f| {
662        document_item_info(cx, item, parent).render_into(f)?;
663        if parent.is_none() {
664            write!(f, "{}", document_full_collapsible(item, cx, heading_offset))
665        } else {
666            write!(f, "{}", document_full(item, cx, heading_offset))
667        }
668    })
669}
670
671/// Render md_text as markdown.
672fn render_markdown(
673    cx: &Context<'_>,
674    md_text: &str,
675    links: Vec<RenderedLink>,
676    heading_offset: HeadingOffset,
677) -> impl fmt::Display {
678    fmt::from_fn(move |f| {
679        f.write_str("<div class=\"docblock\">")?;
680        Markdown {
681            content: md_text,
682            links: &links,
683            ids: &mut cx.id_map.borrow_mut(),
684            error_codes: cx.shared.codes,
685            edition: cx.shared.edition(),
686            playground: &cx.shared.playground,
687            heading_offset,
688        }
689        .write_into(&mut *f)?;
690        f.write_str("</div>")
691    })
692}
693
694/// Writes a documentation block containing only the first paragraph of the documentation. If the
695/// docs are longer, a "Read more" link is appended to the end.
696fn document_short(
697    item: &clean::Item,
698    cx: &Context<'_>,
699    link: AssocItemLink<'_>,
700    parent: &clean::Item,
701    show_def_docs: bool,
702) -> impl fmt::Display {
703    fmt::from_fn(move |f| {
704        document_item_info(cx, item, Some(parent)).render_into(f)?;
705        if !show_def_docs {
706            return Ok(());
707        }
708        let s = item.doc_value();
709        if !s.is_empty() {
710            let (mut summary_html, has_more_content) =
711                MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content();
712
713            let link = if has_more_content {
714                let link = fmt::from_fn(|f| {
715                    write!(
716                        f,
717                        " <a{}>Read more</a>",
718                        assoc_href_attr(item, link, cx).maybe_display()
719                    )
720                });
721
722                if let Some(idx) = summary_html.rfind("</p>") {
723                    summary_html.insert_str(idx, &link.to_string());
724                    None
725                } else {
726                    Some(link)
727                }
728            } else {
729                None
730            }
731            .maybe_display();
732
733            write!(f, "<div class='docblock'>{summary_html}{link}</div>")?;
734        }
735        Ok(())
736    })
737}
738
739fn document_full_collapsible(
740    item: &clean::Item,
741    cx: &Context<'_>,
742    heading_offset: HeadingOffset,
743) -> impl fmt::Display {
744    document_full_inner(item, cx, true, heading_offset)
745}
746
747fn document_full(
748    item: &clean::Item,
749    cx: &Context<'_>,
750    heading_offset: HeadingOffset,
751) -> impl fmt::Display {
752    document_full_inner(item, cx, false, heading_offset)
753}
754
755fn document_full_inner(
756    item: &clean::Item,
757    cx: &Context<'_>,
758    is_collapsible: bool,
759    heading_offset: HeadingOffset,
760) -> impl fmt::Display {
761    fmt::from_fn(move |f| {
762        if let Some(s) = item.opt_doc_value() {
763            debug!("Doc block: =====\n{s}\n=====");
764            if is_collapsible {
765                write!(
766                    f,
767                    "<details class=\"toggle top-doc\" open>\
768                     <summary class=\"hideme\">\
769                        <span>Expand description</span>\
770                     </summary>{}</details>",
771                    render_markdown(cx, &s, item.links(cx), heading_offset)
772                )?;
773            } else {
774                write!(f, "{}", render_markdown(cx, &s, item.links(cx), heading_offset))?;
775            }
776        }
777
778        let kind = match &item.kind {
779            clean::ItemKind::StrippedItem(box kind) | kind => kind,
780        };
781
782        if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind {
783            render_call_locations(f, cx, item)?;
784        }
785        Ok(())
786    })
787}
788
789#[derive(Template)]
790#[template(path = "item_info.html")]
791struct ItemInfo {
792    items: Vec<ShortItemInfo>,
793}
794/// Add extra information about an item such as:
795///
796/// * Stability
797/// * Deprecated
798/// * Required features (through the `doc_cfg` feature)
799fn document_item_info(
800    cx: &Context<'_>,
801    item: &clean::Item,
802    parent: Option<&clean::Item>,
803) -> ItemInfo {
804    let items = short_item_info(item, cx, parent);
805    ItemInfo { items }
806}
807
808fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> {
809    let cfg = match (&item.cfg, parent.and_then(|p| p.cfg.as_ref())) {
810        (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
811        (cfg, _) => cfg.as_deref().cloned(),
812    };
813
814    debug!(
815        "Portability {name:?} {item_cfg:?} (parent: {parent:?}) - {parent_cfg:?} = {cfg:?}",
816        name = item.name,
817        item_cfg = item.cfg,
818        parent_cfg = parent.and_then(|p| p.cfg.as_ref()),
819    );
820
821    Some(cfg?.render_long_html())
822}
823
824#[derive(Template)]
825#[template(path = "short_item_info.html")]
826enum ShortItemInfo {
827    /// A message describing the deprecation of this item
828    Deprecation {
829        message: String,
830    },
831    /// The feature corresponding to an unstable item, and optionally
832    /// a tracking issue URL and number.
833    Unstable {
834        feature: String,
835        tracking: Option<(String, u32)>,
836    },
837    Portability {
838        message: String,
839    },
840}
841
842/// Render the stability, deprecation and portability information that is displayed at the top of
843/// the item's documentation.
844fn short_item_info(
845    item: &clean::Item,
846    cx: &Context<'_>,
847    parent: Option<&clean::Item>,
848) -> Vec<ShortItemInfo> {
849    let mut extra_info = vec![];
850
851    if let Some(depr @ Deprecation { note, since, suggestion: _ }) = item.deprecation(cx.tcx()) {
852        // We display deprecation messages for #[deprecated], but only display
853        // the future-deprecation messages for rustc versions.
854        let mut message = match since {
855            DeprecatedSince::RustcVersion(version) => {
856                if depr.is_in_effect() {
857                    format!("Deprecated since {version}")
858                } else {
859                    format!("Deprecating in {version}")
860                }
861            }
862            DeprecatedSince::Future => String::from("Deprecating in a future version"),
863            DeprecatedSince::NonStandard(since) => {
864                format!("Deprecated since {}", Escape(since.as_str()))
865            }
866            DeprecatedSince::Unspecified | DeprecatedSince::Err => String::from("Deprecated"),
867        };
868
869        if let Some(note) = note {
870            let note = note.as_str();
871            let mut id_map = cx.id_map.borrow_mut();
872            let html = MarkdownItemInfo(note, &mut id_map);
873            message.push_str(": ");
874            html.write_into(&mut message).unwrap();
875        }
876        extra_info.push(ShortItemInfo::Deprecation { message });
877    }
878
879    // Render unstable items. But don't render "rustc_private" crates (internal compiler crates).
880    // Those crates are permanently unstable so it makes no sense to render "unstable" everywhere.
881    if let Some((StabilityLevel::Unstable { reason: _, issue, .. }, feature)) = item
882        .stability(cx.tcx())
883        .as_ref()
884        .filter(|stab| stab.feature != sym::rustc_private)
885        .map(|stab| (stab.level, stab.feature))
886    {
887        let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue)
888        {
889            Some((url.clone(), issue.get()))
890        } else {
891            None
892        };
893        extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking });
894    }
895
896    if let Some(message) = portability(item, parent) {
897        extra_info.push(ShortItemInfo::Portability { message });
898    }
899
900    extra_info
901}
902
903// Render the list of items inside one of the sections "Trait Implementations",
904// "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages).
905fn render_impls(
906    cx: &Context<'_>,
907    mut w: impl Write,
908    impls: &[&Impl],
909    containing_item: &clean::Item,
910    toggle_open_by_default: bool,
911) {
912    let mut rendered_impls = impls
913        .iter()
914        .map(|i| {
915            let did = i.trait_did().unwrap();
916            let provided_trait_methods = i.inner_impl().provided_trait_methods(cx.tcx());
917            let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods);
918            let imp = render_impl(
919                cx,
920                i,
921                containing_item,
922                assoc_link,
923                RenderMode::Normal,
924                None,
925                &[],
926                ImplRenderingParameters {
927                    show_def_docs: true,
928                    show_default_items: true,
929                    show_non_assoc_items: true,
930                    toggle_open_by_default,
931                },
932            );
933            imp.to_string()
934        })
935        .collect::<Vec<_>>();
936    rendered_impls.sort();
937    w.write_str(&rendered_impls.join("")).unwrap();
938}
939
940/// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item.
941fn assoc_href_attr(
942    it: &clean::Item,
943    link: AssocItemLink<'_>,
944    cx: &Context<'_>,
945) -> Option<impl fmt::Display> {
946    let name = it.name.unwrap();
947    let item_type = it.type_();
948
949    enum Href<'a> {
950        AnchorId(&'a str),
951        Anchor(ItemType),
952        Url(String, ItemType),
953    }
954
955    let href = match link {
956        AssocItemLink::Anchor(Some(id)) => Href::AnchorId(id),
957        AssocItemLink::Anchor(None) => Href::Anchor(item_type),
958        AssocItemLink::GotoSource(did, provided_methods) => {
959            // We're creating a link from the implementation of an associated item to its
960            // declaration in the trait declaration.
961            let item_type = match item_type {
962                // For historical but not technical reasons, the item type of methods in
963                // trait declarations depends on whether the method is required (`TyMethod`) or
964                // provided (`Method`).
965                ItemType::Method | ItemType::TyMethod => {
966                    if provided_methods.contains(&name) {
967                        ItemType::Method
968                    } else {
969                        ItemType::TyMethod
970                    }
971                }
972                // For associated types and constants, no such distinction exists.
973                item_type => item_type,
974            };
975
976            match href(did.expect_def_id(), cx) {
977                Ok((url, ..)) => Href::Url(url, item_type),
978                // The link is broken since it points to an external crate that wasn't documented.
979                // Do not create any link in such case. This is better than falling back to a
980                // dummy anchor like `#{item_type}.{name}` representing the `id` of *this* impl item
981                // (that used to happen in older versions). Indeed, in most cases this dummy would
982                // coincide with the `id`. However, it would not always do so.
983                // In general, this dummy would be incorrect:
984                // If the type with the trait impl also had an inherent impl with an assoc. item of
985                // the *same* name as this impl item, the dummy would link to that one even though
986                // those two items are distinct!
987                // In this scenario, the actual `id` of this impl item would be
988                // `#{item_type}.{name}-{n}` for some number `n` (a disambiguator).
989                Err(HrefError::DocumentationNotBuilt) => return None,
990                Err(_) => Href::Anchor(item_type),
991            }
992        }
993    };
994
995    let href = fmt::from_fn(move |f| match &href {
996        Href::AnchorId(id) => write!(f, "#{id}"),
997        Href::Url(url, item_type) => {
998            write!(f, "{url}#{item_type}.{name}")
999        }
1000        Href::Anchor(item_type) => {
1001            write!(f, "#{item_type}.{name}")
1002        }
1003    });
1004
1005    // If there is no `href` for the reason explained above, simply do not render it which is valid:
1006    // https://html.spec.whatwg.org/multipage/links.html#links-created-by-a-and-area-elements
1007    Some(fmt::from_fn(move |f| write!(f, " href=\"{href}\"")))
1008}
1009
1010#[derive(Debug)]
1011enum AssocConstValue<'a> {
1012    // In trait definitions, it is relevant for the public API whether an
1013    // associated constant comes with a default value, so even if we cannot
1014    // render its value, the presence of a value must be shown using `= _`.
1015    TraitDefault(&'a clean::ConstantKind),
1016    // In impls, there is no need to show `= _`.
1017    Impl(&'a clean::ConstantKind),
1018    None,
1019}
1020
1021fn assoc_const(
1022    it: &clean::Item,
1023    generics: &clean::Generics,
1024    ty: &clean::Type,
1025    value: AssocConstValue<'_>,
1026    link: AssocItemLink<'_>,
1027    indent: usize,
1028    cx: &Context<'_>,
1029) -> impl fmt::Display {
1030    let tcx = cx.tcx();
1031    fmt::from_fn(move |w| {
1032        render_attributes_in_code(w, it, &" ".repeat(indent), cx);
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            render_attributes_in_code(w, 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
1313struct CodeAttribute(String);
1314
1315fn render_code_attribute(prefix: &str, code_attr: CodeAttribute, w: &mut impl fmt::Write) {
1316    write!(
1317        w,
1318        "<div class=\"code-attribute\">{prefix}{attr}</div>",
1319        prefix = prefix,
1320        attr = code_attr.0
1321    )
1322    .unwrap();
1323}
1324
1325// When an attribute is rendered inside a <code> tag, it is formatted using
1326// a div to produce a newline after it.
1327fn render_attributes_in_code(
1328    w: &mut impl fmt::Write,
1329    it: &clean::Item,
1330    prefix: &str,
1331    cx: &Context<'_>,
1332) {
1333    for attr in it.attributes(cx.tcx(), cx.cache()) {
1334        render_code_attribute(prefix, 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}