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