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