Skip to main content

rustdoc/html/render/
mod.rs

1//! Rustdoc's HTML rendering module.
2//!
3//! This modules contains the bulk of the logic necessary for rendering a
4//! rustdoc `clean::Crate` instance to a set of static HTML pages. This
5//! rendering process is largely driven by the `format!` syntax extension to
6//! perform all I/O into files and streams.
7//!
8//! The rendering process is largely driven by the `Context` and `Cache`
9//! structures. The cache is pre-populated by crawling the crate in question,
10//! and then it is shared among the various rendering threads. The cache is meant
11//! to be a fairly large structure not implementing `Clone` (because it's shared
12//! among threads). The context, however, should be a lightweight structure. This
13//! is cloned per-thread and contains information about what is currently being
14//! rendered.
15//!
16//! The main entry point to the rendering system is the implementation of
17//! `FormatRenderer` on `Context`.
18//!
19//! In order to speed up rendering (mostly because of markdown rendering), the
20//! rendering process has been parallelized. This parallelization is only
21//! exposed through the `crate` method on the context, and then also from the
22//! fact that the shared cache is stored in TLS (and must be accessed as such).
23//!
24//! In addition to rendering the crate itself, this module is also responsible
25//! for creating the corresponding search index and source file renderings.
26//! These threads are not parallelized (they haven't been a bottleneck yet), and
27//! both occur before the crate is rendered.
28
29pub(crate) mod search_index;
30
31#[cfg(test)]
32mod tests;
33
34mod context;
35mod ordered_json;
36mod print_item;
37pub(crate) mod sidebar;
38mod sorted_template;
39pub(crate) mod span_map;
40mod type_layout;
41mod write_shared;
42
43use std::borrow::Cow;
44use std::collections::VecDeque;
45use std::fmt::{self, Display as _, Write};
46use std::iter::Peekable;
47use std::path::PathBuf;
48use std::{fs, str};
49
50use askama::Template;
51use indexmap::IndexMap;
52use itertools::Either;
53use rustc_ast::join_path_syms;
54use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
55use rustc_hir as hir;
56use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation};
57use rustc_hir::def::DefKind;
58use rustc_hir::def_id::{DefId, DefIdSet};
59use rustc_hir::{ConstStability, Mutability, RustcVersion, StabilityLevel, StableSince};
60use rustc_middle::ty::print::PrintTraitRefExt;
61use rustc_middle::ty::{self, TyCtxt};
62use rustc_span::symbol::{Symbol, sym};
63use rustc_span::{BytePos, DUMMY_SP, FileName};
64use tracing::{debug, info};
65
66pub(crate) use self::context::*;
67pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources};
68pub(crate) use self::write_shared::*;
69use crate::clean::{self, ItemId, RenderedLink};
70use crate::display::{Joined as _, MaybeDisplay as _};
71use crate::error::Error;
72use crate::formats::Impl;
73use crate::formats::cache::Cache;
74use crate::formats::item_type::ItemType;
75use crate::html::escape::Escape;
76use crate::html::format::{
77    Ending, HrefError, HrefInfo, PrintWithSpace, full_print_fn_decl, href, print_abi_with_space,
78    print_constness_with_space, print_default_space, print_generic_bounds, print_generics,
79    print_impl, print_path, print_type, print_where_clause, visibility_print_with_space,
80};
81use crate::html::markdown::{
82    HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine,
83};
84use crate::html::static_files::SCRAPE_EXAMPLES_HELP_MD;
85use crate::html::{highlight, sources};
86use crate::scrape_examples::{CallData, CallLocation};
87use crate::{DOC_RUST_LANG_ORG_VERSION, try_none};
88
89pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display {
90    fmt::from_fn(move |f| {
91        if !v.ends_with('/') && !v.is_empty() { write!(f, "{v}/") } else { f.write_str(v) }
92    })
93}
94
95/// Specifies whether rendering directly implemented trait items or ones from a certain Deref
96/// impl.
97#[derive(Copy, Clone, Debug)]
98enum AssocItemRender<'a> {
99    All,
100    DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool },
101}
102
103impl AssocItemRender<'_> {
104    fn render_mode(&self) -> RenderMode {
105        match self {
106            Self::All => RenderMode::Normal,
107            &Self::DerefFor { deref_mut_, .. } => RenderMode::ForDeref { mut_: deref_mut_ },
108        }
109    }
110
111    fn class(&self) -> Option<&'static str> {
112        if let Self::DerefFor { .. } = self { Some("impl-items") } else { None }
113    }
114}
115
116/// For different handling of associated items from the Deref target of a type rather than the type
117/// itself.
118#[derive(Copy, Clone, PartialEq)]
119enum RenderMode {
120    Normal,
121    ForDeref { mut_: bool },
122}
123
124// Helper structs for rendering items/sidebars and carrying along contextual
125// information
126
127/// Struct representing one entry in the JS search index. These are all emitted
128/// by hand to a large JS file at the end of cache-creation.
129#[derive(Debug)]
130pub(crate) struct IndexItem {
131    pub(crate) ty: ItemType,
132    pub(crate) defid: Option<DefId>,
133    pub(crate) name: Symbol,
134    pub(crate) module_path: Vec<Symbol>,
135    pub(crate) desc: String,
136    pub(crate) parent: Option<DefId>,
137    pub(crate) parent_idx: Option<usize>,
138    pub(crate) trait_parent: Option<DefId>,
139    pub(crate) trait_parent_idx: Option<usize>,
140    pub(crate) exact_module_path: Option<Vec<Symbol>>,
141    pub(crate) impl_id: Option<DefId>,
142    pub(crate) search_type: Option<IndexItemFunctionType>,
143    pub(crate) aliases: Box<[Symbol]>,
144    pub(crate) is_deprecated: bool,
145    pub(crate) is_unstable: bool,
146}
147
148/// A type used for the search index.
149#[derive(Clone, Debug, Eq, PartialEq)]
150struct RenderType {
151    id: Option<RenderTypeId>,
152    generics: Option<Vec<RenderType>>,
153    bindings: Option<Vec<(RenderTypeId, Vec<RenderType>)>>,
154}
155
156impl RenderType {
157    fn size(&self) -> usize {
158        let mut size = 1;
159        if let Some(generics) = &self.generics {
160            size += generics.iter().map(RenderType::size).sum::<usize>();
161        }
162        if let Some(bindings) = &self.bindings {
163            for (_, constraints) in bindings.iter() {
164                size += 1;
165                size += constraints.iter().map(RenderType::size).sum::<usize>();
166            }
167        }
168        size
169    }
170    // Types are rendered as lists of lists, because that's pretty compact.
171    // The contents of the lists are always integers in self-terminating hex
172    // form, handled by `RenderTypeId::write_to_string`, so no commas are
173    // needed to separate the items.
174    fn write_to_string(&self, string: &mut String) {
175        fn write_optional_id(id: Option<RenderTypeId>, string: &mut String) {
176            // 0 is a sentinel, everything else is one-indexed
177            match id {
178                Some(id) => id.write_to_string(string),
179                None => string.push('`'),
180            }
181        }
182        // Either just the type id, or `{type, generics, bindings?}`
183        // where generics is a list of types,
184        // and bindings is a list of `{id, typelist}` pairs.
185        if self.generics.is_some() || self.bindings.is_some() {
186            string.push('{');
187            write_optional_id(self.id, string);
188            string.push('{');
189            for generic in self.generics.as_deref().unwrap_or_default() {
190                generic.write_to_string(string);
191            }
192            string.push('}');
193            if self.bindings.is_some() {
194                string.push('{');
195                for binding in self.bindings.as_deref().unwrap_or_default() {
196                    string.push('{');
197                    binding.0.write_to_string(string);
198                    string.push('{');
199                    for constraint in &binding.1[..] {
200                        constraint.write_to_string(string);
201                    }
202                    string.push_str("}}");
203                }
204                string.push('}');
205            }
206            string.push('}');
207        } else {
208            write_optional_id(self.id, string);
209        }
210    }
211    fn read_from_bytes(string: &[u8]) -> (RenderType, usize) {
212        let mut i = 0;
213        if string[i] == b'{' {
214            i += 1;
215            let (id, offset) = RenderTypeId::read_from_bytes(&string[i..]);
216            i += offset;
217            let generics = if string[i] == b'{' {
218                i += 1;
219                let mut generics = Vec::new();
220                while string[i] != b'}' {
221                    let (ty, offset) = RenderType::read_from_bytes(&string[i..]);
222                    i += offset;
223                    generics.push(ty);
224                }
225                assert!(string[i] == b'}');
226                i += 1;
227                Some(generics)
228            } else {
229                None
230            };
231            let bindings = if string[i] == b'{' {
232                i += 1;
233                let mut bindings = Vec::new();
234                while string[i] == b'{' {
235                    i += 1;
236                    let (binding, boffset) = RenderTypeId::read_from_bytes(&string[i..]);
237                    i += boffset;
238                    let mut bconstraints = Vec::new();
239                    assert!(string[i] == b'{');
240                    i += 1;
241                    while string[i] != b'}' {
242                        let (constraint, coffset) = RenderType::read_from_bytes(&string[i..]);
243                        i += coffset;
244                        bconstraints.push(constraint);
245                    }
246                    assert!(string[i] == b'}');
247                    i += 1;
248                    bindings.push((binding.unwrap(), bconstraints));
249                    assert!(string[i] == b'}');
250                    i += 1;
251                }
252                assert!(string[i] == b'}');
253                i += 1;
254                Some(bindings)
255            } else {
256                None
257            };
258            assert!(string[i] == b'}');
259            i += 1;
260            (RenderType { id, generics, bindings }, i)
261        } else {
262            let (id, offset) = RenderTypeId::read_from_bytes(string);
263            i += offset;
264            (RenderType { id, generics: None, bindings: None }, i)
265        }
266    }
267}
268
269#[derive(Clone, Copy, Debug, Eq, PartialEq)]
270enum RenderTypeId {
271    DefId(DefId),
272    Primitive(clean::PrimitiveType),
273    AssociatedType(Symbol),
274    Index(isize),
275    Mut,
276}
277
278impl RenderTypeId {
279    fn write_to_string(&self, string: &mut String) {
280        let id: i32 = match &self {
281            // 0 is a sentinel, everything else is one-indexed
282            // concrete type
283            RenderTypeId::Index(idx) if *idx >= 0 => (idx + 1isize).try_into().unwrap(),
284            // generic type parameter
285            RenderTypeId::Index(idx) => (*idx).try_into().unwrap(),
286            _ => panic!("must convert render types to indexes before serializing"),
287        };
288        search_index::encode::write_signed_vlqhex_to_string(id, string);
289    }
290    fn read_from_bytes(string: &[u8]) -> (Option<RenderTypeId>, usize) {
291        let Some((value, offset)) = search_index::encode::read_signed_vlqhex_from_string(string)
292        else {
293            return (None, 0);
294        };
295        let value = isize::try_from(value).unwrap();
296        let ty = match value {
297            ..0 => Some(RenderTypeId::Index(value)),
298            0 => None,
299            1.. => Some(RenderTypeId::Index(value - 1)),
300        };
301        (ty, offset)
302    }
303}
304
305/// Full type of functions/methods in the search index.
306#[derive(Clone, Debug, Eq, PartialEq)]
307pub(crate) struct IndexItemFunctionType {
308    inputs: Vec<RenderType>,
309    output: Vec<RenderType>,
310    where_clause: Vec<Vec<RenderType>>,
311    param_names: Vec<Option<Symbol>>,
312}
313
314impl IndexItemFunctionType {
315    fn size(&self) -> usize {
316        self.inputs.iter().map(RenderType::size).sum::<usize>()
317            + self.output.iter().map(RenderType::size).sum::<usize>()
318            + self
319                .where_clause
320                .iter()
321                .map(|constraints| constraints.iter().map(RenderType::size).sum::<usize>())
322                .sum::<usize>()
323    }
324    fn read_from_string_without_param_names(string: &[u8]) -> (IndexItemFunctionType, usize) {
325        let mut i = 0;
326        if string[i] == b'`' {
327            return (
328                IndexItemFunctionType {
329                    inputs: Vec::new(),
330                    output: Vec::new(),
331                    where_clause: Vec::new(),
332                    param_names: Vec::new(),
333                },
334                1,
335            );
336        }
337        assert_eq!(b'{', string[i]);
338        i += 1;
339        fn read_args_from_string(string: &[u8]) -> (Vec<RenderType>, usize) {
340            let mut i = 0;
341            let mut params = Vec::new();
342            if string[i] == b'{' {
343                // multiple params
344                i += 1;
345                while string[i] != b'}' {
346                    let (ty, offset) = RenderType::read_from_bytes(&string[i..]);
347                    i += offset;
348                    params.push(ty);
349                }
350                i += 1;
351            } else if string[i] != b'}' {
352                let (tyid, offset) = RenderTypeId::read_from_bytes(&string[i..]);
353                params.push(RenderType { id: tyid, generics: None, bindings: None });
354                i += offset;
355            }
356            (params, i)
357        }
358        let (inputs, offset) = read_args_from_string(&string[i..]);
359        i += offset;
360        let (output, offset) = read_args_from_string(&string[i..]);
361        i += offset;
362        let mut where_clause = Vec::new();
363        while string[i] != b'}' {
364            let (constraint, offset) = read_args_from_string(&string[i..]);
365            i += offset;
366            where_clause.push(constraint);
367        }
368        assert_eq!(b'}', string[i], "{} {}", String::from_utf8_lossy(&string), i);
369        i += 1;
370        (IndexItemFunctionType { inputs, output, where_clause, param_names: Vec::new() }, i)
371    }
372    fn write_to_string_without_param_names<'a>(&'a self, string: &mut String) {
373        // If we couldn't figure out a type, just write 0,
374        // which is encoded as `` ` `` (see RenderTypeId::write_to_string).
375        let has_missing = self
376            .inputs
377            .iter()
378            .chain(self.output.iter())
379            .any(|i| i.id.is_none() && i.generics.is_none());
380        if has_missing {
381            string.push('`');
382        } else {
383            string.push('{');
384            match &self.inputs[..] {
385                [one] if one.generics.is_none() && one.bindings.is_none() => {
386                    one.write_to_string(string);
387                }
388                _ => {
389                    string.push('{');
390                    for item in &self.inputs[..] {
391                        item.write_to_string(string);
392                    }
393                    string.push('}');
394                }
395            }
396            match &self.output[..] {
397                [] if self.where_clause.is_empty() => {}
398                [one] if one.generics.is_none() && one.bindings.is_none() => {
399                    one.write_to_string(string);
400                }
401                _ => {
402                    string.push('{');
403                    for item in &self.output[..] {
404                        item.write_to_string(string);
405                    }
406                    string.push('}');
407                }
408            }
409            for constraint in &self.where_clause {
410                if let [one] = &constraint[..]
411                    && one.generics.is_none()
412                    && one.bindings.is_none()
413                {
414                    one.write_to_string(string);
415                } else {
416                    string.push('{');
417                    for item in &constraint[..] {
418                        item.write_to_string(string);
419                    }
420                    string.push('}');
421                }
422            }
423            string.push('}');
424        }
425    }
426}
427
428#[derive(Debug, Clone)]
429pub(crate) struct StylePath {
430    /// The path to the theme
431    pub(crate) path: PathBuf,
432}
433
434impl StylePath {
435    pub(crate) fn basename(&self) -> Result<String, Error> {
436        Ok(try_none!(try_none!(self.path.file_stem(), &self.path).to_str(), &self.path).to_string())
437    }
438}
439
440#[derive(Debug, Eq, PartialEq, Hash)]
441struct ItemEntry {
442    url: String,
443    name: String,
444}
445
446impl ItemEntry {
447    fn new(mut url: String, name: String) -> ItemEntry {
448        while url.starts_with('/') {
449            url.remove(0);
450        }
451        ItemEntry { url, name }
452    }
453}
454
455impl ItemEntry {
456    fn print(&self) -> impl fmt::Display {
457        fmt::from_fn(move |f| write!(f, "<a href=\"{}\">{}</a>", self.url, Escape(&self.name)))
458    }
459}
460
461impl PartialOrd for ItemEntry {
462    fn partial_cmp(&self, other: &ItemEntry) -> Option<::std::cmp::Ordering> {
463        Some(self.cmp(other))
464    }
465}
466
467impl Ord for ItemEntry {
468    fn cmp(&self, other: &ItemEntry) -> ::std::cmp::Ordering {
469        self.name.cmp(&other.name)
470    }
471}
472
473#[derive(Debug)]
474struct AllTypes {
475    structs: FxIndexSet<ItemEntry>,
476    enums: FxIndexSet<ItemEntry>,
477    unions: FxIndexSet<ItemEntry>,
478    primitives: FxIndexSet<ItemEntry>,
479    traits: FxIndexSet<ItemEntry>,
480    macros: FxIndexSet<ItemEntry>,
481    functions: FxIndexSet<ItemEntry>,
482    type_aliases: FxIndexSet<ItemEntry>,
483    statics: FxIndexSet<ItemEntry>,
484    constants: FxIndexSet<ItemEntry>,
485    attribute_macros: FxIndexSet<ItemEntry>,
486    derive_macros: FxIndexSet<ItemEntry>,
487    trait_aliases: FxIndexSet<ItemEntry>,
488}
489
490impl AllTypes {
491    fn new() -> AllTypes {
492        let new_set = |cap| FxIndexSet::with_capacity_and_hasher(cap, Default::default());
493        AllTypes {
494            structs: new_set(100),
495            enums: new_set(100),
496            unions: new_set(100),
497            primitives: new_set(26),
498            traits: new_set(100),
499            macros: new_set(100),
500            functions: new_set(100),
501            type_aliases: new_set(100),
502            statics: new_set(100),
503            constants: new_set(100),
504            attribute_macros: new_set(100),
505            derive_macros: new_set(100),
506            trait_aliases: new_set(100),
507        }
508    }
509
510    fn append(&mut self, item_name: String, item_type: &ItemType) {
511        let mut url: Vec<_> = item_name.split("::").skip(1).collect();
512        if let Some(name) = url.pop() {
513            let new_url = format!("{}/{item_type}.{name}.html", url.join("/"));
514            url.push(name);
515            let name = url.join("::");
516            match *item_type {
517                ItemType::Struct => self.structs.insert(ItemEntry::new(new_url, name)),
518                ItemType::Enum => self.enums.insert(ItemEntry::new(new_url, name)),
519                ItemType::Union => self.unions.insert(ItemEntry::new(new_url, name)),
520                ItemType::Primitive => self.primitives.insert(ItemEntry::new(new_url, name)),
521                ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)),
522                ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)),
523                ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)),
524                ItemType::TypeAlias => self.type_aliases.insert(ItemEntry::new(new_url, name)),
525                ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)),
526                ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)),
527                ItemType::ProcAttribute => {
528                    self.attribute_macros.insert(ItemEntry::new(new_url, name))
529                }
530                ItemType::ProcDerive => self.derive_macros.insert(ItemEntry::new(new_url, name)),
531                ItemType::TraitAlias => self.trait_aliases.insert(ItemEntry::new(new_url, name)),
532                _ => true,
533            };
534        }
535    }
536
537    fn item_sections(&self) -> FxHashSet<ItemSection> {
538        let mut sections = FxHashSet::default();
539
540        if !self.structs.is_empty() {
541            sections.insert(ItemSection::Structs);
542        }
543        if !self.enums.is_empty() {
544            sections.insert(ItemSection::Enums);
545        }
546        if !self.unions.is_empty() {
547            sections.insert(ItemSection::Unions);
548        }
549        if !self.primitives.is_empty() {
550            sections.insert(ItemSection::PrimitiveTypes);
551        }
552        if !self.traits.is_empty() {
553            sections.insert(ItemSection::Traits);
554        }
555        if !self.macros.is_empty() {
556            sections.insert(ItemSection::Macros);
557        }
558        if !self.functions.is_empty() {
559            sections.insert(ItemSection::Functions);
560        }
561        if !self.type_aliases.is_empty() {
562            sections.insert(ItemSection::TypeAliases);
563        }
564        if !self.statics.is_empty() {
565            sections.insert(ItemSection::Statics);
566        }
567        if !self.constants.is_empty() {
568            sections.insert(ItemSection::Constants);
569        }
570        if !self.attribute_macros.is_empty() {
571            sections.insert(ItemSection::AttributeMacros);
572        }
573        if !self.derive_macros.is_empty() {
574            sections.insert(ItemSection::DeriveMacros);
575        }
576        if !self.trait_aliases.is_empty() {
577            sections.insert(ItemSection::TraitAliases);
578        }
579
580        sections
581    }
582
583    fn print(&self) -> impl fmt::Display {
584        fn print_entries(e: &FxIndexSet<ItemEntry>, kind: ItemSection) -> impl fmt::Display {
585            fmt::from_fn(move |f| {
586                if e.is_empty() {
587                    return Ok(());
588                }
589
590                let mut e: Vec<&ItemEntry> = e.iter().collect();
591                e.sort();
592                write!(
593                    f,
594                    "<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">",
595                    id = kind.id(),
596                    title = kind.name(),
597                )?;
598
599                for s in e.iter() {
600                    write!(f, "<li>{}</li>", s.print())?;
601                }
602
603                f.write_str("</ul>")
604            })
605        }
606
607        fmt::from_fn(|f| {
608            f.write_str(
609                "<div class=\"main-heading\">\
610                    <h1>List of all items</h1>\
611                    <rustdoc-toolbar></rustdoc-toolbar>\
612                </div>",
613            )?;
614            // Note: print_entries does not escape the title, because we know the current set of titles
615            // doesn't require escaping.
616            print_entries(&self.structs, ItemSection::Structs).fmt(f)?;
617            print_entries(&self.enums, ItemSection::Enums).fmt(f)?;
618            print_entries(&self.unions, ItemSection::Unions).fmt(f)?;
619            print_entries(&self.primitives, ItemSection::PrimitiveTypes).fmt(f)?;
620            print_entries(&self.traits, ItemSection::Traits).fmt(f)?;
621            print_entries(&self.macros, ItemSection::Macros).fmt(f)?;
622            print_entries(&self.attribute_macros, ItemSection::AttributeMacros).fmt(f)?;
623            print_entries(&self.derive_macros, ItemSection::DeriveMacros).fmt(f)?;
624            print_entries(&self.functions, ItemSection::Functions).fmt(f)?;
625            print_entries(&self.type_aliases, ItemSection::TypeAliases).fmt(f)?;
626            print_entries(&self.trait_aliases, ItemSection::TraitAliases).fmt(f)?;
627            print_entries(&self.statics, ItemSection::Statics).fmt(f)?;
628            print_entries(&self.constants, ItemSection::Constants).fmt(f)?;
629            Ok(())
630        })
631    }
632}
633
634fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
635    let mut content = SCRAPE_EXAMPLES_HELP_MD.to_owned();
636    content.push_str(&format!(
637        "## More information\n\n\
638      If you want more information about this feature, please read the [corresponding chapter in \
639      the Rustdoc book]({DOC_RUST_LANG_ORG_VERSION}/rustdoc/scraped-examples.html)."
640    ));
641
642    format!(
643        "<div class=\"main-heading\">\
644             <h1>About scraped examples</h1>\
645         </div>\
646         <div>{}</div>",
647        fmt::from_fn(|f| Markdown {
648            content: &content,
649            links: &[],
650            ids: &mut IdMap::default(),
651            error_codes: shared.codes,
652            edition: shared.edition(),
653            playground: &shared.playground,
654            heading_offset: HeadingOffset::H1,
655        }
656        .write_into(f))
657    )
658}
659
660fn document(
661    cx: &Context<'_>,
662    item: &clean::Item,
663    parent: Option<&clean::Item>,
664    heading_offset: HeadingOffset,
665) -> impl fmt::Display {
666    if let Some(ref name) = item.name {
667        info!("Documenting {name}");
668    }
669
670    fmt::from_fn(move |f| {
671        document_item_info(cx, item, parent).render_into(f)?;
672        if parent.is_none() {
673            write!(f, "{}", document_full_collapsible(item, cx, heading_offset))
674        } else {
675            write!(f, "{}", document_full(item, cx, heading_offset))
676        }
677    })
678}
679
680/// Render md_text as markdown.
681fn render_markdown(
682    cx: &Context<'_>,
683    md_text: &str,
684    links: Vec<RenderedLink>,
685    heading_offset: HeadingOffset,
686) -> impl fmt::Display {
687    fmt::from_fn(move |f| {
688        f.write_str("<div class=\"docblock\">")?;
689        Markdown {
690            content: md_text,
691            links: &links,
692            ids: &mut cx.id_map.borrow_mut(),
693            error_codes: cx.shared.codes,
694            edition: cx.shared.edition(),
695            playground: &cx.shared.playground,
696            heading_offset,
697        }
698        .write_into(&mut *f)?;
699        f.write_str("</div>")
700    })
701}
702
703/// Writes a documentation block containing only the first paragraph of the documentation. If the
704/// docs are longer, a "Read more" link is appended to the end.
705fn document_short(
706    item: &clean::Item,
707    cx: &Context<'_>,
708    link: AssocItemLink<'_>,
709    parent: &clean::Item,
710    show_def_docs: bool,
711) -> impl fmt::Display {
712    fmt::from_fn(move |f| {
713        document_item_info(cx, item, Some(parent)).render_into(f)?;
714        if !show_def_docs {
715            return Ok(());
716        }
717        let s = item.doc_value();
718        if !s.is_empty() {
719            let (mut summary_html, has_more_content) =
720                MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content();
721
722            let link = if has_more_content {
723                let link = fmt::from_fn(|f| {
724                    write!(
725                        f,
726                        " <a{}>Read more</a>",
727                        assoc_href_attr(item, link, cx).maybe_display()
728                    )
729                });
730
731                if let Some(idx) = summary_html.rfind("</p>") {
732                    summary_html.insert_str(idx, &link.to_string());
733                    None
734                } else {
735                    Some(link)
736                }
737            } else {
738                None
739            }
740            .maybe_display();
741
742            write!(f, "<div class='docblock'>{summary_html}{link}</div>")?;
743        }
744        Ok(())
745    })
746}
747
748fn document_full_collapsible(
749    item: &clean::Item,
750    cx: &Context<'_>,
751    heading_offset: HeadingOffset,
752) -> impl fmt::Display {
753    document_full_inner(item, cx, true, heading_offset)
754}
755
756fn document_full(
757    item: &clean::Item,
758    cx: &Context<'_>,
759    heading_offset: HeadingOffset,
760) -> impl fmt::Display {
761    document_full_inner(item, cx, false, heading_offset)
762}
763
764fn document_full_inner(
765    item: &clean::Item,
766    cx: &Context<'_>,
767    is_collapsible: bool,
768    heading_offset: HeadingOffset,
769) -> impl fmt::Display {
770    fmt::from_fn(move |f| {
771        if let Some(s) = item.opt_doc_value() {
772            debug!("Doc block: =====\n{s}\n=====");
773            if is_collapsible {
774                write!(
775                    f,
776                    "<details class=\"toggle top-doc\" open>\
777                     <summary class=\"hideme\">\
778                        <span>Expand description</span>\
779                     </summary>{}</details>",
780                    render_markdown(cx, &s, item.links(cx), heading_offset)
781                )?;
782            } else {
783                write!(f, "{}", render_markdown(cx, &s, item.links(cx), heading_offset))?;
784            }
785        }
786
787        let kind = match &item.kind {
788            clean::ItemKind::StrippedItem(box kind) | kind => kind,
789        };
790
791        if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind {
792            render_call_locations(f, cx, item)?;
793        }
794        Ok(())
795    })
796}
797
798#[derive(Template)]
799#[template(path = "item_info.html")]
800struct ItemInfo {
801    items: Vec<ShortItemInfo>,
802}
803/// Add extra information about an item such as:
804///
805/// * Stability
806/// * Deprecated
807/// * Required features (through the `doc_cfg` feature)
808fn document_item_info(
809    cx: &Context<'_>,
810    item: &clean::Item,
811    parent: Option<&clean::Item>,
812) -> ItemInfo {
813    let items = short_item_info(item, cx, parent);
814    ItemInfo { items }
815}
816
817fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> {
818    let cfg = match (&item.cfg, parent.and_then(|p| p.cfg.as_ref())) {
819        (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
820        (cfg, _) => cfg.as_deref().cloned(),
821    };
822
823    debug!(
824        "Portability {name:?} {item_cfg:?} (parent: {parent:?}) - {parent_cfg:?} = {cfg:?}",
825        name = item.name,
826        item_cfg = item.cfg,
827        parent_cfg = parent.and_then(|p| p.cfg.as_ref()),
828    );
829
830    Some(cfg?.render_long_html())
831}
832
833#[derive(Template)]
834#[template(path = "short_item_info.html")]
835enum ShortItemInfo {
836    /// A message describing the deprecation of this item
837    Deprecation {
838        message: String,
839    },
840    /// The feature corresponding to an unstable item, and optionally
841    /// a tracking issue URL and number.
842    Unstable {
843        feature: String,
844        tracking: Option<(String, u32)>,
845    },
846    Portability {
847        message: String,
848    },
849}
850
851/// Render the stability, deprecation and portability information that is displayed at the top of
852/// the item's documentation.
853fn short_item_info(
854    item: &clean::Item,
855    cx: &Context<'_>,
856    parent: Option<&clean::Item>,
857) -> Vec<ShortItemInfo> {
858    let mut extra_info = vec![];
859
860    if let Some(depr @ Deprecation { note, since, suggestion: _ }) = item.deprecation(cx.tcx()) {
861        // We display deprecation messages for #[deprecated], but only display
862        // the future-deprecation messages for rustc versions.
863        let mut message = match since {
864            DeprecatedSince::RustcVersion(version) => {
865                if depr.is_in_effect() {
866                    format!("Deprecated since {version}")
867                } else {
868                    format!("Deprecating in {version}")
869                }
870            }
871            DeprecatedSince::Future => String::from("Deprecating in a future version"),
872            DeprecatedSince::NonStandard(since) => {
873                format!("Deprecated since {}", Escape(since.as_str()))
874            }
875            DeprecatedSince::Unspecified | DeprecatedSince::Err => String::from("Deprecated"),
876        };
877
878        if let Some(note) = note {
879            let note = note.as_str();
880            let mut id_map = cx.id_map.borrow_mut();
881            let links = item.links(cx);
882            let html = MarkdownItemInfo::new(note, &links, &mut id_map);
883            message.push_str(": ");
884            html.write_into(&mut message).unwrap();
885        }
886        extra_info.push(ShortItemInfo::Deprecation { message });
887    }
888
889    // Render unstable items. But don't render "rustc_private" crates (internal compiler crates).
890    // Those crates are permanently unstable so it makes no sense to render "unstable" everywhere.
891    if let Some((StabilityLevel::Unstable { reason: _, issue, .. }, feature)) = item
892        .stability(cx.tcx())
893        .as_ref()
894        .filter(|stab| stab.feature != sym::rustc_private)
895        .map(|stab| (stab.level, stab.feature))
896    {
897        let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue)
898        {
899            Some((url.clone(), issue.get()))
900        } else {
901            None
902        };
903        extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking });
904    }
905
906    if let Some(message) = portability(item, parent) {
907        extra_info.push(ShortItemInfo::Portability { message });
908    }
909
910    extra_info
911}
912
913// Render the list of items inside one of the sections "Trait Implementations",
914// "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages).
915fn render_impls(
916    cx: &Context<'_>,
917    mut w: impl Write,
918    impls: &[&Impl],
919    containing_item: &clean::Item,
920    toggle_open_by_default: bool,
921) -> fmt::Result {
922    let mut rendered_impls = impls
923        .iter()
924        .map(|i| {
925            let did = i.trait_did().unwrap();
926            let provided_trait_methods = i.inner_impl().provided_trait_methods(cx.tcx());
927            let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods);
928            let imp = render_impl(
929                cx,
930                i,
931                containing_item,
932                assoc_link,
933                RenderMode::Normal,
934                None,
935                &[],
936                ImplRenderingParameters {
937                    show_def_docs: true,
938                    show_default_items: true,
939                    show_non_assoc_items: true,
940                    toggle_open_by_default,
941                },
942            );
943            imp.to_string()
944        })
945        .collect::<Vec<_>>();
946    rendered_impls.sort();
947    w.write_str(&rendered_impls.join(""))
948}
949
950/// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item.
951fn assoc_href_attr(
952    it: &clean::Item,
953    link: AssocItemLink<'_>,
954    cx: &Context<'_>,
955) -> Option<impl fmt::Display> {
956    let name = it.name.unwrap();
957    let item_type = it.type_();
958
959    enum Href<'a> {
960        AnchorId(&'a str),
961        Anchor(ItemType),
962        Url(String, ItemType),
963    }
964
965    let href = match link {
966        AssocItemLink::Anchor(Some(id)) => Href::AnchorId(id),
967        AssocItemLink::Anchor(None) => Href::Anchor(item_type),
968        AssocItemLink::GotoSource(did, provided_methods) => {
969            // We're creating a link from the implementation of an associated item to its
970            // declaration in the trait declaration.
971            let item_type = match item_type {
972                // For historical but not technical reasons, the item type of methods in
973                // trait declarations depends on whether the method is required (`TyMethod`) or
974                // provided (`Method`).
975                ItemType::Method | ItemType::TyMethod => {
976                    if provided_methods.contains(&name) {
977                        ItemType::Method
978                    } else {
979                        ItemType::TyMethod
980                    }
981                }
982                // For associated types and constants, no such distinction exists.
983                item_type => item_type,
984            };
985
986            match href(did.expect_def_id(), cx) {
987                Ok(HrefInfo { url, .. }) => Href::Url(url, item_type),
988                // The link is broken since it points to an external crate that wasn't documented.
989                // Do not create any link in such case. This is better than falling back to a
990                // dummy anchor like `#{item_type}.{name}` representing the `id` of *this* impl item
991                // (that used to happen in older versions). Indeed, in most cases this dummy would
992                // coincide with the `id`. However, it would not always do so.
993                // In general, this dummy would be incorrect:
994                // If the type with the trait impl also had an inherent impl with an assoc. item of
995                // the *same* name as this impl item, the dummy would link to that one even though
996                // those two items are distinct!
997                // In this scenario, the actual `id` of this impl item would be
998                // `#{item_type}.{name}-{n}` for some number `n` (a disambiguator).
999                Err(HrefError::DocumentationNotBuilt) => return None,
1000                Err(_) => Href::Anchor(item_type),
1001            }
1002        }
1003    };
1004
1005    let href = fmt::from_fn(move |f| match &href {
1006        Href::AnchorId(id) => write!(f, "#{id}"),
1007        Href::Url(url, item_type) => {
1008            write!(f, "{url}#{item_type}.{name}")
1009        }
1010        Href::Anchor(item_type) => {
1011            write!(f, "#{item_type}.{name}")
1012        }
1013    });
1014
1015    // If there is no `href` for the reason explained above, simply do not render it which is valid:
1016    // https://html.spec.whatwg.org/multipage/links.html#links-created-by-a-and-area-elements
1017    Some(fmt::from_fn(move |f| write!(f, " href=\"{href}\"")))
1018}
1019
1020#[derive(Debug)]
1021enum AssocConstValue<'a> {
1022    // In trait definitions, it is relevant for the public API whether an
1023    // associated constant comes with a default value, so even if we cannot
1024    // render its value, the presence of a value must be shown using `= _`.
1025    TraitDefault(&'a clean::ConstantKind),
1026    // In impls, there is no need to show `= _`.
1027    Impl(&'a clean::ConstantKind),
1028    None,
1029}
1030
1031fn assoc_const(
1032    it: &clean::Item,
1033    generics: &clean::Generics,
1034    ty: &clean::Type,
1035    value: AssocConstValue<'_>,
1036    link: AssocItemLink<'_>,
1037    indent: usize,
1038    cx: &Context<'_>,
1039) -> impl fmt::Display {
1040    let tcx = cx.tcx();
1041    fmt::from_fn(move |w| {
1042        render_attributes_in_code(w, it, &" ".repeat(indent), cx)?;
1043        write!(
1044            w,
1045            "{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
1046            indent = " ".repeat(indent),
1047            vis = visibility_print_with_space(it, cx),
1048            href = assoc_href_attr(it, link, cx).maybe_display(),
1049            name = it.name.as_ref().unwrap(),
1050            generics = print_generics(generics, cx),
1051            ty = print_type(ty, cx),
1052        )?;
1053        if let AssocConstValue::TraitDefault(konst) | AssocConstValue::Impl(konst) = value {
1054            let repr = konst.expr(tcx);
1055            if match value {
1056                AssocConstValue::TraitDefault(_) => true, // always show
1057                // FIXME: Comparing against the special string "_" denoting overly complex const exprs
1058                //        is rather hacky; `ConstKind::expr` should have a richer return type.
1059                AssocConstValue::Impl(_) => repr != "_", // show if there is a meaningful value to show
1060                AssocConstValue::None => unreachable!(),
1061            } {
1062                write!(w, " = {}", Escape(&repr))?;
1063            }
1064        }
1065        write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display())
1066    })
1067}
1068
1069fn assoc_type(
1070    it: &clean::Item,
1071    generics: &clean::Generics,
1072    bounds: &[clean::GenericBound],
1073    default: Option<&clean::Type>,
1074    link: AssocItemLink<'_>,
1075    indent: usize,
1076    cx: &Context<'_>,
1077) -> impl fmt::Display {
1078    fmt::from_fn(move |w| {
1079        render_attributes_in_code(w, it, &" ".repeat(indent), cx)?;
1080        write!(
1081            w,
1082            "{indent}{vis}type <a{href} class=\"associatedtype\">{name}</a>{generics}",
1083            indent = " ".repeat(indent),
1084            vis = visibility_print_with_space(it, cx),
1085            href = assoc_href_attr(it, link, cx).maybe_display(),
1086            name = it.name.as_ref().unwrap(),
1087            generics = print_generics(generics, cx),
1088        )?;
1089        if !bounds.is_empty() {
1090            write!(w, ": {}", print_generic_bounds(bounds, cx))?;
1091        }
1092        // Render the default before the where-clause which aligns with the new recommended style. See #89122.
1093        if let Some(default) = default {
1094            write!(w, " = {}", print_type(default, cx))?;
1095        }
1096        write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display())
1097    })
1098}
1099
1100fn assoc_method(
1101    meth: &clean::Item,
1102    g: &clean::Generics,
1103    d: &clean::FnDecl,
1104    link: AssocItemLink<'_>,
1105    parent: ItemType,
1106    cx: &Context<'_>,
1107    render_mode: RenderMode,
1108) -> impl fmt::Display {
1109    let tcx = cx.tcx();
1110    let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item");
1111    let name = meth.name.as_ref().unwrap();
1112    let vis = visibility_print_with_space(meth, cx).to_string();
1113    let defaultness = 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            let mut trait_item_deprecated = false;
1797            if render_method_item {
1798                if !is_default_item {
1799                    if let Some(t) = trait_ {
1800                        // The trait item may have been stripped so we might not
1801                        // find any documentation or stability for it.
1802                        if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1803                            trait_item_deprecated = it.is_deprecated(cx.tcx());
1804                            // We need the stability of the item from the trait
1805                            // because impls can't have a stability.
1806                            if !item.doc_value().is_empty() {
1807                                document_item_info(cx, it, Some(parent))
1808                                    .render_into(&mut info_buffer)?;
1809                                doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1810                                short_documented = false;
1811                            } else {
1812                                // In case the item isn't documented,
1813                                // provide short documentation from the trait.
1814                                doc_buffer = document_short(
1815                                    it,
1816                                    cx,
1817                                    link,
1818                                    parent,
1819                                    rendering_params.show_def_docs,
1820                                )
1821                                .to_string();
1822                            }
1823                        }
1824                    } else {
1825                        document_item_info(cx, item, Some(parent)).render_into(&mut info_buffer)?;
1826                        if rendering_params.show_def_docs {
1827                            doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1828                            short_documented = false;
1829                        }
1830                    }
1831                } else {
1832                    doc_buffer =
1833                        document_short(item, cx, link, parent, rendering_params.show_def_docs)
1834                            .to_string();
1835                }
1836            }
1837            let mut w = if short_documented && trait_.is_some() {
1838                Either::Left(interesting)
1839            } else {
1840                Either::Right(boring)
1841            };
1842
1843            let mut deprecation_class = if trait_item_deprecated || item.is_deprecated(cx.tcx()) {
1844                " deprecated"
1845            } else {
1846                ""
1847            };
1848
1849            let toggled = !doc_buffer.is_empty();
1850            if toggled {
1851                let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
1852                write!(
1853                    w,
1854                    "<details class=\"toggle{method_toggle_class}{deprecation_class}\" open><summary>"
1855                )?;
1856                deprecation_class = "";
1857            }
1858            match &item.kind {
1859                clean::MethodItem(..) | clean::RequiredMethodItem(_) => {
1860                    // Only render when the method is not static or we allow static methods
1861                    if render_method_item {
1862                        let id = cx.derive_id(format!("{item_type}.{name}"));
1863                        let source_id = trait_
1864                            .and_then(|trait_| {
1865                                trait_
1866                                    .items
1867                                    .iter()
1868                                    .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
1869                            })
1870                            .map(|item| format!("{}.{name}", item.type_()));
1871                        write!(
1872                            w,
1873                            "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1874                                {}",
1875                            render_rightside(cx, item, render_mode)
1876                        )?;
1877                        if trait_.is_some() {
1878                            // Anchors are only used on trait impls.
1879                            write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1880                        }
1881                        write!(
1882                            w,
1883                            "<h4 class=\"code-header\">{}</h4></section>",
1884                            render_assoc_item(
1885                                item,
1886                                link.anchor(source_id.as_ref().unwrap_or(&id)),
1887                                ItemType::Impl,
1888                                cx,
1889                                render_mode,
1890                            ),
1891                        )?;
1892                    }
1893                }
1894                clean::RequiredAssocConstItem(generics, ty) => {
1895                    let source_id = format!("{item_type}.{name}");
1896                    let id = cx.derive_id(&source_id);
1897                    write!(
1898                        w,
1899                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1900                            {}",
1901                        render_rightside(cx, item, render_mode)
1902                    )?;
1903                    if trait_.is_some() {
1904                        // Anchors are only used on trait impls.
1905                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1906                    }
1907                    write!(
1908                        w,
1909                        "<h4 class=\"code-header\">{}</h4></section>",
1910                        assoc_const(
1911                            item,
1912                            generics,
1913                            ty,
1914                            AssocConstValue::None,
1915                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
1916                            0,
1917                            cx,
1918                        ),
1919                    )?;
1920                }
1921                clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
1922                    let source_id = format!("{item_type}.{name}");
1923                    let id = cx.derive_id(&source_id);
1924                    write!(
1925                        w,
1926                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1927                            {}",
1928                        render_rightside(cx, item, render_mode),
1929                    )?;
1930                    if trait_.is_some() {
1931                        // Anchors are only used on trait impls.
1932                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1933                    }
1934                    write!(
1935                        w,
1936                        "<h4 class=\"code-header\">{}</h4></section>",
1937                        assoc_const(
1938                            item,
1939                            &ci.generics,
1940                            &ci.type_,
1941                            match item.kind {
1942                                clean::ProvidedAssocConstItem(_) =>
1943                                    AssocConstValue::TraitDefault(&ci.kind),
1944                                clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
1945                                _ => unreachable!(),
1946                            },
1947                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
1948                            0,
1949                            cx,
1950                        ),
1951                    )?;
1952                }
1953                clean::RequiredAssocTypeItem(generics, bounds) => {
1954                    let source_id = format!("{item_type}.{name}");
1955                    let id = cx.derive_id(&source_id);
1956                    write!(
1957                        w,
1958                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1959                            {}",
1960                        render_rightside(cx, item, render_mode),
1961                    )?;
1962                    if trait_.is_some() {
1963                        // Anchors are only used on trait impls.
1964                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1965                    }
1966                    write!(
1967                        w,
1968                        "<h4 class=\"code-header\">{}</h4></section>",
1969                        assoc_type(
1970                            item,
1971                            generics,
1972                            bounds,
1973                            None,
1974                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
1975                            0,
1976                            cx,
1977                        ),
1978                    )?;
1979                }
1980                clean::AssocTypeItem(tydef, _bounds) => {
1981                    let source_id = format!("{item_type}.{name}");
1982                    let id = cx.derive_id(&source_id);
1983                    write!(
1984                        w,
1985                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1986                            {}",
1987                        render_rightside(cx, item, render_mode),
1988                    )?;
1989                    if trait_.is_some() {
1990                        // Anchors are only used on trait impls.
1991                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1992                    }
1993                    write!(
1994                        w,
1995                        "<h4 class=\"code-header\">{}</h4></section>",
1996                        assoc_type(
1997                            item,
1998                            &tydef.generics,
1999                            &[], // intentionally leaving out bounds
2000                            Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
2001                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
2002                            0,
2003                            cx,
2004                        ),
2005                    )?;
2006                }
2007                clean::StrippedItem(..) => return Ok(()),
2008                _ => panic!("can't make docs for trait item with name {:?}", item.name),
2009            }
2010
2011            w.write_str(&info_buffer)?;
2012            if toggled {
2013                write!(w, "</summary>{doc_buffer}</details>")?;
2014            }
2015            Ok(())
2016        }
2017
2018        let mut impl_items = String::new();
2019        let mut default_impl_items = String::new();
2020        let impl_ = i.inner_impl();
2021
2022        // Impl items are grouped by kinds:
2023        //
2024        // 1. Constants
2025        // 2. Types
2026        // 3. Functions
2027        //
2028        // This order is because you can have associated constants used in associated types (like array
2029        // length), and both in associated functions. So with this order, when reading from top to
2030        // bottom, you should see items definitions before they're actually used most of the time.
2031        let mut assoc_types = Vec::new();
2032        let mut methods = Vec::new();
2033
2034        if !impl_.is_negative_trait_impl() {
2035            for impl_item in &impl_.items {
2036                match impl_item.kind {
2037                    clean::MethodItem(..) | clean::RequiredMethodItem(_) => methods.push(impl_item),
2038                    clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => {
2039                        assoc_types.push(impl_item)
2040                    }
2041                    clean::RequiredAssocConstItem(..)
2042                    | clean::ProvidedAssocConstItem(_)
2043                    | clean::ImplAssocConstItem(_) => {
2044                        // We render it directly since they're supposed to come first.
2045                        doc_impl_item(
2046                            &mut default_impl_items,
2047                            &mut impl_items,
2048                            cx,
2049                            impl_item,
2050                            if trait_.is_some() { &i.impl_item } else { parent },
2051                            link,
2052                            render_mode,
2053                            false,
2054                            trait_,
2055                            rendering_params,
2056                        )?;
2057                    }
2058                    _ => {}
2059                }
2060            }
2061
2062            for assoc_type in assoc_types {
2063                doc_impl_item(
2064                    &mut default_impl_items,
2065                    &mut impl_items,
2066                    cx,
2067                    assoc_type,
2068                    if trait_.is_some() { &i.impl_item } else { parent },
2069                    link,
2070                    render_mode,
2071                    false,
2072                    trait_,
2073                    rendering_params,
2074                )?;
2075            }
2076            for method in methods {
2077                doc_impl_item(
2078                    &mut default_impl_items,
2079                    &mut impl_items,
2080                    cx,
2081                    method,
2082                    if trait_.is_some() { &i.impl_item } else { parent },
2083                    link,
2084                    render_mode,
2085                    false,
2086                    trait_,
2087                    rendering_params,
2088                )?;
2089            }
2090        }
2091
2092        fn render_default_items(
2093            mut boring: impl fmt::Write,
2094            mut interesting: impl fmt::Write,
2095            cx: &Context<'_>,
2096            t: &clean::Trait,
2097            i: &clean::Impl,
2098            parent: &clean::Item,
2099            render_mode: RenderMode,
2100            rendering_params: ImplRenderingParameters,
2101        ) -> fmt::Result {
2102            for trait_item in &t.items {
2103                // Skip over any default trait items that are impossible to reference
2104                // (e.g. if it has a `Self: Sized` bound on an unsized type).
2105                if let Some(impl_def_id) = parent.item_id.as_def_id()
2106                    && let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
2107                    && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id))
2108                {
2109                    continue;
2110                }
2111
2112                let n = trait_item.name;
2113                if i.items.iter().any(|m| m.name == n) {
2114                    continue;
2115                }
2116                let did = i.trait_.as_ref().unwrap().def_id();
2117                let provided_methods = i.provided_trait_methods(cx.tcx());
2118                let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
2119
2120                doc_impl_item(
2121                    &mut boring,
2122                    &mut interesting,
2123                    cx,
2124                    trait_item,
2125                    parent,
2126                    assoc_link,
2127                    render_mode,
2128                    true,
2129                    Some(t),
2130                    rendering_params,
2131                )?;
2132            }
2133            Ok(())
2134        }
2135
2136        // If we've implemented a trait, then also emit documentation for all
2137        // default items which weren't overridden in the implementation block.
2138        // We don't emit documentation for default items if they appear in the
2139        // Implementations on Foreign Types or Implementors sections.
2140        if rendering_params.show_default_items
2141            && let Some(t) = trait_
2142            && !impl_.is_negative_trait_impl()
2143        {
2144            render_default_items(
2145                &mut default_impl_items,
2146                &mut impl_items,
2147                cx,
2148                t,
2149                impl_,
2150                &i.impl_item,
2151                render_mode,
2152                rendering_params,
2153            )?;
2154        }
2155        if render_mode == RenderMode::Normal {
2156            let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
2157            let deprecation_attr = if impl_.is_deprecated
2158                || trait_.is_some_and(|trait_| trait_.is_deprecated(cx.tcx()))
2159            {
2160                " deprecated"
2161            } else {
2162                ""
2163            };
2164            if toggled {
2165                close_tags.push("</details>");
2166                write!(
2167                    w,
2168                    "<details class=\"toggle implementors-toggle{deprecation_attr}\"{}>\
2169                        <summary>",
2170                    if rendering_params.toggle_open_by_default { " open" } else { "" }
2171                )?;
2172            }
2173
2174            let (before_dox, after_dox) = i
2175                .impl_item
2176                .opt_doc_value()
2177                .map(|dox| {
2178                    Markdown {
2179                        content: &dox,
2180                        links: &i.impl_item.links(cx),
2181                        ids: &mut cx.id_map.borrow_mut(),
2182                        error_codes: cx.shared.codes,
2183                        edition: cx.shared.edition(),
2184                        playground: &cx.shared.playground,
2185                        heading_offset: HeadingOffset::H4,
2186                    }
2187                    .split_summary_and_content()
2188                })
2189                .unwrap_or((None, None));
2190
2191            write!(
2192                w,
2193                "{}",
2194                render_impl_summary(
2195                    cx,
2196                    i,
2197                    parent,
2198                    rendering_params.show_def_docs,
2199                    use_absolute,
2200                    aliases,
2201                    before_dox.as_deref(),
2202                    trait_.is_none() && impl_.items.is_empty(),
2203                )
2204            )?;
2205            if toggled {
2206                w.write_str("</summary>")?;
2207            }
2208
2209            if before_dox.is_some()
2210                && let Some(after_dox) = after_dox
2211            {
2212                write!(w, "<div class=\"docblock\">{after_dox}</div>")?;
2213            }
2214
2215            if !default_impl_items.is_empty() || !impl_items.is_empty() {
2216                w.write_str("<div class=\"impl-items\">")?;
2217                close_tags.push("</div>");
2218            }
2219        }
2220        if !default_impl_items.is_empty() || !impl_items.is_empty() {
2221            w.write_str(&default_impl_items)?;
2222            w.write_str(&impl_items)?;
2223        }
2224        for tag in close_tags.into_iter().rev() {
2225            w.write_str(tag)?;
2226        }
2227        Ok(())
2228    })
2229}
2230
2231// Render the items that appear on the right side of methods, impls, and
2232// associated types. For example "1.0.0 (const: 1.39.0) · source".
2233fn render_rightside(
2234    cx: &Context<'_>,
2235    item: &clean::Item,
2236    render_mode: RenderMode,
2237) -> impl fmt::Display {
2238    let tcx = cx.tcx();
2239
2240    fmt::from_fn(move |w| {
2241        // FIXME: Once https://github.com/rust-lang/rust/issues/143874 is implemented, we can remove
2242        // this condition.
2243        let const_stability = match render_mode {
2244            RenderMode::Normal => item.const_stability(tcx),
2245            RenderMode::ForDeref { .. } => None,
2246        };
2247        let src_href = cx.src_href(item);
2248        let stability = render_stability_since_raw_with_extra(
2249            item.stable_since(tcx),
2250            const_stability,
2251            if src_href.is_some() { "" } else { " rightside" },
2252        );
2253
2254        match (stability, src_href) {
2255            (Some(stability), Some(link)) => {
2256                write!(
2257                    w,
2258                    "<span class=\"rightside\">{stability} · <a class=\"src\" href=\"{link}\">Source</a></span>",
2259                )
2260            }
2261            (Some(stability), None) => {
2262                write!(w, "{stability}")
2263            }
2264            (None, Some(link)) => {
2265                write!(w, "<a class=\"src rightside\" href=\"{link}\">Source</a>")
2266            }
2267            (None, None) => Ok(()),
2268        }
2269    })
2270}
2271
2272fn render_impl_summary(
2273    cx: &Context<'_>,
2274    i: &Impl,
2275    parent: &clean::Item,
2276    show_def_docs: bool,
2277    use_absolute: Option<bool>,
2278    // This argument is used to reference same type with different paths to avoid duplication
2279    // in documentation pages for trait with automatic implementations like "Send" and "Sync".
2280    aliases: &[String],
2281    doc: Option<&str>,
2282    impl_is_empty: bool,
2283) -> impl fmt::Display {
2284    fmt::from_fn(move |w| {
2285        let inner_impl = i.inner_impl();
2286        let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
2287        let aliases = (!aliases.is_empty())
2288            .then_some(fmt::from_fn(|f| {
2289                write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f)))
2290            }))
2291            .maybe_display();
2292        write!(
2293            w,
2294            "<section id=\"{id}\" class=\"impl\"{aliases}>\
2295                {}\
2296                <a href=\"#{id}\" class=\"anchor\">§</a>\
2297                <h3 class=\"code-header\">",
2298            render_rightside(cx, &i.impl_item, RenderMode::Normal)
2299        )?;
2300
2301        if let Some(use_absolute) = use_absolute {
2302            write!(w, "{}", print_impl(inner_impl, use_absolute, cx))?;
2303            if show_def_docs {
2304                for it in &inner_impl.items {
2305                    if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
2306                        write!(
2307                            w,
2308                            "<div class=\"where\">  {};</div>",
2309                            assoc_type(
2310                                it,
2311                                &tydef.generics,
2312                                &[], // intentionally leaving out bounds
2313                                Some(&tydef.type_),
2314                                AssocItemLink::Anchor(None),
2315                                0,
2316                                cx,
2317                            )
2318                        )?;
2319                    }
2320                }
2321            }
2322        } else {
2323            write!(w, "{}", print_impl(inner_impl, false, cx))?;
2324        }
2325        w.write_str("</h3>")?;
2326
2327        let is_trait = inner_impl.trait_.is_some();
2328        if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) {
2329            write!(
2330                w,
2331                "<span class=\"item-info\">\
2332                    <div class=\"stab portability\">{portability}</div>\
2333                </span>",
2334            )?;
2335        }
2336
2337        if let Some(doc) = doc {
2338            if impl_is_empty {
2339                w.write_str(
2340                    "<div class=\"item-info\">\
2341                         <div class=\"stab empty-impl\">This impl block contains no items.</div>\
2342                     </div>",
2343                )?;
2344            }
2345            write!(w, "<div class=\"docblock\">{doc}</div>")?;
2346        }
2347
2348        w.write_str("</section>")
2349    })
2350}
2351
2352pub(crate) fn small_url_encode(s: String) -> String {
2353    // These characters don't need to be escaped in a URI.
2354    // See https://url.spec.whatwg.org/#query-percent-encode-set
2355    // and https://url.spec.whatwg.org/#urlencoded-parsing
2356    // and https://url.spec.whatwg.org/#url-code-points
2357    fn dont_escape(c: u8) -> bool {
2358        c.is_ascii_alphanumeric()
2359            || c == b'-'
2360            || c == b'_'
2361            || c == b'.'
2362            || c == b','
2363            || c == b'~'
2364            || c == b'!'
2365            || c == b'\''
2366            || c == b'('
2367            || c == b')'
2368            || c == b'*'
2369            || c == b'/'
2370            || c == b';'
2371            || c == b':'
2372            || c == b'?'
2373            // As described in urlencoded-parsing, the
2374            // first `=` is the one that separates key from
2375            // value. Following `=`s are part of the value.
2376            || c == b'='
2377    }
2378    let mut st = String::new();
2379    let mut last_match = 0;
2380    for (idx, b) in s.bytes().enumerate() {
2381        if dont_escape(b) {
2382            continue;
2383        }
2384
2385        if last_match != idx {
2386            // Invariant: `idx` must be the first byte in a character at this point.
2387            st += &s[last_match..idx];
2388        }
2389        if b == b' ' {
2390            // URL queries are decoded with + replaced with SP.
2391            // While the same is not true for hashes, rustdoc only needs to be
2392            // consistent with itself when encoding them.
2393            st += "+";
2394        } else {
2395            write!(st, "%{b:02X}").unwrap();
2396        }
2397        // Invariant: if the current byte is not at the start of a multi-byte character,
2398        // we need to get down here so that when the next turn of the loop comes around,
2399        // last_match winds up equalling idx.
2400        //
2401        // In other words, dont_escape must always return `false` in multi-byte character.
2402        last_match = idx + 1;
2403    }
2404
2405    if last_match != 0 {
2406        st += &s[last_match..];
2407        st
2408    } else {
2409        s
2410    }
2411}
2412
2413fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String {
2414    use rustc_middle::ty::print::with_forced_trimmed_paths;
2415    let (type_, trait_) = match impl_id {
2416        ItemId::Auto { trait_, for_ } => {
2417            let ty = tcx.type_of(for_).skip_binder();
2418            (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
2419        }
2420        ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
2421            if let Some(trait_ref) = tcx.impl_opt_trait_ref(impl_id) {
2422                let trait_ref = trait_ref.skip_binder();
2423                (trait_ref.self_ty(), Some(trait_ref))
2424            } else {
2425                (tcx.type_of(impl_id).skip_binder(), None)
2426            }
2427        }
2428    };
2429    with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
2430        format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
2431    } else {
2432        format!("impl-{type_}")
2433    }))
2434}
2435
2436fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2437    match item.kind {
2438        clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
2439            // Alternative format produces no URLs,
2440            // so this parameter does nothing.
2441            Some((
2442                format!("{:#}", print_type(&i.for_, cx)),
2443                get_id_for_impl(cx.tcx(), item.item_id),
2444            ))
2445        }
2446        _ => None,
2447    }
2448}
2449
2450/// Returns the list of implementations for the primitive reference type, filtering out any
2451/// implementations that are on concrete or partially generic types, only keeping implementations
2452/// of the form `impl<T> Trait for &T`.
2453pub(crate) fn get_filtered_impls_for_reference<'a>(
2454    shared: &'a SharedContext<'_>,
2455    it: &clean::Item,
2456) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
2457    let def_id = it.item_id.expect_def_id();
2458    // If the reference primitive is somehow not defined, exit early.
2459    let Some(v) = shared.cache.impls.get(&def_id) else {
2460        return (Vec::new(), Vec::new(), Vec::new());
2461    };
2462    // Since there is no "direct implementation" on the reference primitive type, we filter out
2463    // every implementation which isn't a trait implementation.
2464    let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
2465    let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
2466        traits.partition(|t| t.inner_impl().kind.is_auto());
2467
2468    let (blanket_impl, concrete): (Vec<&Impl>, _) =
2469        concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
2470    // Now we keep only references over full generic types.
2471    let concrete: Vec<_> = concrete
2472        .into_iter()
2473        .filter(|t| match t.inner_impl().for_ {
2474            clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
2475            _ => false,
2476        })
2477        .collect();
2478
2479    (concrete, synthetic, blanket_impl)
2480}
2481
2482#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2483pub(crate) enum ItemSection {
2484    Reexports,
2485    PrimitiveTypes,
2486    Modules,
2487    Macros,
2488    Structs,
2489    Enums,
2490    Constants,
2491    Statics,
2492    Traits,
2493    Functions,
2494    TypeAliases,
2495    Unions,
2496    Implementations,
2497    TypeMethods,
2498    Methods,
2499    StructFields,
2500    Variants,
2501    AssociatedTypes,
2502    AssociatedConstants,
2503    ForeignTypes,
2504    Keywords,
2505    Attributes,
2506    AttributeMacros,
2507    DeriveMacros,
2508    TraitAliases,
2509}
2510
2511impl ItemSection {
2512    const ALL: &'static [Self] = {
2513        use ItemSection::*;
2514        // NOTE: The order here affects the order in the UI.
2515        // Keep this synchronized with addSidebarItems in main.js
2516        &[
2517            Reexports,
2518            PrimitiveTypes,
2519            Modules,
2520            Macros,
2521            Structs,
2522            Enums,
2523            Constants,
2524            Statics,
2525            Traits,
2526            Functions,
2527            TypeAliases,
2528            Unions,
2529            Implementations,
2530            TypeMethods,
2531            Methods,
2532            StructFields,
2533            Variants,
2534            AssociatedTypes,
2535            AssociatedConstants,
2536            ForeignTypes,
2537            Keywords,
2538            Attributes,
2539            AttributeMacros,
2540            DeriveMacros,
2541            TraitAliases,
2542        ]
2543    };
2544
2545    fn id(self) -> &'static str {
2546        match self {
2547            Self::Reexports => "reexports",
2548            Self::Modules => "modules",
2549            Self::Structs => "structs",
2550            Self::Unions => "unions",
2551            Self::Enums => "enums",
2552            Self::Functions => "functions",
2553            Self::TypeAliases => "types",
2554            Self::Statics => "statics",
2555            Self::Constants => "constants",
2556            Self::Traits => "traits",
2557            Self::Implementations => "impls",
2558            Self::TypeMethods => "tymethods",
2559            Self::Methods => "methods",
2560            Self::StructFields => "fields",
2561            Self::Variants => "variants",
2562            Self::Macros => "macros",
2563            Self::PrimitiveTypes => "primitives",
2564            Self::AssociatedTypes => "associated-types",
2565            Self::AssociatedConstants => "associated-consts",
2566            Self::ForeignTypes => "foreign-types",
2567            Self::Keywords => "keywords",
2568            Self::Attributes => "attributes",
2569            Self::AttributeMacros => "attributes",
2570            Self::DeriveMacros => "derives",
2571            Self::TraitAliases => "trait-aliases",
2572        }
2573    }
2574
2575    fn name(self) -> &'static str {
2576        match self {
2577            Self::Reexports => "Re-exports",
2578            Self::Modules => "Modules",
2579            Self::Structs => "Structs",
2580            Self::Unions => "Unions",
2581            Self::Enums => "Enums",
2582            Self::Functions => "Functions",
2583            Self::TypeAliases => "Type Aliases",
2584            Self::Statics => "Statics",
2585            Self::Constants => "Constants",
2586            Self::Traits => "Traits",
2587            Self::Implementations => "Implementations",
2588            Self::TypeMethods => "Type Methods",
2589            Self::Methods => "Methods",
2590            Self::StructFields => "Struct Fields",
2591            Self::Variants => "Variants",
2592            Self::Macros => "Macros",
2593            Self::PrimitiveTypes => "Primitive Types",
2594            Self::AssociatedTypes => "Associated Types",
2595            Self::AssociatedConstants => "Associated Constants",
2596            Self::ForeignTypes => "Foreign Types",
2597            Self::Keywords => "Keywords",
2598            Self::Attributes => "Attributes",
2599            Self::AttributeMacros => "Attribute Macros",
2600            Self::DeriveMacros => "Derive Macros",
2601            Self::TraitAliases => "Trait Aliases",
2602        }
2603    }
2604}
2605
2606fn item_ty_to_section(ty: ItemType) -> ItemSection {
2607    match ty {
2608        ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
2609        ItemType::Module => ItemSection::Modules,
2610        ItemType::Struct => ItemSection::Structs,
2611        ItemType::Union => ItemSection::Unions,
2612        ItemType::Enum => ItemSection::Enums,
2613        ItemType::Function => ItemSection::Functions,
2614        ItemType::TypeAlias => ItemSection::TypeAliases,
2615        ItemType::Static => ItemSection::Statics,
2616        ItemType::Constant => ItemSection::Constants,
2617        ItemType::Trait => ItemSection::Traits,
2618        ItemType::Impl => ItemSection::Implementations,
2619        ItemType::TyMethod => ItemSection::TypeMethods,
2620        ItemType::Method => ItemSection::Methods,
2621        ItemType::StructField => ItemSection::StructFields,
2622        ItemType::Variant => ItemSection::Variants,
2623        ItemType::Macro => ItemSection::Macros,
2624        ItemType::Primitive => ItemSection::PrimitiveTypes,
2625        ItemType::AssocType => ItemSection::AssociatedTypes,
2626        ItemType::AssocConst => ItemSection::AssociatedConstants,
2627        ItemType::ForeignType => ItemSection::ForeignTypes,
2628        ItemType::Keyword => ItemSection::Keywords,
2629        ItemType::Attribute => ItemSection::Attributes,
2630        ItemType::ProcAttribute => ItemSection::AttributeMacros,
2631        ItemType::ProcDerive => ItemSection::DeriveMacros,
2632        ItemType::TraitAlias => ItemSection::TraitAliases,
2633    }
2634}
2635
2636/// Returns a list of all paths used in the type.
2637/// This is used to help deduplicate imported impls
2638/// for reexported types. If any of the contained
2639/// types are re-exported, we don't use the corresponding
2640/// entry from the js file, as inlining will have already
2641/// picked up the impl
2642fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec<String> {
2643    let mut out = Vec::new();
2644    let mut visited = FxHashSet::default();
2645    let mut work = VecDeque::new();
2646
2647    let mut process_path = |did: DefId| {
2648        let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
2649        let fqp = cache.exact_paths.get(&did).or_else(get_extern);
2650
2651        if let Some(path) = fqp {
2652            out.push(join_path_syms(path));
2653        }
2654    };
2655
2656    work.push_back(first_ty);
2657
2658    while let Some(ty) = work.pop_front() {
2659        if !visited.insert(ty) {
2660            continue;
2661        }
2662
2663        match ty {
2664            clean::Type::Path { path } => process_path(path.def_id()),
2665            clean::Type::Tuple(tys) => {
2666                work.extend(tys.iter());
2667            }
2668            clean::Type::Slice(ty) => {
2669                work.push_back(ty);
2670            }
2671            clean::Type::Array(ty, _) => {
2672                work.push_back(ty);
2673            }
2674            clean::Type::RawPointer(_, ty) => {
2675                work.push_back(ty);
2676            }
2677            clean::Type::BorrowedRef { type_, .. } => {
2678                work.push_back(type_);
2679            }
2680            clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
2681                work.push_back(self_type);
2682                if let Some(trait_) = trait_ {
2683                    process_path(trait_.def_id());
2684                }
2685            }
2686            _ => {}
2687        }
2688    }
2689    out
2690}
2691
2692const MAX_FULL_EXAMPLES: usize = 5;
2693const NUM_VISIBLE_LINES: usize = 10;
2694
2695/// Generates the HTML for example call locations generated via the --scrape-examples flag.
2696fn render_call_locations<W: fmt::Write>(
2697    mut w: W,
2698    cx: &Context<'_>,
2699    item: &clean::Item,
2700) -> fmt::Result {
2701    let tcx = cx.tcx();
2702    let def_id = item.item_id.expect_def_id();
2703    let key = tcx.def_path_hash(def_id);
2704    let Some(call_locations) = cx.shared.call_locations.get(&key) else { return Ok(()) };
2705
2706    // Generate a unique ID so users can link to this section for a given method
2707    let id = cx.derive_id("scraped-examples");
2708    write!(
2709        &mut w,
2710        "<div class=\"docblock scraped-example-list\">\
2711          <span></span>\
2712          <h5 id=\"{id}\">\
2713             <a href=\"#{id}\">Examples found in repository</a>\
2714             <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
2715          </h5>",
2716        root_path = cx.root_path(),
2717        id = id
2718    )?;
2719
2720    // Create a URL to a particular location in a reverse-dependency's source file
2721    let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
2722        let (line_lo, line_hi) = loc.call_expr.line_span;
2723        let (anchor, title) = if line_lo == line_hi {
2724            ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
2725        } else {
2726            (
2727                format!("{}-{}", line_lo + 1, line_hi + 1),
2728                format!("lines {}-{}", line_lo + 1, line_hi + 1),
2729            )
2730        };
2731        let url = format!("{}{}#{anchor}", cx.root_path(), call_data.url);
2732        (url, title)
2733    };
2734
2735    // Generate the HTML for a single example, being the title and code block
2736    let write_example = |w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
2737        let contents = match fs::read_to_string(path) {
2738            Ok(contents) => contents,
2739            Err(err) => {
2740                let span = item.span(tcx).map_or(DUMMY_SP, |span| span.inner());
2741                tcx.dcx().span_err(span, format!("failed to read file {}: {err}", path.display()));
2742                return false;
2743            }
2744        };
2745
2746        // To reduce file sizes, we only want to embed the source code needed to understand the example, not
2747        // the entire file. So we find the smallest byte range that covers all items enclosing examples.
2748        assert!(!call_data.locations.is_empty());
2749        let min_loc =
2750            call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2751        let byte_min = min_loc.enclosing_item.byte_span.0;
2752        let line_min = min_loc.enclosing_item.line_span.0;
2753        let max_loc =
2754            call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
2755        let byte_max = max_loc.enclosing_item.byte_span.1;
2756        let line_max = max_loc.enclosing_item.line_span.1;
2757
2758        // The output code is limited to that byte range.
2759        let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
2760
2761        // The call locations need to be updated to reflect that the size of the program has changed.
2762        // Specifically, the ranges are all subtracted by `byte_min` since that's the new zero point.
2763        let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
2764            .locations
2765            .iter()
2766            .map(|loc| {
2767                let (byte_lo, byte_hi) = loc.call_ident.byte_span;
2768                let (line_lo, line_hi) = loc.call_expr.line_span;
2769                let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
2770
2771                let line_range = (line_lo - line_min, line_hi - line_min);
2772                let (line_url, line_title) = link_to_loc(call_data, loc);
2773
2774                (byte_range, (line_range, line_url, line_title))
2775            })
2776            .unzip();
2777
2778        let (_, init_url, init_title) = &line_ranges[0];
2779        let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
2780        let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
2781
2782        let source_map = tcx.sess.source_map();
2783        let files = source_map.files();
2784        let local = tcx.sess.local_crate_source_file().unwrap();
2785
2786        let get_file_start_pos = || {
2787            let crate_src = local.clone().into_local_path()?;
2788            let abs_crate_src = crate_src.canonicalize().ok()?;
2789            let crate_root = abs_crate_src.parent()?.parent()?;
2790            let rel_path = path.strip_prefix(crate_root).ok()?;
2791            files
2792                .iter()
2793                .find(|file| match &file.name {
2794                    FileName::Real(real) => real.local_path().map_or(false, |p| p == rel_path),
2795                    _ => false,
2796                })
2797                .map(|file| file.start_pos)
2798        };
2799
2800        // Look for the example file in the source map if it exists, otherwise
2801        // return a span to the local crate's source file
2802        let Some(file_span) = get_file_start_pos()
2803            .or_else(|| {
2804                files
2805                    .iter()
2806                    .find(|file| match &file.name {
2807                        FileName::Real(file_name) => file_name == &local,
2808                        _ => false,
2809                    })
2810                    .map(|file| file.start_pos)
2811            })
2812            .map(|start_pos| {
2813                rustc_span::Span::with_root_ctxt(
2814                    start_pos + BytePos(byte_min),
2815                    start_pos + BytePos(byte_max),
2816                )
2817            })
2818        else {
2819            // if the fallback span can't be built, don't render the code for this example
2820            return false;
2821        };
2822
2823        let mut decoration_info = FxIndexMap::default();
2824        decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
2825        decoration_info.insert("highlight", byte_ranges);
2826
2827        sources::print_src(
2828            w,
2829            contents_subset,
2830            file_span,
2831            cx,
2832            &cx.root_path(),
2833            &highlight::DecorationInfo(decoration_info),
2834            &sources::SourceContext::Embedded(sources::ScrapedInfo {
2835                needs_expansion,
2836                offset: line_min,
2837                name: &call_data.display_name,
2838                url: init_url,
2839                title: init_title,
2840                locations: locations_encoded,
2841            }),
2842        )
2843        .unwrap();
2844
2845        true
2846    };
2847
2848    // The call locations are output in sequence, so that sequence needs to be determined.
2849    // Ideally the most "relevant" examples would be shown first, but there's no general algorithm
2850    // for determining relevance. We instead proxy relevance with the following heuristics:
2851    //   1. Code written to be an example is better than code not written to be an example, e.g.
2852    //      a snippet from examples/foo.rs is better than src/lib.rs. We don't know the Cargo
2853    //      directory structure in Rustdoc, so we proxy this by prioritizing code that comes from
2854    //      a --crate-type bin.
2855    //   2. Smaller examples are better than large examples. So we prioritize snippets that have
2856    //      the smallest number of lines in their enclosing item.
2857    //   3. Finally we sort by the displayed file name, which is arbitrary but prevents the
2858    //      ordering of examples from randomly changing between Rustdoc invocations.
2859    let ordered_locations = {
2860        fn sort_criterion<'a>(
2861            (_, call_data): &(&PathBuf, &'a CallData),
2862        ) -> (bool, u32, &'a String) {
2863            // Use the first location because that's what the user will see initially
2864            let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2865            (!call_data.is_bin, hi - lo, &call_data.display_name)
2866        }
2867
2868        let mut locs = call_locations.iter().collect::<Vec<_>>();
2869        locs.sort_by_key(sort_criterion);
2870        locs
2871    };
2872
2873    let mut it = ordered_locations.into_iter().peekable();
2874
2875    // An example may fail to write if its source can't be read for some reason, so this method
2876    // continues iterating until a write succeeds
2877    let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
2878        for example in it.by_ref() {
2879            if write_example(&mut *w, example) {
2880                break;
2881            }
2882        }
2883    };
2884
2885    // Write just one example that's visible by default in the method's description.
2886    write_and_skip_failure(&mut w, &mut it);
2887
2888    // Then add the remaining examples in a hidden section.
2889    if it.peek().is_some() {
2890        write!(
2891            w,
2892            "<details class=\"toggle more-examples-toggle\">\
2893                  <summary class=\"hideme\">\
2894                     <span>More examples</span>\
2895                  </summary>\
2896                  <div class=\"hide-more\">Hide additional examples</div>\
2897                  <div class=\"more-scraped-examples\">\
2898                    <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
2899        )?;
2900
2901        // Only generate inline code for MAX_FULL_EXAMPLES number of examples. Otherwise we could
2902        // make the page arbitrarily huge!
2903        for _ in 0..MAX_FULL_EXAMPLES {
2904            write_and_skip_failure(&mut w, &mut it);
2905        }
2906
2907        // For the remaining examples, generate a <ul> containing links to the source files.
2908        if it.peek().is_some() {
2909            w.write_str(
2910                r#"<div class="example-links">Additional examples can be found in:<br><ul>"#,
2911            )?;
2912            it.try_for_each(|(_, call_data)| {
2913                let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
2914                write!(
2915                    w,
2916                    r#"<li><a href="{url}">{name}</a></li>"#,
2917                    url = url,
2918                    name = call_data.display_name
2919                )
2920            })?;
2921            w.write_str("</ul></div>")?;
2922        }
2923
2924        w.write_str("</div></details>")?;
2925    }
2926
2927    w.write_str("</div>")
2928}
2929
2930fn render_attributes_in_code(
2931    w: &mut impl fmt::Write,
2932    item: &clean::Item,
2933    prefix: &str,
2934    cx: &Context<'_>,
2935) -> fmt::Result {
2936    render_attributes_in_code_with_options(w, item, prefix, cx, true, "")
2937}
2938
2939pub(super) fn render_attributes_in_code_with_options(
2940    w: &mut impl fmt::Write,
2941    item: &clean::Item,
2942    prefix: &str,
2943    cx: &Context<'_>,
2944    render_doc_hidden: bool,
2945    open_tag: &str,
2946) -> fmt::Result {
2947    w.write_str(open_tag)?;
2948    if render_doc_hidden && item.is_doc_hidden() {
2949        render_code_attribute(prefix, "#[doc(hidden)]", w)?;
2950    }
2951    for attr in &item.attrs.other_attrs {
2952        let hir::Attribute::Parsed(kind) = attr else { continue };
2953        let attr = match kind {
2954            AttributeKind::LinkSection { name, .. } => {
2955                Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}"))))
2956            }
2957            AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"),
2958            AttributeKind::ExportName { name, .. } => {
2959                Cow::Owned(format!("#[unsafe(export_name = {})]", Escape(&format!("{name:?}"))))
2960            }
2961            AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"),
2962            _ => continue,
2963        };
2964        render_code_attribute(prefix, attr.as_ref(), w)?;
2965    }
2966
2967    if let Some(def_id) = item.def_id()
2968        && let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id)
2969    {
2970        render_code_attribute(prefix, &repr, w)?;
2971    }
2972    Ok(())
2973}
2974
2975fn render_repr_attribute_in_code(
2976    w: &mut impl fmt::Write,
2977    cx: &Context<'_>,
2978    def_id: DefId,
2979) -> fmt::Result {
2980    if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) {
2981        render_code_attribute("", &repr, w)?;
2982    }
2983    Ok(())
2984}
2985
2986fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) -> fmt::Result {
2987    write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>")
2988}
2989
2990/// Compute the *public* `#[repr]` of the item given by `DefId`.
2991///
2992/// Read more about it here:
2993/// <https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type>.
2994fn repr_attribute<'tcx>(
2995    tcx: TyCtxt<'tcx>,
2996    cache: &Cache,
2997    def_id: DefId,
2998) -> Option<Cow<'static, str>> {
2999    let adt = match tcx.def_kind(def_id) {
3000        DefKind::Struct | DefKind::Enum | DefKind::Union => tcx.adt_def(def_id),
3001        _ => return None,
3002    };
3003    let repr = adt.repr();
3004
3005    let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id);
3006    let is_public_field = |field: &ty::FieldDef| {
3007        (cache.document_private || field.vis.is_public()) && is_visible(field.did)
3008    };
3009
3010    if repr.transparent() {
3011        // The transparent repr is public iff the non-1-ZST field is public and visible or
3012        // – in case all fields are 1-ZST fields — at least one field is public and visible.
3013        let is_public = 'is_public: {
3014            // `#[repr(transparent)]` can only be applied to structs and single-variant enums.
3015            let var = adt.variant(rustc_abi::FIRST_VARIANT); // the first and only variant
3016
3017            if !is_visible(var.def_id) {
3018                break 'is_public false;
3019            }
3020
3021            // Side note: There can only ever be one or zero non-1-ZST fields.
3022            let non_1zst_field = var.fields.iter().find(|field| {
3023                let ty = ty::TypingEnv::post_analysis(tcx, field.did)
3024                    .as_query_input(tcx.type_of(field.did).instantiate_identity());
3025                tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst())
3026            });
3027
3028            match non_1zst_field {
3029                Some(field) => is_public_field(field),
3030                None => var.fields.is_empty() || var.fields.iter().any(is_public_field),
3031            }
3032        };
3033
3034        // Since the transparent repr can't have any other reprs or
3035        // repr modifiers beside it, we can safely return early here.
3036        return is_public.then(|| "#[repr(transparent)]".into());
3037    }
3038
3039    // Fast path which avoids looking through the variants and fields in
3040    // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
3041    // FIXME: This check is not very robust / forward compatible!
3042    if !repr.c()
3043        && !repr.simd()
3044        && repr.int.is_none()
3045        && repr.pack.is_none()
3046        && repr.align.is_none()
3047    {
3048        return None;
3049    }
3050
3051    // The repr is public iff all components are public and visible.
3052    let is_public = adt
3053        .variants()
3054        .iter()
3055        .all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field));
3056    if !is_public {
3057        return None;
3058    }
3059
3060    let mut result = Vec::<Cow<'_, _>>::new();
3061
3062    if repr.c() {
3063        result.push("C".into());
3064    }
3065    if repr.simd() {
3066        result.push("simd".into());
3067    }
3068    if let Some(int) = repr.int {
3069        let prefix = if int.is_signed() { 'i' } else { 'u' };
3070        let int = match int {
3071            rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"),
3072            rustc_abi::IntegerType::Fixed(int, _) => {
3073                format!("{prefix}{}", int.size().bytes() * 8)
3074            }
3075        };
3076        result.push(int.into());
3077    }
3078
3079    // Render modifiers last.
3080    if let Some(pack) = repr.pack {
3081        result.push(format!("packed({})", pack.bytes()).into());
3082    }
3083    if let Some(align) = repr.align {
3084        result.push(format!("align({})", align.bytes()).into());
3085    }
3086
3087    (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into())
3088}