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