Skip to main content

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