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