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, RealFileName};
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 html = MarkdownItemInfo(note, &mut id_map);
881            message.push_str(": ");
882            html.write_into(&mut message).unwrap();
883        }
884        extra_info.push(ShortItemInfo::Deprecation { message });
885    }
886
887    // Render unstable items. But don't render "rustc_private" crates (internal compiler crates).
888    // Those crates are permanently unstable so it makes no sense to render "unstable" everywhere.
889    if let Some((StabilityLevel::Unstable { reason: _, issue, .. }, feature)) = item
890        .stability(cx.tcx())
891        .as_ref()
892        .filter(|stab| stab.feature != sym::rustc_private)
893        .map(|stab| (stab.level, stab.feature))
894    {
895        let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue)
896        {
897            Some((url.clone(), issue.get()))
898        } else {
899            None
900        };
901        extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking });
902    }
903
904    if let Some(message) = portability(item, parent) {
905        extra_info.push(ShortItemInfo::Portability { message });
906    }
907
908    extra_info
909}
910
911// Render the list of items inside one of the sections "Trait Implementations",
912// "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages).
913fn render_impls(
914    cx: &Context<'_>,
915    mut w: impl Write,
916    impls: &[&Impl],
917    containing_item: &clean::Item,
918    toggle_open_by_default: bool,
919) -> fmt::Result {
920    let mut rendered_impls = impls
921        .iter()
922        .map(|i| {
923            let did = i.trait_did().unwrap();
924            let provided_trait_methods = i.inner_impl().provided_trait_methods(cx.tcx());
925            let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods);
926            let imp = render_impl(
927                cx,
928                i,
929                containing_item,
930                assoc_link,
931                RenderMode::Normal,
932                None,
933                &[],
934                ImplRenderingParameters {
935                    show_def_docs: true,
936                    show_default_items: true,
937                    show_non_assoc_items: true,
938                    toggle_open_by_default,
939                },
940            );
941            imp.to_string()
942        })
943        .collect::<Vec<_>>();
944    rendered_impls.sort();
945    w.write_str(&rendered_impls.join(""))
946}
947
948/// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item.
949fn assoc_href_attr(
950    it: &clean::Item,
951    link: AssocItemLink<'_>,
952    cx: &Context<'_>,
953) -> Option<impl fmt::Display> {
954    let name = it.name.unwrap();
955    let item_type = it.type_();
956
957    enum Href<'a> {
958        AnchorId(&'a str),
959        Anchor(ItemType),
960        Url(String, ItemType),
961    }
962
963    let href = match link {
964        AssocItemLink::Anchor(Some(id)) => Href::AnchorId(id),
965        AssocItemLink::Anchor(None) => Href::Anchor(item_type),
966        AssocItemLink::GotoSource(did, provided_methods) => {
967            // We're creating a link from the implementation of an associated item to its
968            // declaration in the trait declaration.
969            let item_type = match item_type {
970                // For historical but not technical reasons, the item type of methods in
971                // trait declarations depends on whether the method is required (`TyMethod`) or
972                // provided (`Method`).
973                ItemType::Method | ItemType::TyMethod => {
974                    if provided_methods.contains(&name) {
975                        ItemType::Method
976                    } else {
977                        ItemType::TyMethod
978                    }
979                }
980                // For associated types and constants, no such distinction exists.
981                item_type => item_type,
982            };
983
984            match href(did.expect_def_id(), cx) {
985                Ok(HrefInfo { url, .. }) => Href::Url(url, item_type),
986                // The link is broken since it points to an external crate that wasn't documented.
987                // Do not create any link in such case. This is better than falling back to a
988                // dummy anchor like `#{item_type}.{name}` representing the `id` of *this* impl item
989                // (that used to happen in older versions). Indeed, in most cases this dummy would
990                // coincide with the `id`. However, it would not always do so.
991                // In general, this dummy would be incorrect:
992                // If the type with the trait impl also had an inherent impl with an assoc. item of
993                // the *same* name as this impl item, the dummy would link to that one even though
994                // those two items are distinct!
995                // In this scenario, the actual `id` of this impl item would be
996                // `#{item_type}.{name}-{n}` for some number `n` (a disambiguator).
997                Err(HrefError::DocumentationNotBuilt) => return None,
998                Err(_) => Href::Anchor(item_type),
999            }
1000        }
1001    };
1002
1003    let href = fmt::from_fn(move |f| match &href {
1004        Href::AnchorId(id) => write!(f, "#{id}"),
1005        Href::Url(url, item_type) => {
1006            write!(f, "{url}#{item_type}.{name}")
1007        }
1008        Href::Anchor(item_type) => {
1009            write!(f, "#{item_type}.{name}")
1010        }
1011    });
1012
1013    // If there is no `href` for the reason explained above, simply do not render it which is valid:
1014    // https://html.spec.whatwg.org/multipage/links.html#links-created-by-a-and-area-elements
1015    Some(fmt::from_fn(move |f| write!(f, " href=\"{href}\"")))
1016}
1017
1018#[derive(Debug)]
1019enum AssocConstValue<'a> {
1020    // In trait definitions, it is relevant for the public API whether an
1021    // associated constant comes with a default value, so even if we cannot
1022    // render its value, the presence of a value must be shown using `= _`.
1023    TraitDefault(&'a clean::ConstantKind),
1024    // In impls, there is no need to show `= _`.
1025    Impl(&'a clean::ConstantKind),
1026    None,
1027}
1028
1029fn assoc_const(
1030    it: &clean::Item,
1031    generics: &clean::Generics,
1032    ty: &clean::Type,
1033    value: AssocConstValue<'_>,
1034    link: AssocItemLink<'_>,
1035    indent: usize,
1036    cx: &Context<'_>,
1037) -> impl fmt::Display {
1038    let tcx = cx.tcx();
1039    fmt::from_fn(move |w| {
1040        render_attributes_in_code(w, it, &" ".repeat(indent), cx)?;
1041        write!(
1042            w,
1043            "{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
1044            indent = " ".repeat(indent),
1045            vis = visibility_print_with_space(it, cx),
1046            href = assoc_href_attr(it, link, cx).maybe_display(),
1047            name = it.name.as_ref().unwrap(),
1048            generics = print_generics(generics, cx),
1049            ty = print_type(ty, cx),
1050        )?;
1051        if let AssocConstValue::TraitDefault(konst) | AssocConstValue::Impl(konst) = value {
1052            let repr = konst.expr(tcx);
1053            if match value {
1054                AssocConstValue::TraitDefault(_) => true, // always show
1055                // FIXME: Comparing against the special string "_" denoting overly complex const exprs
1056                //        is rather hacky; `ConstKind::expr` should have a richer return type.
1057                AssocConstValue::Impl(_) => repr != "_", // show if there is a meaningful value to show
1058                AssocConstValue::None => unreachable!(),
1059            } {
1060                write!(w, " = {}", Escape(&repr))?;
1061            }
1062        }
1063        write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display())
1064    })
1065}
1066
1067fn assoc_type(
1068    it: &clean::Item,
1069    generics: &clean::Generics,
1070    bounds: &[clean::GenericBound],
1071    default: Option<&clean::Type>,
1072    link: AssocItemLink<'_>,
1073    indent: usize,
1074    cx: &Context<'_>,
1075) -> impl fmt::Display {
1076    fmt::from_fn(move |w| {
1077        write!(
1078            w,
1079            "{indent}{vis}type <a{href} class=\"associatedtype\">{name}</a>{generics}",
1080            indent = " ".repeat(indent),
1081            vis = visibility_print_with_space(it, cx),
1082            href = assoc_href_attr(it, link, cx).maybe_display(),
1083            name = it.name.as_ref().unwrap(),
1084            generics = print_generics(generics, cx),
1085        )?;
1086        if !bounds.is_empty() {
1087            write!(w, ": {}", print_generic_bounds(bounds, cx))?;
1088        }
1089        // Render the default before the where-clause which aligns with the new recommended style. See #89122.
1090        if let Some(default) = default {
1091            write!(w, " = {}", print_type(default, cx))?;
1092        }
1093        write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display())
1094    })
1095}
1096
1097fn assoc_method(
1098    meth: &clean::Item,
1099    g: &clean::Generics,
1100    d: &clean::FnDecl,
1101    link: AssocItemLink<'_>,
1102    parent: ItemType,
1103    cx: &Context<'_>,
1104    render_mode: RenderMode,
1105) -> impl fmt::Display {
1106    let tcx = cx.tcx();
1107    let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item");
1108    let name = meth.name.as_ref().unwrap();
1109    let vis = visibility_print_with_space(meth, cx).to_string();
1110    let defaultness = print_default_space(meth.is_default());
1111    // FIXME: Once https://github.com/rust-lang/rust/issues/143874 is implemented, we can remove
1112    // this condition.
1113    let constness = match render_mode {
1114        RenderMode::Normal => print_constness_with_space(
1115            &header.constness,
1116            meth.stable_since(tcx),
1117            meth.const_stability(tcx),
1118        ),
1119        RenderMode::ForDeref { .. } => "",
1120    };
1121
1122    fmt::from_fn(move |w| {
1123        let asyncness = header.asyncness.print_with_space();
1124        let safety = header.safety.print_with_space();
1125        let abi = print_abi_with_space(header.abi).to_string();
1126        let href = assoc_href_attr(meth, link, cx).maybe_display();
1127
1128        // NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`.
1129        let generics_len = format!("{:#}", print_generics(g, cx)).len();
1130        let mut header_len = "fn ".len()
1131            + vis.len()
1132            + defaultness.len()
1133            + constness.len()
1134            + asyncness.len()
1135            + safety.len()
1136            + abi.len()
1137            + name.as_str().len()
1138            + generics_len;
1139
1140        let notable_traits = notable_traits_button(&d.output, cx).maybe_display();
1141
1142        let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
1143            header_len += 4;
1144            let indent_str = "    ";
1145            render_attributes_in_code(w, meth, indent_str, cx)?;
1146            (4, indent_str, Ending::NoNewline)
1147        } else {
1148            render_attributes_in_code(w, meth, "", cx)?;
1149            (0, "", Ending::Newline)
1150        };
1151        write!(
1152            w,
1153            "{indent}{vis}{defaultness}{constness}{asyncness}{safety}{abi}fn \
1154            <a{href} class=\"fn\">{name}</a>{generics}{decl}{notable_traits}{where_clause}",
1155            indent = indent_str,
1156            generics = print_generics(g, cx),
1157            decl = full_print_fn_decl(d, header_len, indent, cx),
1158            where_clause = print_where_clause(g, cx, indent, end_newline).maybe_display(),
1159        )
1160    })
1161}
1162
1163/// Writes a span containing the versions at which an item became stable and/or const-stable. For
1164/// example, if the item became stable at 1.0.0, and const-stable at 1.45.0, this function would
1165/// write a span containing "1.0.0 (const: 1.45.0)".
1166///
1167/// Returns `None` if there is no stability annotation to be rendered.
1168///
1169/// Stability and const-stability are considered separately. If the item is unstable, no version
1170/// will be written. If the item is const-unstable, "const: unstable" will be appended to the
1171/// span, with a link to the tracking issue if present. If an item's stability or const-stability
1172/// version matches the version of its enclosing item, that version will be omitted.
1173///
1174/// Note that it is possible for an unstable function to be const-stable. In that case, the span
1175/// will include the const-stable version, but no stable version will be emitted, as a natural
1176/// consequence of the above rules.
1177fn render_stability_since_raw_with_extra(
1178    stable_version: Option<StableSince>,
1179    const_stability: Option<ConstStability>,
1180    extra_class: &str,
1181) -> Option<impl fmt::Display> {
1182    let mut title = String::new();
1183    let mut stability = String::new();
1184
1185    if let Some(version) = stable_version.and_then(|version| since_to_string(&version)) {
1186        stability.push_str(&version);
1187        title.push_str(&format!("Stable since Rust version {version}"));
1188    }
1189
1190    let const_title_and_stability = match const_stability {
1191        Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) => {
1192            since_to_string(&since)
1193                .map(|since| (format!("const since {since}"), format!("const: {since}")))
1194        }
1195        Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => {
1196            if stable_version.is_none() {
1197                // don't display const unstable if entirely unstable
1198                None
1199            } else {
1200                let unstable = if let Some(n) = issue {
1201                    format!(
1202                        "<a \
1203                        href=\"https://github.com/rust-lang/rust/issues/{n}\" \
1204                        title=\"Tracking issue for {feature}\"\
1205                       >unstable</a>"
1206                    )
1207                } else {
1208                    String::from("unstable")
1209                };
1210
1211                Some((String::from("const unstable"), format!("const: {unstable}")))
1212            }
1213        }
1214        _ => None,
1215    };
1216
1217    if let Some((const_title, const_stability)) = const_title_and_stability {
1218        if !title.is_empty() {
1219            title.push_str(&format!(", {const_title}"));
1220        } else {
1221            title.push_str(&const_title);
1222        }
1223
1224        if !stability.is_empty() {
1225            stability.push_str(&format!(" ({const_stability})"));
1226        } else {
1227            stability.push_str(&const_stability);
1228        }
1229    }
1230
1231    (!stability.is_empty()).then_some(fmt::from_fn(move |w| {
1232        write!(w, r#"<span class="since{extra_class}" title="{title}">{stability}</span>"#)
1233    }))
1234}
1235
1236fn since_to_string(since: &StableSince) -> Option<String> {
1237    match since {
1238        StableSince::Version(since) => Some(since.to_string()),
1239        StableSince::Current => Some(RustcVersion::CURRENT.to_string()),
1240        StableSince::Err(_) => None,
1241    }
1242}
1243
1244#[inline]
1245fn render_stability_since_raw(
1246    ver: Option<StableSince>,
1247    const_stability: Option<ConstStability>,
1248) -> Option<impl fmt::Display> {
1249    render_stability_since_raw_with_extra(ver, const_stability, "")
1250}
1251
1252fn render_assoc_item(
1253    item: &clean::Item,
1254    link: AssocItemLink<'_>,
1255    parent: ItemType,
1256    cx: &Context<'_>,
1257    render_mode: RenderMode,
1258) -> impl fmt::Display {
1259    fmt::from_fn(move |f| match &item.kind {
1260        clean::StrippedItem(..) => Ok(()),
1261        clean::RequiredMethodItem(m) | clean::MethodItem(m, _) => {
1262            assoc_method(item, &m.generics, &m.decl, link, parent, cx, render_mode).fmt(f)
1263        }
1264        clean::RequiredAssocConstItem(generics, ty) => assoc_const(
1265            item,
1266            generics,
1267            ty,
1268            AssocConstValue::None,
1269            link,
1270            if parent == ItemType::Trait { 4 } else { 0 },
1271            cx,
1272        )
1273        .fmt(f),
1274        clean::ProvidedAssocConstItem(ci) => assoc_const(
1275            item,
1276            &ci.generics,
1277            &ci.type_,
1278            AssocConstValue::TraitDefault(&ci.kind),
1279            link,
1280            if parent == ItemType::Trait { 4 } else { 0 },
1281            cx,
1282        )
1283        .fmt(f),
1284        clean::ImplAssocConstItem(ci) => assoc_const(
1285            item,
1286            &ci.generics,
1287            &ci.type_,
1288            AssocConstValue::Impl(&ci.kind),
1289            link,
1290            if parent == ItemType::Trait { 4 } else { 0 },
1291            cx,
1292        )
1293        .fmt(f),
1294        clean::RequiredAssocTypeItem(generics, bounds) => assoc_type(
1295            item,
1296            generics,
1297            bounds,
1298            None,
1299            link,
1300            if parent == ItemType::Trait { 4 } else { 0 },
1301            cx,
1302        )
1303        .fmt(f),
1304        clean::AssocTypeItem(ty, bounds) => assoc_type(
1305            item,
1306            &ty.generics,
1307            bounds,
1308            Some(ty.item_type.as_ref().unwrap_or(&ty.type_)),
1309            link,
1310            if parent == ItemType::Trait { 4 } else { 0 },
1311            cx,
1312        )
1313        .fmt(f),
1314        _ => panic!("render_assoc_item called on non-associated-item"),
1315    })
1316}
1317
1318#[derive(Copy, Clone)]
1319enum AssocItemLink<'a> {
1320    Anchor(Option<&'a str>),
1321    GotoSource(ItemId, &'a FxIndexSet<Symbol>),
1322}
1323
1324impl<'a> AssocItemLink<'a> {
1325    fn anchor(&self, id: &'a str) -> Self {
1326        match *self {
1327            AssocItemLink::Anchor(_) => AssocItemLink::Anchor(Some(id)),
1328            ref other => *other,
1329        }
1330    }
1331}
1332
1333fn write_section_heading(
1334    title: impl fmt::Display,
1335    id: &str,
1336    extra_class: Option<&str>,
1337    extra: impl fmt::Display,
1338) -> impl fmt::Display {
1339    fmt::from_fn(move |w| {
1340        let (extra_class, whitespace) = match extra_class {
1341            Some(extra) => (extra, " "),
1342            None => ("", ""),
1343        };
1344        write!(
1345            w,
1346            "<h2 id=\"{id}\" class=\"{extra_class}{whitespace}section-header\">\
1347            {title}\
1348            <a href=\"#{id}\" class=\"anchor\">§</a>\
1349         </h2>{extra}",
1350        )
1351    })
1352}
1353
1354fn write_impl_section_heading(title: impl fmt::Display, id: &str) -> impl fmt::Display {
1355    write_section_heading(title, id, None, "")
1356}
1357
1358fn render_all_impls(
1359    mut w: impl Write,
1360    cx: &Context<'_>,
1361    containing_item: &clean::Item,
1362    concrete: &[&Impl],
1363    synthetic: &[&Impl],
1364    blanket_impl: &[&Impl],
1365) -> fmt::Result {
1366    let impls = {
1367        let mut buf = String::new();
1368        render_impls(cx, &mut buf, concrete, containing_item, true)?;
1369        buf
1370    };
1371    if !impls.is_empty() {
1372        write!(
1373            w,
1374            "{}<div id=\"trait-implementations-list\">{impls}</div>",
1375            write_impl_section_heading("Trait Implementations", "trait-implementations")
1376        )?;
1377    }
1378
1379    if !synthetic.is_empty() {
1380        write!(
1381            w,
1382            "{}<div id=\"synthetic-implementations-list\">",
1383            write_impl_section_heading("Auto Trait Implementations", "synthetic-implementations",)
1384        )?;
1385        render_impls(cx, &mut w, synthetic, containing_item, false)?;
1386        w.write_str("</div>")?;
1387    }
1388
1389    if !blanket_impl.is_empty() {
1390        write!(
1391            w,
1392            "{}<div id=\"blanket-implementations-list\">",
1393            write_impl_section_heading("Blanket Implementations", "blanket-implementations")
1394        )?;
1395        render_impls(cx, &mut w, blanket_impl, containing_item, false)?;
1396        w.write_str("</div>")?;
1397    }
1398    Ok(())
1399}
1400
1401fn render_assoc_items(
1402    cx: &Context<'_>,
1403    containing_item: &clean::Item,
1404    it: DefId,
1405    what: AssocItemRender<'_>,
1406) -> impl fmt::Display {
1407    fmt::from_fn(move |f| {
1408        let mut derefs = DefIdSet::default();
1409        derefs.insert(it);
1410        render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs)
1411    })
1412}
1413
1414fn render_assoc_items_inner(
1415    mut w: &mut dyn fmt::Write,
1416    cx: &Context<'_>,
1417    containing_item: &clean::Item,
1418    it: DefId,
1419    what: AssocItemRender<'_>,
1420    derefs: &mut DefIdSet,
1421) -> fmt::Result {
1422    info!("Documenting associated items of {:?}", containing_item.name);
1423    let cache = &cx.shared.cache;
1424    let Some(v) = cache.impls.get(&it) else { return Ok(()) };
1425    let (mut non_trait, traits): (Vec<_>, _) =
1426        v.iter().partition(|i| i.inner_impl().trait_.is_none());
1427    if !non_trait.is_empty() {
1428        let render_mode = what.render_mode();
1429        let class_html = what
1430            .class()
1431            .map(|class| fmt::from_fn(move |f| write!(f, r#" class="{class}""#)))
1432            .maybe_display();
1433        let (section_heading, id) = match what {
1434            AssocItemRender::All => (
1435                Either::Left(write_impl_section_heading("Implementations", "implementations")),
1436                Cow::Borrowed("implementations-list"),
1437            ),
1438            AssocItemRender::DerefFor { trait_, type_, .. } => {
1439                let id = cx.derive_id(small_url_encode(format!(
1440                    "deref-methods-{:#}",
1441                    print_type(type_, cx)
1442                )));
1443                // the `impls.get` above only looks at the outermost type,
1444                // and the Deref impl may only be implemented for certain
1445                // values of generic parameters.
1446                // for example, if an item impls `Deref<[u8]>`,
1447                // we should not show methods from `[MaybeUninit<u8>]`.
1448                // this `retain` filters out any instances where
1449                // the types do not line up perfectly.
1450                non_trait.retain(|impl_| {
1451                    type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache)
1452                });
1453                let derived_id = cx.derive_id(&id);
1454                if let Some(def_id) = type_.def_id(cx.cache()) {
1455                    cx.deref_id_map.borrow_mut().insert(def_id, id.clone());
1456                }
1457                (
1458                    Either::Right(fmt::from_fn(move |f| {
1459                        write!(
1460                            f,
1461                            "<details class=\"toggle big-toggle\" open><summary>{}</summary>",
1462                            write_impl_section_heading(
1463                                fmt::from_fn(|f| write!(
1464                                    f,
1465                                    "<span>Methods from {trait_}&lt;Target = {type_}&gt;</span>",
1466                                    trait_ = print_path(trait_, cx),
1467                                    type_ = print_type(type_, cx),
1468                                )),
1469                                &id,
1470                            )
1471                        )
1472                    })),
1473                    Cow::Owned(derived_id),
1474                )
1475            }
1476        };
1477        let impls_buf = fmt::from_fn(|f| {
1478            non_trait
1479                .iter()
1480                .map(|i| {
1481                    render_impl(
1482                        cx,
1483                        i,
1484                        containing_item,
1485                        AssocItemLink::Anchor(None),
1486                        render_mode,
1487                        None,
1488                        &[],
1489                        ImplRenderingParameters {
1490                            show_def_docs: true,
1491                            show_default_items: true,
1492                            show_non_assoc_items: true,
1493                            toggle_open_by_default: true,
1494                        },
1495                    )
1496                })
1497                .joined("", f)
1498        })
1499        .to_string();
1500
1501        if !impls_buf.is_empty() {
1502            write!(
1503                w,
1504                "{section_heading}<div id=\"{id}\"{class_html}>{impls_buf}</div>{}",
1505                matches!(what, AssocItemRender::DerefFor { .. })
1506                    .then_some("</details>")
1507                    .maybe_display(),
1508            )?;
1509        }
1510    }
1511
1512    if !traits.is_empty() {
1513        let deref_impl =
1514            traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait());
1515        if let Some(impl_) = deref_impl {
1516            let has_deref_mut =
1517                traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
1518            render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs)?;
1519        }
1520
1521        // If we were already one level into rendering deref methods, we don't want to render
1522        // anything after recursing into any further deref methods above.
1523        if let AssocItemRender::DerefFor { .. } = what {
1524            return Ok(());
1525        }
1526
1527        let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
1528            traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
1529        let (blanket_impl, concrete): (Vec<&Impl>, _) =
1530            concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
1531
1532        render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl)?;
1533    }
1534    Ok(())
1535}
1536
1537/// `derefs` is the set of all deref targets that have already been handled.
1538fn render_deref_methods(
1539    mut w: impl Write,
1540    cx: &Context<'_>,
1541    impl_: &Impl,
1542    container_item: &clean::Item,
1543    deref_mut: bool,
1544    derefs: &mut DefIdSet,
1545) -> fmt::Result {
1546    let cache = cx.cache();
1547    let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
1548    let (target, real_target) = impl_
1549        .inner_impl()
1550        .items
1551        .iter()
1552        .find_map(|item| match item.kind {
1553            clean::AssocTypeItem(box ref t, _) => Some(match *t {
1554                clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
1555                _ => (&t.type_, &t.type_),
1556            }),
1557            _ => None,
1558        })
1559        .expect("Expected associated type binding");
1560    debug!(
1561        "Render deref methods for {for_:#?}, target {target:#?}",
1562        for_ = impl_.inner_impl().for_
1563    );
1564    let what =
1565        AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
1566    if let Some(did) = target.def_id(cache) {
1567        if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) {
1568            // `impl Deref<Target = S> for S`
1569            if did == type_did || !derefs.insert(did) {
1570                // Avoid infinite cycles
1571                return Ok(());
1572            }
1573        }
1574        render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1575    } else if let Some(prim) = target.primitive_type()
1576        && let Some(&did) = cache.primitive_locations.get(&prim)
1577    {
1578        render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1579    }
1580    Ok(())
1581}
1582
1583fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool {
1584    let self_type_opt = match item.kind {
1585        clean::MethodItem(ref method, _) => method.decl.receiver_type(),
1586        clean::RequiredMethodItem(ref method) => method.decl.receiver_type(),
1587        _ => None,
1588    };
1589
1590    if let Some(self_ty) = self_type_opt {
1591        let (by_mut_ref, by_box, by_value) = match *self_ty {
1592            clean::Type::BorrowedRef { mutability, .. } => {
1593                (mutability == Mutability::Mut, false, false)
1594            }
1595            clean::Type::Path { ref path } => {
1596                (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false)
1597            }
1598            clean::Type::SelfTy => (false, false, true),
1599            _ => (false, false, false),
1600        };
1601
1602        (deref_mut_ || !by_mut_ref) && !by_box && !by_value
1603    } else {
1604        false
1605    }
1606}
1607
1608fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option<impl fmt::Display> {
1609    if ty.is_unit() {
1610        // Very common fast path.
1611        return None;
1612    }
1613
1614    let did = ty.def_id(cx.cache())?;
1615
1616    // Box has pass-through impls for Read, Write, Iterator, and Future when the
1617    // boxed type implements one of those. We don't want to treat every Box return
1618    // as being notably an Iterator (etc), though, so we exempt it. Pin has the same
1619    // issue, with a pass-through impl for Future.
1620    if Some(did) == cx.tcx().lang_items().owned_box()
1621        || Some(did) == cx.tcx().lang_items().pin_type()
1622    {
1623        return None;
1624    }
1625
1626    let impls = cx.cache().impls.get(&did)?;
1627    let has_notable_trait = impls
1628        .iter()
1629        .map(Impl::inner_impl)
1630        .filter(|impl_| {
1631            impl_.polarity == ty::ImplPolarity::Positive
1632                // Two different types might have the same did,
1633                // without actually being the same.
1634                && ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1635        })
1636        .filter_map(|impl_| impl_.trait_.as_ref())
1637        .filter_map(|trait_| cx.cache().traits.get(&trait_.def_id()))
1638        .any(|t| t.is_notable_trait(cx.tcx()));
1639
1640    has_notable_trait.then(|| {
1641        cx.types_with_notable_traits.borrow_mut().insert(ty.clone());
1642        fmt::from_fn(|f| {
1643            write!(
1644                f,
1645                " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>",
1646                ty = Escape(&format!("{:#}", print_type(ty, cx))),
1647            )
1648        })
1649    })
1650}
1651
1652fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
1653    let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
1654
1655    let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");
1656
1657    let out = fmt::from_fn(|f| {
1658        let mut notable_impls = impls
1659            .iter()
1660            .map(|impl_| impl_.inner_impl())
1661            .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive)
1662            .filter(|impl_| {
1663                // Two different types might have the same did, without actually being the same.
1664                ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1665            })
1666            .filter_map(|impl_| {
1667                if let Some(trait_) = &impl_.trait_
1668                    && let trait_did = trait_.def_id()
1669                    && let Some(trait_) = cx.cache().traits.get(&trait_did)
1670                    && trait_.is_notable_trait(cx.tcx())
1671                {
1672                    Some((impl_, trait_did))
1673                } else {
1674                    None
1675                }
1676            })
1677            .peekable();
1678
1679        let has_notable_impl = if let Some((impl_, _)) = notable_impls.peek() {
1680            write!(
1681                f,
1682                "<h3>Notable traits for <code>{}</code></h3>\
1683                <pre><code>",
1684                print_type(&impl_.for_, cx),
1685            )?;
1686            true
1687        } else {
1688            false
1689        };
1690
1691        for (impl_, trait_did) in notable_impls {
1692            write!(f, "<div class=\"where\">{}</div>", print_impl(impl_, false, cx))?;
1693            for it in &impl_.items {
1694                let clean::AssocTypeItem(tydef, ..) = &it.kind else {
1695                    continue;
1696                };
1697
1698                let empty_set = FxIndexSet::default();
1699                let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
1700
1701                write!(
1702                    f,
1703                    "<div class=\"where\">    {};</div>",
1704                    assoc_type(
1705                        it,
1706                        &tydef.generics,
1707                        &[], // intentionally leaving out bounds
1708                        Some(&tydef.type_),
1709                        src_link,
1710                        0,
1711                        cx,
1712                    )
1713                )?;
1714            }
1715        }
1716
1717        if !has_notable_impl {
1718            f.write_str("</code></pre>")?;
1719        }
1720
1721        Ok(())
1722    })
1723    .to_string();
1724
1725    (format!("{:#}", print_type(ty, cx)), out)
1726}
1727
1728fn notable_traits_json<'a>(tys: impl Iterator<Item = &'a clean::Type>, cx: &Context<'_>) -> String {
1729    let mut mp = tys.map(|ty| notable_traits_decl(ty, cx)).collect::<IndexMap<_, _>>();
1730    mp.sort_unstable_keys();
1731    serde_json::to_string(&mp).expect("serialize (string, string) -> json object cannot fail")
1732}
1733
1734#[derive(Clone, Copy, Debug)]
1735struct ImplRenderingParameters {
1736    show_def_docs: bool,
1737    show_default_items: bool,
1738    /// Whether or not to show methods.
1739    show_non_assoc_items: bool,
1740    toggle_open_by_default: bool,
1741}
1742
1743fn render_impl(
1744    cx: &Context<'_>,
1745    i: &Impl,
1746    parent: &clean::Item,
1747    link: AssocItemLink<'_>,
1748    render_mode: RenderMode,
1749    use_absolute: Option<bool>,
1750    aliases: &[String],
1751    rendering_params: ImplRenderingParameters,
1752) -> impl fmt::Display {
1753    fmt::from_fn(move |w| {
1754        let cache = &cx.shared.cache;
1755        let traits = &cache.traits;
1756        let trait_ = i.trait_did().map(|did| &traits[&did]);
1757        let mut close_tags = <Vec<&str>>::with_capacity(2);
1758
1759        // For trait implementations, the `interesting` output contains all methods that have doc
1760        // comments, and the `boring` output contains all methods that do not. The distinction is
1761        // used to allow hiding the boring methods.
1762        // `containing_item` is used for rendering stability info. If the parent is a trait impl,
1763        // `containing_item` will the grandparent, since trait impls can't have stability attached.
1764        fn doc_impl_item(
1765            boring: impl fmt::Write,
1766            interesting: impl fmt::Write,
1767            cx: &Context<'_>,
1768            item: &clean::Item,
1769            parent: &clean::Item,
1770            link: AssocItemLink<'_>,
1771            render_mode: RenderMode,
1772            is_default_item: bool,
1773            trait_: Option<&clean::Trait>,
1774            rendering_params: ImplRenderingParameters,
1775        ) -> fmt::Result {
1776            let item_type = item.type_();
1777            let name = item.name.as_ref().unwrap();
1778
1779            let render_method_item = rendering_params.show_non_assoc_items
1780                && match render_mode {
1781                    RenderMode::Normal => true,
1782                    RenderMode::ForDeref { mut_: deref_mut_ } => {
1783                        should_render_item(item, deref_mut_, cx.tcx())
1784                    }
1785                };
1786
1787            let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
1788
1789            let mut doc_buffer = String::new();
1790            let mut info_buffer = String::new();
1791            let mut short_documented = true;
1792
1793            if render_method_item {
1794                if !is_default_item {
1795                    if let Some(t) = trait_ {
1796                        // The trait item may have been stripped so we might not
1797                        // find any documentation or stability for it.
1798                        if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1799                            // We need the stability of the item from the trait
1800                            // because impls can't have a stability.
1801                            if !item.doc_value().is_empty() {
1802                                document_item_info(cx, it, Some(parent))
1803                                    .render_into(&mut info_buffer)?;
1804                                doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1805                                short_documented = false;
1806                            } else {
1807                                // In case the item isn't documented,
1808                                // provide short documentation from the trait.
1809                                doc_buffer = document_short(
1810                                    it,
1811                                    cx,
1812                                    link,
1813                                    parent,
1814                                    rendering_params.show_def_docs,
1815                                )
1816                                .to_string();
1817                            }
1818                        }
1819                    } else {
1820                        document_item_info(cx, item, Some(parent)).render_into(&mut info_buffer)?;
1821                        if rendering_params.show_def_docs {
1822                            doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1823                            short_documented = false;
1824                        }
1825                    }
1826                } else {
1827                    doc_buffer =
1828                        document_short(item, cx, link, parent, rendering_params.show_def_docs)
1829                            .to_string();
1830                }
1831            }
1832            let mut w = if short_documented && trait_.is_some() {
1833                Either::Left(interesting)
1834            } else {
1835                Either::Right(boring)
1836            };
1837
1838            let toggled = !doc_buffer.is_empty();
1839            if toggled {
1840                let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
1841                write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>")?;
1842            }
1843            match &item.kind {
1844                clean::MethodItem(..) | clean::RequiredMethodItem(_) => {
1845                    // Only render when the method is not static or we allow static methods
1846                    if render_method_item {
1847                        let id = cx.derive_id(format!("{item_type}.{name}"));
1848                        let source_id = trait_
1849                            .and_then(|trait_| {
1850                                trait_
1851                                    .items
1852                                    .iter()
1853                                    .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
1854                            })
1855                            .map(|item| format!("{}.{name}", item.type_()));
1856                        write!(
1857                            w,
1858                            "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1859                                {}",
1860                            render_rightside(cx, item, render_mode)
1861                        )?;
1862                        if trait_.is_some() {
1863                            // Anchors are only used on trait impls.
1864                            write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1865                        }
1866                        write!(
1867                            w,
1868                            "<h4 class=\"code-header\">{}</h4></section>",
1869                            render_assoc_item(
1870                                item,
1871                                link.anchor(source_id.as_ref().unwrap_or(&id)),
1872                                ItemType::Impl,
1873                                cx,
1874                                render_mode,
1875                            ),
1876                        )?;
1877                    }
1878                }
1879                clean::RequiredAssocConstItem(generics, ty) => {
1880                    let source_id = format!("{item_type}.{name}");
1881                    let id = cx.derive_id(&source_id);
1882                    write!(
1883                        w,
1884                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1885                            {}",
1886                        render_rightside(cx, item, render_mode)
1887                    )?;
1888                    if trait_.is_some() {
1889                        // Anchors are only used on trait impls.
1890                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1891                    }
1892                    write!(
1893                        w,
1894                        "<h4 class=\"code-header\">{}</h4></section>",
1895                        assoc_const(
1896                            item,
1897                            generics,
1898                            ty,
1899                            AssocConstValue::None,
1900                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
1901                            0,
1902                            cx,
1903                        ),
1904                    )?;
1905                }
1906                clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
1907                    let source_id = format!("{item_type}.{name}");
1908                    let id = cx.derive_id(&source_id);
1909                    write!(
1910                        w,
1911                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1912                            {}",
1913                        render_rightside(cx, item, render_mode),
1914                    )?;
1915                    if trait_.is_some() {
1916                        // Anchors are only used on trait impls.
1917                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1918                    }
1919                    write!(
1920                        w,
1921                        "<h4 class=\"code-header\">{}</h4></section>",
1922                        assoc_const(
1923                            item,
1924                            &ci.generics,
1925                            &ci.type_,
1926                            match item.kind {
1927                                clean::ProvidedAssocConstItem(_) =>
1928                                    AssocConstValue::TraitDefault(&ci.kind),
1929                                clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
1930                                _ => unreachable!(),
1931                            },
1932                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
1933                            0,
1934                            cx,
1935                        ),
1936                    )?;
1937                }
1938                clean::RequiredAssocTypeItem(generics, bounds) => {
1939                    let source_id = format!("{item_type}.{name}");
1940                    let id = cx.derive_id(&source_id);
1941                    write!(
1942                        w,
1943                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1944                            {}",
1945                        render_rightside(cx, item, render_mode),
1946                    )?;
1947                    if trait_.is_some() {
1948                        // Anchors are only used on trait impls.
1949                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1950                    }
1951                    write!(
1952                        w,
1953                        "<h4 class=\"code-header\">{}</h4></section>",
1954                        assoc_type(
1955                            item,
1956                            generics,
1957                            bounds,
1958                            None,
1959                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
1960                            0,
1961                            cx,
1962                        ),
1963                    )?;
1964                }
1965                clean::AssocTypeItem(tydef, _bounds) => {
1966                    let source_id = format!("{item_type}.{name}");
1967                    let id = cx.derive_id(&source_id);
1968                    write!(
1969                        w,
1970                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1971                            {}",
1972                        render_rightside(cx, item, render_mode),
1973                    )?;
1974                    if trait_.is_some() {
1975                        // Anchors are only used on trait impls.
1976                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1977                    }
1978                    write!(
1979                        w,
1980                        "<h4 class=\"code-header\">{}</h4></section>",
1981                        assoc_type(
1982                            item,
1983                            &tydef.generics,
1984                            &[], // intentionally leaving out bounds
1985                            Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
1986                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
1987                            0,
1988                            cx,
1989                        ),
1990                    )?;
1991                }
1992                clean::StrippedItem(..) => return Ok(()),
1993                _ => panic!("can't make docs for trait item with name {:?}", item.name),
1994            }
1995
1996            w.write_str(&info_buffer)?;
1997            if toggled {
1998                write!(w, "</summary>{doc_buffer}</details>")?;
1999            }
2000            Ok(())
2001        }
2002
2003        let mut impl_items = String::new();
2004        let mut default_impl_items = String::new();
2005        let impl_ = i.inner_impl();
2006
2007        // Impl items are grouped by kinds:
2008        //
2009        // 1. Constants
2010        // 2. Types
2011        // 3. Functions
2012        //
2013        // This order is because you can have associated constants used in associated types (like array
2014        // length), and both in associated functions. So with this order, when reading from top to
2015        // bottom, you should see items definitions before they're actually used most of the time.
2016        let mut assoc_types = Vec::new();
2017        let mut methods = Vec::new();
2018
2019        if !impl_.is_negative_trait_impl() {
2020            for impl_item in &impl_.items {
2021                match impl_item.kind {
2022                    clean::MethodItem(..) | clean::RequiredMethodItem(_) => methods.push(impl_item),
2023                    clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => {
2024                        assoc_types.push(impl_item)
2025                    }
2026                    clean::RequiredAssocConstItem(..)
2027                    | clean::ProvidedAssocConstItem(_)
2028                    | clean::ImplAssocConstItem(_) => {
2029                        // We render it directly since they're supposed to come first.
2030                        doc_impl_item(
2031                            &mut default_impl_items,
2032                            &mut impl_items,
2033                            cx,
2034                            impl_item,
2035                            if trait_.is_some() { &i.impl_item } else { parent },
2036                            link,
2037                            render_mode,
2038                            false,
2039                            trait_,
2040                            rendering_params,
2041                        )?;
2042                    }
2043                    _ => {}
2044                }
2045            }
2046
2047            for assoc_type in assoc_types {
2048                doc_impl_item(
2049                    &mut default_impl_items,
2050                    &mut impl_items,
2051                    cx,
2052                    assoc_type,
2053                    if trait_.is_some() { &i.impl_item } else { parent },
2054                    link,
2055                    render_mode,
2056                    false,
2057                    trait_,
2058                    rendering_params,
2059                )?;
2060            }
2061            for method in methods {
2062                doc_impl_item(
2063                    &mut default_impl_items,
2064                    &mut impl_items,
2065                    cx,
2066                    method,
2067                    if trait_.is_some() { &i.impl_item } else { parent },
2068                    link,
2069                    render_mode,
2070                    false,
2071                    trait_,
2072                    rendering_params,
2073                )?;
2074            }
2075        }
2076
2077        fn render_default_items(
2078            mut boring: impl fmt::Write,
2079            mut interesting: impl fmt::Write,
2080            cx: &Context<'_>,
2081            t: &clean::Trait,
2082            i: &clean::Impl,
2083            parent: &clean::Item,
2084            render_mode: RenderMode,
2085            rendering_params: ImplRenderingParameters,
2086        ) -> fmt::Result {
2087            for trait_item in &t.items {
2088                // Skip over any default trait items that are impossible to reference
2089                // (e.g. if it has a `Self: Sized` bound on an unsized type).
2090                if let Some(impl_def_id) = parent.item_id.as_def_id()
2091                    && let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
2092                    && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id))
2093                {
2094                    continue;
2095                }
2096
2097                let n = trait_item.name;
2098                if i.items.iter().any(|m| m.name == n) {
2099                    continue;
2100                }
2101                let did = i.trait_.as_ref().unwrap().def_id();
2102                let provided_methods = i.provided_trait_methods(cx.tcx());
2103                let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
2104
2105                doc_impl_item(
2106                    &mut boring,
2107                    &mut interesting,
2108                    cx,
2109                    trait_item,
2110                    parent,
2111                    assoc_link,
2112                    render_mode,
2113                    true,
2114                    Some(t),
2115                    rendering_params,
2116                )?;
2117            }
2118            Ok(())
2119        }
2120
2121        // If we've implemented a trait, then also emit documentation for all
2122        // default items which weren't overridden in the implementation block.
2123        // We don't emit documentation for default items if they appear in the
2124        // Implementations on Foreign Types or Implementors sections.
2125        if rendering_params.show_default_items
2126            && let Some(t) = trait_
2127            && !impl_.is_negative_trait_impl()
2128        {
2129            render_default_items(
2130                &mut default_impl_items,
2131                &mut impl_items,
2132                cx,
2133                t,
2134                impl_,
2135                &i.impl_item,
2136                render_mode,
2137                rendering_params,
2138            )?;
2139        }
2140        if render_mode == RenderMode::Normal {
2141            let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
2142            if toggled {
2143                close_tags.push("</details>");
2144                write!(
2145                    w,
2146                    "<details class=\"toggle implementors-toggle\"{}>\
2147                        <summary>",
2148                    if rendering_params.toggle_open_by_default { " open" } else { "" }
2149                )?;
2150            }
2151
2152            let (before_dox, after_dox) = i
2153                .impl_item
2154                .opt_doc_value()
2155                .map(|dox| {
2156                    Markdown {
2157                        content: &dox,
2158                        links: &i.impl_item.links(cx),
2159                        ids: &mut cx.id_map.borrow_mut(),
2160                        error_codes: cx.shared.codes,
2161                        edition: cx.shared.edition(),
2162                        playground: &cx.shared.playground,
2163                        heading_offset: HeadingOffset::H4,
2164                    }
2165                    .split_summary_and_content()
2166                })
2167                .unwrap_or((None, None));
2168
2169            write!(
2170                w,
2171                "{}",
2172                render_impl_summary(
2173                    cx,
2174                    i,
2175                    parent,
2176                    rendering_params.show_def_docs,
2177                    use_absolute,
2178                    aliases,
2179                    before_dox.as_deref(),
2180                    trait_.is_none() && impl_.items.is_empty(),
2181                )
2182            )?;
2183            if toggled {
2184                w.write_str("</summary>")?;
2185            }
2186
2187            if before_dox.is_some()
2188                && let Some(after_dox) = after_dox
2189            {
2190                write!(w, "<div class=\"docblock\">{after_dox}</div>")?;
2191            }
2192
2193            if !default_impl_items.is_empty() || !impl_items.is_empty() {
2194                w.write_str("<div class=\"impl-items\">")?;
2195                close_tags.push("</div>");
2196            }
2197        }
2198        if !default_impl_items.is_empty() || !impl_items.is_empty() {
2199            w.write_str(&default_impl_items)?;
2200            w.write_str(&impl_items)?;
2201        }
2202        for tag in close_tags.into_iter().rev() {
2203            w.write_str(tag)?;
2204        }
2205        Ok(())
2206    })
2207}
2208
2209// Render the items that appear on the right side of methods, impls, and
2210// associated types. For example "1.0.0 (const: 1.39.0) · source".
2211fn render_rightside(
2212    cx: &Context<'_>,
2213    item: &clean::Item,
2214    render_mode: RenderMode,
2215) -> impl fmt::Display {
2216    let tcx = cx.tcx();
2217
2218    fmt::from_fn(move |w| {
2219        // FIXME: Once https://github.com/rust-lang/rust/issues/143874 is implemented, we can remove
2220        // this condition.
2221        let const_stability = match render_mode {
2222            RenderMode::Normal => item.const_stability(tcx),
2223            RenderMode::ForDeref { .. } => None,
2224        };
2225        let src_href = cx.src_href(item);
2226        let stability = render_stability_since_raw_with_extra(
2227            item.stable_since(tcx),
2228            const_stability,
2229            if src_href.is_some() { "" } else { " rightside" },
2230        );
2231
2232        match (stability, src_href) {
2233            (Some(stability), Some(link)) => {
2234                write!(
2235                    w,
2236                    "<span class=\"rightside\">{stability} · <a class=\"src\" href=\"{link}\">Source</a></span>",
2237                )
2238            }
2239            (Some(stability), None) => {
2240                write!(w, "{stability}")
2241            }
2242            (None, Some(link)) => {
2243                write!(w, "<a class=\"src rightside\" href=\"{link}\">Source</a>")
2244            }
2245            (None, None) => Ok(()),
2246        }
2247    })
2248}
2249
2250fn render_impl_summary(
2251    cx: &Context<'_>,
2252    i: &Impl,
2253    parent: &clean::Item,
2254    show_def_docs: bool,
2255    use_absolute: Option<bool>,
2256    // This argument is used to reference same type with different paths to avoid duplication
2257    // in documentation pages for trait with automatic implementations like "Send" and "Sync".
2258    aliases: &[String],
2259    doc: Option<&str>,
2260    impl_is_empty: bool,
2261) -> impl fmt::Display {
2262    fmt::from_fn(move |w| {
2263        let inner_impl = i.inner_impl();
2264        let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
2265        let aliases = (!aliases.is_empty())
2266            .then_some(fmt::from_fn(|f| {
2267                write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f)))
2268            }))
2269            .maybe_display();
2270        write!(
2271            w,
2272            "<section id=\"{id}\" class=\"impl\"{aliases}>\
2273                {}\
2274                <a href=\"#{id}\" class=\"anchor\">§</a>\
2275                <h3 class=\"code-header\">",
2276            render_rightside(cx, &i.impl_item, RenderMode::Normal)
2277        )?;
2278
2279        if let Some(use_absolute) = use_absolute {
2280            write!(w, "{}", print_impl(inner_impl, use_absolute, cx))?;
2281            if show_def_docs {
2282                for it in &inner_impl.items {
2283                    if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
2284                        write!(
2285                            w,
2286                            "<div class=\"where\">  {};</div>",
2287                            assoc_type(
2288                                it,
2289                                &tydef.generics,
2290                                &[], // intentionally leaving out bounds
2291                                Some(&tydef.type_),
2292                                AssocItemLink::Anchor(None),
2293                                0,
2294                                cx,
2295                            )
2296                        )?;
2297                    }
2298                }
2299            }
2300        } else {
2301            write!(w, "{}", print_impl(inner_impl, false, cx))?;
2302        }
2303        w.write_str("</h3>")?;
2304
2305        let is_trait = inner_impl.trait_.is_some();
2306        if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) {
2307            write!(
2308                w,
2309                "<span class=\"item-info\">\
2310                    <div class=\"stab portability\">{portability}</div>\
2311                </span>",
2312            )?;
2313        }
2314
2315        if let Some(doc) = doc {
2316            if impl_is_empty {
2317                w.write_str(
2318                    "<div class=\"item-info\">\
2319                         <div class=\"stab empty-impl\">This impl block contains no items.</div>\
2320                     </div>",
2321                )?;
2322            }
2323            write!(w, "<div class=\"docblock\">{doc}</div>")?;
2324        }
2325
2326        w.write_str("</section>")
2327    })
2328}
2329
2330pub(crate) fn small_url_encode(s: String) -> String {
2331    // These characters don't need to be escaped in a URI.
2332    // See https://url.spec.whatwg.org/#query-percent-encode-set
2333    // and https://url.spec.whatwg.org/#urlencoded-parsing
2334    // and https://url.spec.whatwg.org/#url-code-points
2335    fn dont_escape(c: u8) -> bool {
2336        c.is_ascii_alphanumeric()
2337            || c == b'-'
2338            || c == b'_'
2339            || c == b'.'
2340            || c == b','
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            // As described in urlencoded-parsing, the
2352            // first `=` is the one that separates key from
2353            // value. Following `=`s are part of the value.
2354            || c == b'='
2355    }
2356    let mut st = String::new();
2357    let mut last_match = 0;
2358    for (idx, b) in s.bytes().enumerate() {
2359        if dont_escape(b) {
2360            continue;
2361        }
2362
2363        if last_match != idx {
2364            // Invariant: `idx` must be the first byte in a character at this point.
2365            st += &s[last_match..idx];
2366        }
2367        if b == b' ' {
2368            // URL queries are decoded with + replaced with SP.
2369            // While the same is not true for hashes, rustdoc only needs to be
2370            // consistent with itself when encoding them.
2371            st += "+";
2372        } else {
2373            write!(st, "%{b:02X}").unwrap();
2374        }
2375        // Invariant: if the current byte is not at the start of a multi-byte character,
2376        // we need to get down here so that when the next turn of the loop comes around,
2377        // last_match winds up equalling idx.
2378        //
2379        // In other words, dont_escape must always return `false` in multi-byte character.
2380        last_match = idx + 1;
2381    }
2382
2383    if last_match != 0 {
2384        st += &s[last_match..];
2385        st
2386    } else {
2387        s
2388    }
2389}
2390
2391fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String {
2392    use rustc_middle::ty::print::with_forced_trimmed_paths;
2393    let (type_, trait_) = match impl_id {
2394        ItemId::Auto { trait_, for_ } => {
2395            let ty = tcx.type_of(for_).skip_binder();
2396            (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
2397        }
2398        ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
2399            if let Some(trait_ref) = tcx.impl_opt_trait_ref(impl_id) {
2400                let trait_ref = trait_ref.skip_binder();
2401                (trait_ref.self_ty(), Some(trait_ref))
2402            } else {
2403                (tcx.type_of(impl_id).skip_binder(), None)
2404            }
2405        }
2406    };
2407    with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
2408        format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
2409    } else {
2410        format!("impl-{type_}")
2411    }))
2412}
2413
2414fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2415    match item.kind {
2416        clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
2417            // Alternative format produces no URLs,
2418            // so this parameter does nothing.
2419            Some((
2420                format!("{:#}", print_type(&i.for_, cx)),
2421                get_id_for_impl(cx.tcx(), item.item_id),
2422            ))
2423        }
2424        _ => None,
2425    }
2426}
2427
2428/// Returns the list of implementations for the primitive reference type, filtering out any
2429/// implementations that are on concrete or partially generic types, only keeping implementations
2430/// of the form `impl<T> Trait for &T`.
2431pub(crate) fn get_filtered_impls_for_reference<'a>(
2432    shared: &'a SharedContext<'_>,
2433    it: &clean::Item,
2434) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
2435    let def_id = it.item_id.expect_def_id();
2436    // If the reference primitive is somehow not defined, exit early.
2437    let Some(v) = shared.cache.impls.get(&def_id) else {
2438        return (Vec::new(), Vec::new(), Vec::new());
2439    };
2440    // Since there is no "direct implementation" on the reference primitive type, we filter out
2441    // every implementation which isn't a trait implementation.
2442    let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
2443    let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
2444        traits.partition(|t| t.inner_impl().kind.is_auto());
2445
2446    let (blanket_impl, concrete): (Vec<&Impl>, _) =
2447        concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
2448    // Now we keep only references over full generic types.
2449    let concrete: Vec<_> = concrete
2450        .into_iter()
2451        .filter(|t| match t.inner_impl().for_ {
2452            clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
2453            _ => false,
2454        })
2455        .collect();
2456
2457    (concrete, synthetic, blanket_impl)
2458}
2459
2460#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2461pub(crate) enum ItemSection {
2462    Reexports,
2463    PrimitiveTypes,
2464    Modules,
2465    Macros,
2466    Structs,
2467    Enums,
2468    Constants,
2469    Statics,
2470    Traits,
2471    Functions,
2472    TypeAliases,
2473    Unions,
2474    Implementations,
2475    TypeMethods,
2476    Methods,
2477    StructFields,
2478    Variants,
2479    AssociatedTypes,
2480    AssociatedConstants,
2481    ForeignTypes,
2482    Keywords,
2483    Attributes,
2484    AttributeMacros,
2485    DeriveMacros,
2486    TraitAliases,
2487}
2488
2489impl ItemSection {
2490    const ALL: &'static [Self] = {
2491        use ItemSection::*;
2492        // NOTE: The order here affects the order in the UI.
2493        // Keep this synchronized with addSidebarItems in main.js
2494        &[
2495            Reexports,
2496            PrimitiveTypes,
2497            Modules,
2498            Macros,
2499            Structs,
2500            Enums,
2501            Constants,
2502            Statics,
2503            Traits,
2504            Functions,
2505            TypeAliases,
2506            Unions,
2507            Implementations,
2508            TypeMethods,
2509            Methods,
2510            StructFields,
2511            Variants,
2512            AssociatedTypes,
2513            AssociatedConstants,
2514            ForeignTypes,
2515            Keywords,
2516            Attributes,
2517            AttributeMacros,
2518            DeriveMacros,
2519            TraitAliases,
2520        ]
2521    };
2522
2523    fn id(self) -> &'static str {
2524        match self {
2525            Self::Reexports => "reexports",
2526            Self::Modules => "modules",
2527            Self::Structs => "structs",
2528            Self::Unions => "unions",
2529            Self::Enums => "enums",
2530            Self::Functions => "functions",
2531            Self::TypeAliases => "types",
2532            Self::Statics => "statics",
2533            Self::Constants => "constants",
2534            Self::Traits => "traits",
2535            Self::Implementations => "impls",
2536            Self::TypeMethods => "tymethods",
2537            Self::Methods => "methods",
2538            Self::StructFields => "fields",
2539            Self::Variants => "variants",
2540            Self::Macros => "macros",
2541            Self::PrimitiveTypes => "primitives",
2542            Self::AssociatedTypes => "associated-types",
2543            Self::AssociatedConstants => "associated-consts",
2544            Self::ForeignTypes => "foreign-types",
2545            Self::Keywords => "keywords",
2546            Self::Attributes => "attributes",
2547            Self::AttributeMacros => "attributes",
2548            Self::DeriveMacros => "derives",
2549            Self::TraitAliases => "trait-aliases",
2550        }
2551    }
2552
2553    fn name(self) -> &'static str {
2554        match self {
2555            Self::Reexports => "Re-exports",
2556            Self::Modules => "Modules",
2557            Self::Structs => "Structs",
2558            Self::Unions => "Unions",
2559            Self::Enums => "Enums",
2560            Self::Functions => "Functions",
2561            Self::TypeAliases => "Type Aliases",
2562            Self::Statics => "Statics",
2563            Self::Constants => "Constants",
2564            Self::Traits => "Traits",
2565            Self::Implementations => "Implementations",
2566            Self::TypeMethods => "Type Methods",
2567            Self::Methods => "Methods",
2568            Self::StructFields => "Struct Fields",
2569            Self::Variants => "Variants",
2570            Self::Macros => "Macros",
2571            Self::PrimitiveTypes => "Primitive Types",
2572            Self::AssociatedTypes => "Associated Types",
2573            Self::AssociatedConstants => "Associated Constants",
2574            Self::ForeignTypes => "Foreign Types",
2575            Self::Keywords => "Keywords",
2576            Self::Attributes => "Attributes",
2577            Self::AttributeMacros => "Attribute Macros",
2578            Self::DeriveMacros => "Derive Macros",
2579            Self::TraitAliases => "Trait Aliases",
2580        }
2581    }
2582}
2583
2584fn item_ty_to_section(ty: ItemType) -> ItemSection {
2585    match ty {
2586        ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
2587        ItemType::Module => ItemSection::Modules,
2588        ItemType::Struct => ItemSection::Structs,
2589        ItemType::Union => ItemSection::Unions,
2590        ItemType::Enum => ItemSection::Enums,
2591        ItemType::Function => ItemSection::Functions,
2592        ItemType::TypeAlias => ItemSection::TypeAliases,
2593        ItemType::Static => ItemSection::Statics,
2594        ItemType::Constant => ItemSection::Constants,
2595        ItemType::Trait => ItemSection::Traits,
2596        ItemType::Impl => ItemSection::Implementations,
2597        ItemType::TyMethod => ItemSection::TypeMethods,
2598        ItemType::Method => ItemSection::Methods,
2599        ItemType::StructField => ItemSection::StructFields,
2600        ItemType::Variant => ItemSection::Variants,
2601        ItemType::Macro => ItemSection::Macros,
2602        ItemType::Primitive => ItemSection::PrimitiveTypes,
2603        ItemType::AssocType => ItemSection::AssociatedTypes,
2604        ItemType::AssocConst => ItemSection::AssociatedConstants,
2605        ItemType::ForeignType => ItemSection::ForeignTypes,
2606        ItemType::Keyword => ItemSection::Keywords,
2607        ItemType::Attribute => ItemSection::Attributes,
2608        ItemType::ProcAttribute => ItemSection::AttributeMacros,
2609        ItemType::ProcDerive => ItemSection::DeriveMacros,
2610        ItemType::TraitAlias => ItemSection::TraitAliases,
2611    }
2612}
2613
2614/// Returns a list of all paths used in the type.
2615/// This is used to help deduplicate imported impls
2616/// for reexported types. If any of the contained
2617/// types are re-exported, we don't use the corresponding
2618/// entry from the js file, as inlining will have already
2619/// picked up the impl
2620fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec<String> {
2621    let mut out = Vec::new();
2622    let mut visited = FxHashSet::default();
2623    let mut work = VecDeque::new();
2624
2625    let mut process_path = |did: DefId| {
2626        let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
2627        let fqp = cache.exact_paths.get(&did).or_else(get_extern);
2628
2629        if let Some(path) = fqp {
2630            out.push(join_path_syms(path));
2631        }
2632    };
2633
2634    work.push_back(first_ty);
2635
2636    while let Some(ty) = work.pop_front() {
2637        if !visited.insert(ty) {
2638            continue;
2639        }
2640
2641        match ty {
2642            clean::Type::Path { path } => process_path(path.def_id()),
2643            clean::Type::Tuple(tys) => {
2644                work.extend(tys.iter());
2645            }
2646            clean::Type::Slice(ty) => {
2647                work.push_back(ty);
2648            }
2649            clean::Type::Array(ty, _) => {
2650                work.push_back(ty);
2651            }
2652            clean::Type::RawPointer(_, ty) => {
2653                work.push_back(ty);
2654            }
2655            clean::Type::BorrowedRef { type_, .. } => {
2656                work.push_back(type_);
2657            }
2658            clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
2659                work.push_back(self_type);
2660                if let Some(trait_) = trait_ {
2661                    process_path(trait_.def_id());
2662                }
2663            }
2664            _ => {}
2665        }
2666    }
2667    out
2668}
2669
2670const MAX_FULL_EXAMPLES: usize = 5;
2671const NUM_VISIBLE_LINES: usize = 10;
2672
2673/// Generates the HTML for example call locations generated via the --scrape-examples flag.
2674fn render_call_locations<W: fmt::Write>(
2675    mut w: W,
2676    cx: &Context<'_>,
2677    item: &clean::Item,
2678) -> fmt::Result {
2679    let tcx = cx.tcx();
2680    let def_id = item.item_id.expect_def_id();
2681    let key = tcx.def_path_hash(def_id);
2682    let Some(call_locations) = cx.shared.call_locations.get(&key) else { return Ok(()) };
2683
2684    // Generate a unique ID so users can link to this section for a given method
2685    let id = cx.derive_id("scraped-examples");
2686    write!(
2687        &mut w,
2688        "<div class=\"docblock scraped-example-list\">\
2689          <span></span>\
2690          <h5 id=\"{id}\">\
2691             <a href=\"#{id}\">Examples found in repository</a>\
2692             <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
2693          </h5>",
2694        root_path = cx.root_path(),
2695        id = id
2696    )?;
2697
2698    // Create a URL to a particular location in a reverse-dependency's source file
2699    let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
2700        let (line_lo, line_hi) = loc.call_expr.line_span;
2701        let (anchor, title) = if line_lo == line_hi {
2702            ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
2703        } else {
2704            (
2705                format!("{}-{}", line_lo + 1, line_hi + 1),
2706                format!("lines {}-{}", line_lo + 1, line_hi + 1),
2707            )
2708        };
2709        let url = format!("{}{}#{anchor}", cx.root_path(), call_data.url);
2710        (url, title)
2711    };
2712
2713    // Generate the HTML for a single example, being the title and code block
2714    let write_example = |w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
2715        let contents = match fs::read_to_string(path) {
2716            Ok(contents) => contents,
2717            Err(err) => {
2718                let span = item.span(tcx).map_or(DUMMY_SP, |span| span.inner());
2719                tcx.dcx().span_err(span, format!("failed to read file {}: {err}", path.display()));
2720                return false;
2721            }
2722        };
2723
2724        // To reduce file sizes, we only want to embed the source code needed to understand the example, not
2725        // the entire file. So we find the smallest byte range that covers all items enclosing examples.
2726        assert!(!call_data.locations.is_empty());
2727        let min_loc =
2728            call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2729        let byte_min = min_loc.enclosing_item.byte_span.0;
2730        let line_min = min_loc.enclosing_item.line_span.0;
2731        let max_loc =
2732            call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
2733        let byte_max = max_loc.enclosing_item.byte_span.1;
2734        let line_max = max_loc.enclosing_item.line_span.1;
2735
2736        // The output code is limited to that byte range.
2737        let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
2738
2739        // The call locations need to be updated to reflect that the size of the program has changed.
2740        // Specifically, the ranges are all subtracted by `byte_min` since that's the new zero point.
2741        let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
2742            .locations
2743            .iter()
2744            .map(|loc| {
2745                let (byte_lo, byte_hi) = loc.call_ident.byte_span;
2746                let (line_lo, line_hi) = loc.call_expr.line_span;
2747                let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
2748
2749                let line_range = (line_lo - line_min, line_hi - line_min);
2750                let (line_url, line_title) = link_to_loc(call_data, loc);
2751
2752                (byte_range, (line_range, line_url, line_title))
2753            })
2754            .unzip();
2755
2756        let (_, init_url, init_title) = &line_ranges[0];
2757        let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
2758        let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
2759
2760        let source_map = tcx.sess.source_map();
2761        let files = source_map.files();
2762        let local = tcx.sess.local_crate_source_file().unwrap();
2763
2764        let get_file_start_pos = || {
2765            let crate_src = local.clone().into_local_path()?;
2766            let abs_crate_src = crate_src.canonicalize().ok()?;
2767            let crate_root = abs_crate_src.parent()?.parent()?;
2768            let rel_path = path.strip_prefix(crate_root).ok()?;
2769            files
2770                .iter()
2771                .find(|file| match &file.name {
2772                    FileName::Real(RealFileName::LocalPath(other_path)) => rel_path == other_path,
2773                    _ => false,
2774                })
2775                .map(|file| file.start_pos)
2776        };
2777
2778        // Look for the example file in the source map if it exists, otherwise
2779        // return a span to the local crate's source file
2780        let Some(file_span) = get_file_start_pos()
2781            .or_else(|| {
2782                files
2783                    .iter()
2784                    .find(|file| match &file.name {
2785                        FileName::Real(file_name) => file_name == &local,
2786                        _ => false,
2787                    })
2788                    .map(|file| file.start_pos)
2789            })
2790            .map(|start_pos| {
2791                rustc_span::Span::with_root_ctxt(
2792                    start_pos + BytePos(byte_min),
2793                    start_pos + BytePos(byte_max),
2794                )
2795            })
2796        else {
2797            // if the fallback span can't be built, don't render the code for this example
2798            return false;
2799        };
2800
2801        let mut decoration_info = FxIndexMap::default();
2802        decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
2803        decoration_info.insert("highlight", byte_ranges);
2804
2805        sources::print_src(
2806            w,
2807            contents_subset,
2808            file_span,
2809            cx,
2810            &cx.root_path(),
2811            &highlight::DecorationInfo(decoration_info),
2812            &sources::SourceContext::Embedded(sources::ScrapedInfo {
2813                needs_expansion,
2814                offset: line_min,
2815                name: &call_data.display_name,
2816                url: init_url,
2817                title: init_title,
2818                locations: locations_encoded,
2819            }),
2820        )
2821        .unwrap();
2822
2823        true
2824    };
2825
2826    // The call locations are output in sequence, so that sequence needs to be determined.
2827    // Ideally the most "relevant" examples would be shown first, but there's no general algorithm
2828    // for determining relevance. We instead proxy relevance with the following heuristics:
2829    //   1. Code written to be an example is better than code not written to be an example, e.g.
2830    //      a snippet from examples/foo.rs is better than src/lib.rs. We don't know the Cargo
2831    //      directory structure in Rustdoc, so we proxy this by prioritizing code that comes from
2832    //      a --crate-type bin.
2833    //   2. Smaller examples are better than large examples. So we prioritize snippets that have
2834    //      the smallest number of lines in their enclosing item.
2835    //   3. Finally we sort by the displayed file name, which is arbitrary but prevents the
2836    //      ordering of examples from randomly changing between Rustdoc invocations.
2837    let ordered_locations = {
2838        fn sort_criterion<'a>(
2839            (_, call_data): &(&PathBuf, &'a CallData),
2840        ) -> (bool, u32, &'a String) {
2841            // Use the first location because that's what the user will see initially
2842            let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2843            (!call_data.is_bin, hi - lo, &call_data.display_name)
2844        }
2845
2846        let mut locs = call_locations.iter().collect::<Vec<_>>();
2847        locs.sort_by_key(sort_criterion);
2848        locs
2849    };
2850
2851    let mut it = ordered_locations.into_iter().peekable();
2852
2853    // An example may fail to write if its source can't be read for some reason, so this method
2854    // continues iterating until a write succeeds
2855    let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
2856        for example in it.by_ref() {
2857            if write_example(&mut *w, example) {
2858                break;
2859            }
2860        }
2861    };
2862
2863    // Write just one example that's visible by default in the method's description.
2864    write_and_skip_failure(&mut w, &mut it);
2865
2866    // Then add the remaining examples in a hidden section.
2867    if it.peek().is_some() {
2868        write!(
2869            w,
2870            "<details class=\"toggle more-examples-toggle\">\
2871                  <summary class=\"hideme\">\
2872                     <span>More examples</span>\
2873                  </summary>\
2874                  <div class=\"hide-more\">Hide additional examples</div>\
2875                  <div class=\"more-scraped-examples\">\
2876                    <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
2877        )?;
2878
2879        // Only generate inline code for MAX_FULL_EXAMPLES number of examples. Otherwise we could
2880        // make the page arbitrarily huge!
2881        for _ in 0..MAX_FULL_EXAMPLES {
2882            write_and_skip_failure(&mut w, &mut it);
2883        }
2884
2885        // For the remaining examples, generate a <ul> containing links to the source files.
2886        if it.peek().is_some() {
2887            w.write_str(
2888                r#"<div class="example-links">Additional examples can be found in:<br><ul>"#,
2889            )?;
2890            it.try_for_each(|(_, call_data)| {
2891                let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
2892                write!(
2893                    w,
2894                    r#"<li><a href="{url}">{name}</a></li>"#,
2895                    url = url,
2896                    name = call_data.display_name
2897                )
2898            })?;
2899            w.write_str("</ul></div>")?;
2900        }
2901
2902        w.write_str("</div></details>")?;
2903    }
2904
2905    w.write_str("</div>")
2906}
2907
2908fn render_attributes_in_code(
2909    w: &mut impl fmt::Write,
2910    item: &clean::Item,
2911    prefix: &str,
2912    cx: &Context<'_>,
2913) -> fmt::Result {
2914    for attr in &item.attrs.other_attrs {
2915        let hir::Attribute::Parsed(kind) = attr else { continue };
2916        let attr = match kind {
2917            AttributeKind::LinkSection { name, .. } => {
2918                Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}"))))
2919            }
2920            AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"),
2921            AttributeKind::ExportName { name, .. } => {
2922                Cow::Owned(format!("#[unsafe(export_name = {})]", Escape(&format!("{name:?}"))))
2923            }
2924            AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"),
2925            _ => continue,
2926        };
2927        render_code_attribute(prefix, attr.as_ref(), w)?;
2928    }
2929
2930    if let Some(def_id) = item.def_id()
2931        && let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id)
2932    {
2933        render_code_attribute(prefix, &repr, w)?;
2934    }
2935    Ok(())
2936}
2937
2938fn render_repr_attribute_in_code(
2939    w: &mut impl fmt::Write,
2940    cx: &Context<'_>,
2941    def_id: DefId,
2942) -> fmt::Result {
2943    if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) {
2944        render_code_attribute("", &repr, w)?;
2945    }
2946    Ok(())
2947}
2948
2949fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) -> fmt::Result {
2950    write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>")
2951}
2952
2953/// Compute the *public* `#[repr]` of the item given by `DefId`.
2954///
2955/// Read more about it here:
2956/// <https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type>.
2957fn repr_attribute<'tcx>(
2958    tcx: TyCtxt<'tcx>,
2959    cache: &Cache,
2960    def_id: DefId,
2961) -> Option<Cow<'static, str>> {
2962    let adt = match tcx.def_kind(def_id) {
2963        DefKind::Struct | DefKind::Enum | DefKind::Union => tcx.adt_def(def_id),
2964        _ => return None,
2965    };
2966    let repr = adt.repr();
2967
2968    let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id);
2969    let is_public_field = |field: &ty::FieldDef| {
2970        (cache.document_private || field.vis.is_public()) && is_visible(field.did)
2971    };
2972
2973    if repr.transparent() {
2974        // The transparent repr is public iff the non-1-ZST field is public and visible or
2975        // – in case all fields are 1-ZST fields — at least one field is public and visible.
2976        let is_public = 'is_public: {
2977            // `#[repr(transparent)]` can only be applied to structs and single-variant enums.
2978            let var = adt.variant(rustc_abi::FIRST_VARIANT); // the first and only variant
2979
2980            if !is_visible(var.def_id) {
2981                break 'is_public false;
2982            }
2983
2984            // Side note: There can only ever be one or zero non-1-ZST fields.
2985            let non_1zst_field = var.fields.iter().find(|field| {
2986                let ty = ty::TypingEnv::post_analysis(tcx, field.did)
2987                    .as_query_input(tcx.type_of(field.did).instantiate_identity());
2988                tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst())
2989            });
2990
2991            match non_1zst_field {
2992                Some(field) => is_public_field(field),
2993                None => var.fields.is_empty() || var.fields.iter().any(is_public_field),
2994            }
2995        };
2996
2997        // Since the transparent repr can't have any other reprs or
2998        // repr modifiers beside it, we can safely return early here.
2999        return is_public.then(|| "#[repr(transparent)]".into());
3000    }
3001
3002    // Fast path which avoids looking through the variants and fields in
3003    // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
3004    // FIXME: This check is not very robust / forward compatible!
3005    if !repr.c()
3006        && !repr.simd()
3007        && repr.int.is_none()
3008        && repr.pack.is_none()
3009        && repr.align.is_none()
3010    {
3011        return None;
3012    }
3013
3014    // The repr is public iff all components are public and visible.
3015    let is_public = adt
3016        .variants()
3017        .iter()
3018        .all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field));
3019    if !is_public {
3020        return None;
3021    }
3022
3023    let mut result = Vec::<Cow<'_, _>>::new();
3024
3025    if repr.c() {
3026        result.push("C".into());
3027    }
3028    if repr.simd() {
3029        result.push("simd".into());
3030    }
3031    if let Some(int) = repr.int {
3032        let prefix = if int.is_signed() { 'i' } else { 'u' };
3033        let int = match int {
3034            rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"),
3035            rustc_abi::IntegerType::Fixed(int, _) => {
3036                format!("{prefix}{}", int.size().bytes() * 8)
3037            }
3038        };
3039        result.push(int.into());
3040    }
3041
3042    // Render modifiers last.
3043    if let Some(pack) = repr.pack {
3044        result.push(format!("packed({})", pack.bytes()).into());
3045    }
3046    if let Some(align) = repr.align {
3047        result.push(format!("align({})", align.bytes()).into());
3048    }
3049
3050    (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into())
3051}