Skip to main content

rustdoc/html/render/
mod.rs

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