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 = traits.iter().find(|t| {
1521            t.trait_did() == cx.tcx().lang_items().deref_trait() && !t.is_negative_trait_impl()
1522        });
1523        if let Some(impl_) = deref_impl {
1524            let has_deref_mut =
1525                traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
1526            render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs)?;
1527        }
1528
1529        // If we were already one level into rendering deref methods, we don't want to render
1530        // anything after recursing into any further deref methods above.
1531        if let AssocItemRender::DerefFor { .. } = what {
1532            return Ok(());
1533        }
1534
1535        let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
1536            traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
1537        let (blanket_impl, concrete): (Vec<&Impl>, _) =
1538            concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
1539
1540        render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl)?;
1541    }
1542    Ok(())
1543}
1544
1545/// `derefs` is the set of all deref targets that have already been handled.
1546fn render_deref_methods(
1547    mut w: impl Write,
1548    cx: &Context<'_>,
1549    impl_: &Impl,
1550    container_item: &clean::Item,
1551    deref_mut: bool,
1552    derefs: &mut DefIdSet,
1553) -> fmt::Result {
1554    let cache = cx.cache();
1555    let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
1556    let (target, real_target) = impl_
1557        .inner_impl()
1558        .items
1559        .iter()
1560        .find_map(|item| match item.kind {
1561            clean::AssocTypeItem(box ref t, _) => Some(match *t {
1562                clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
1563                _ => (&t.type_, &t.type_),
1564            }),
1565            _ => None,
1566        })
1567        .expect("Expected associated type binding");
1568    debug!(
1569        "Render deref methods for {for_:#?}, target {target:#?}",
1570        for_ = impl_.inner_impl().for_
1571    );
1572    let what =
1573        AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
1574    if let Some(did) = target.def_id(cache) {
1575        if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) {
1576            // `impl Deref<Target = S> for S`
1577            if did == type_did || !derefs.insert(did) {
1578                // Avoid infinite cycles
1579                return Ok(());
1580            }
1581        }
1582        render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1583    } else if let Some(prim) = target.primitive_type()
1584        && let Some(&did) = cache.primitive_locations.get(&prim)
1585    {
1586        render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1587    }
1588    Ok(())
1589}
1590
1591fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool {
1592    let self_type_opt = match item.kind {
1593        clean::MethodItem(ref method, _) => method.decl.receiver_type(),
1594        clean::RequiredMethodItem(ref method, _) => method.decl.receiver_type(),
1595        _ => None,
1596    };
1597
1598    if let Some(self_ty) = self_type_opt {
1599        let (by_mut_ref, by_box, by_value) = match *self_ty {
1600            clean::Type::BorrowedRef { mutability, .. } => {
1601                (mutability == Mutability::Mut, false, false)
1602            }
1603            clean::Type::Path { ref path } => {
1604                (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false)
1605            }
1606            clean::Type::SelfTy => (false, false, true),
1607            _ => (false, false, false),
1608        };
1609
1610        (deref_mut_ || !by_mut_ref) && !by_box && !by_value
1611    } else {
1612        false
1613    }
1614}
1615
1616fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option<impl fmt::Display> {
1617    if ty.is_unit() {
1618        // Very common fast path.
1619        return None;
1620    }
1621
1622    let did = ty.def_id(cx.cache())?;
1623
1624    // Box has pass-through impls for Read, Write, Iterator, and Future when the
1625    // boxed type implements one of those. We don't want to treat every Box return
1626    // as being notably an Iterator (etc), though, so we exempt it. Pin has the same
1627    // issue, with a pass-through impl for Future.
1628    if Some(did) == cx.tcx().lang_items().owned_box()
1629        || Some(did) == cx.tcx().lang_items().pin_type()
1630    {
1631        return None;
1632    }
1633
1634    let impls = cx.cache().impls.get(&did)?;
1635    let has_notable_trait = impls
1636        .iter()
1637        .map(Impl::inner_impl)
1638        .filter(|impl_| {
1639            impl_.polarity == ty::ImplPolarity::Positive
1640                // Two different types might have the same did,
1641                // without actually being the same.
1642                && ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1643        })
1644        .filter_map(|impl_| impl_.trait_.as_ref())
1645        .filter_map(|trait_| cx.cache().traits.get(&trait_.def_id()))
1646        .any(|t| t.is_notable_trait(cx.tcx()));
1647
1648    has_notable_trait.then(|| {
1649        cx.types_with_notable_traits.borrow_mut().insert(ty.clone());
1650        fmt::from_fn(|f| {
1651            write!(
1652                f,
1653                " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>",
1654                ty = Escape(&format!("{:#}", print_type(ty, cx))),
1655            )
1656        })
1657    })
1658}
1659
1660fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
1661    let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
1662
1663    let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");
1664
1665    let out = fmt::from_fn(|f| {
1666        let mut notable_impls = impls
1667            .iter()
1668            .map(|impl_| impl_.inner_impl())
1669            .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive)
1670            .filter(|impl_| {
1671                // Two different types might have the same did, without actually being the same.
1672                ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1673            })
1674            .filter_map(|impl_| {
1675                if let Some(trait_) = &impl_.trait_
1676                    && let trait_did = trait_.def_id()
1677                    && let Some(trait_) = cx.cache().traits.get(&trait_did)
1678                    && trait_.is_notable_trait(cx.tcx())
1679                {
1680                    Some((impl_, trait_did))
1681                } else {
1682                    None
1683                }
1684            })
1685            .peekable();
1686
1687        let has_notable_impl = if let Some((impl_, _)) = notable_impls.peek() {
1688            write!(
1689                f,
1690                "<h3>Notable traits for <code>{}</code></h3>\
1691                <pre><code>",
1692                print_type(&impl_.for_, cx),
1693            )?;
1694            true
1695        } else {
1696            false
1697        };
1698
1699        for (impl_, trait_did) in notable_impls {
1700            write!(f, "<div class=\"where\">{}</div>", print_impl(impl_, false, cx))?;
1701            for it in &impl_.items {
1702                let clean::AssocTypeItem(tydef, ..) = &it.kind else {
1703                    continue;
1704                };
1705
1706                let empty_set = FxIndexSet::default();
1707                let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
1708
1709                write!(
1710                    f,
1711                    "<div class=\"where\">    {};</div>",
1712                    assoc_type(
1713                        it,
1714                        &tydef.generics,
1715                        &[], // intentionally leaving out bounds
1716                        Some(&tydef.type_),
1717                        src_link,
1718                        0,
1719                        cx,
1720                    )
1721                )?;
1722            }
1723        }
1724
1725        if !has_notable_impl {
1726            f.write_str("</code></pre>")?;
1727        }
1728
1729        Ok(())
1730    })
1731    .to_string();
1732
1733    (format!("{:#}", print_type(ty, cx)), out)
1734}
1735
1736fn notable_traits_json<'a>(tys: impl Iterator<Item = &'a clean::Type>, cx: &Context<'_>) -> String {
1737    let mut mp = tys.map(|ty| notable_traits_decl(ty, cx)).collect::<IndexMap<_, _>>();
1738    mp.sort_unstable_keys();
1739    serde_json::to_string(&mp).expect("serialize (string, string) -> json object cannot fail")
1740}
1741
1742#[derive(Clone, Copy, Debug)]
1743struct ImplRenderingParameters {
1744    show_def_docs: bool,
1745    show_default_items: bool,
1746    /// Whether or not to show methods.
1747    show_non_assoc_items: bool,
1748    toggle_open_by_default: bool,
1749}
1750
1751fn render_impl(
1752    cx: &Context<'_>,
1753    i: &Impl,
1754    parent: &clean::Item,
1755    link: AssocItemLink<'_>,
1756    render_mode: RenderMode,
1757    use_absolute: Option<bool>,
1758    aliases: &[String],
1759    rendering_params: ImplRenderingParameters,
1760) -> impl fmt::Display {
1761    fmt::from_fn(move |w| {
1762        let cache = &cx.shared.cache;
1763        let traits = &cache.traits;
1764        let trait_ = i.trait_did().map(|did| &traits[&did]);
1765        let mut close_tags = <Vec<&str>>::with_capacity(2);
1766
1767        // For trait implementations, the `interesting` output contains all methods that have doc
1768        // comments, and the `boring` output contains all methods that do not. The distinction is
1769        // used to allow hiding the boring methods.
1770        // `containing_item` is used for rendering stability info. If the parent is a trait impl,
1771        // `containing_item` will the grandparent, since trait impls can't have stability attached.
1772        fn doc_impl_item(
1773            boring: impl fmt::Write,
1774            interesting: impl fmt::Write,
1775            cx: &Context<'_>,
1776            item: &clean::Item,
1777            parent: &clean::Item,
1778            link: AssocItemLink<'_>,
1779            render_mode: RenderMode,
1780            is_default_item: bool,
1781            trait_: Option<&clean::Trait>,
1782            rendering_params: ImplRenderingParameters,
1783        ) -> fmt::Result {
1784            let item_type = item.type_();
1785            let name = item.name.as_ref().unwrap();
1786
1787            let render_method_item = rendering_params.show_non_assoc_items
1788                && match render_mode {
1789                    RenderMode::Normal => true,
1790                    RenderMode::ForDeref { mut_: deref_mut_ } => {
1791                        should_render_item(item, deref_mut_, cx.tcx())
1792                    }
1793                };
1794
1795            let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
1796
1797            let mut doc_buffer = String::new();
1798            let mut info_buffer = String::new();
1799            let mut short_documented = true;
1800
1801            let mut trait_item_deprecated = false;
1802            if render_method_item {
1803                if !is_default_item {
1804                    if let Some(t) = trait_ {
1805                        // The trait item may have been stripped so we might not
1806                        // find any documentation or stability for it.
1807                        if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1808                            trait_item_deprecated = it.is_deprecated(cx.tcx());
1809                            // We need the stability of the item from the trait
1810                            // because impls can't have a stability.
1811                            if !item.doc_value().is_empty() {
1812                                document_item_info(cx, it, Some(parent))
1813                                    .render_into(&mut info_buffer)?;
1814                                doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1815                                short_documented = false;
1816                            } else {
1817                                // In case the item isn't documented,
1818                                // provide short documentation from the trait.
1819                                doc_buffer = document_short(
1820                                    it,
1821                                    cx,
1822                                    link,
1823                                    parent,
1824                                    rendering_params.show_def_docs,
1825                                )
1826                                .to_string();
1827                            }
1828                        }
1829                    } else {
1830                        document_item_info(cx, item, Some(parent)).render_into(&mut info_buffer)?;
1831                        if rendering_params.show_def_docs {
1832                            doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1833                            short_documented = false;
1834                        }
1835                    }
1836                } else {
1837                    doc_buffer =
1838                        document_short(item, cx, link, parent, rendering_params.show_def_docs)
1839                            .to_string();
1840                }
1841            }
1842            let mut w = if short_documented && trait_.is_some() {
1843                Either::Left(interesting)
1844            } else {
1845                Either::Right(boring)
1846            };
1847
1848            let mut deprecation_class = if trait_item_deprecated || item.is_deprecated(cx.tcx()) {
1849                " deprecated"
1850            } else {
1851                ""
1852            };
1853
1854            let toggled = !doc_buffer.is_empty();
1855            if toggled {
1856                let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
1857                write!(
1858                    w,
1859                    "<details class=\"toggle{method_toggle_class}{deprecation_class}\" open><summary>"
1860                )?;
1861                deprecation_class = "";
1862            }
1863            match &item.kind {
1864                clean::MethodItem(..) | clean::RequiredMethodItem(..) => {
1865                    // Only render when the method is not static or we allow static methods
1866                    if render_method_item {
1867                        let id = cx.derive_id(format!("{item_type}.{name}"));
1868                        let source_id = trait_
1869                            .and_then(|trait_| {
1870                                trait_
1871                                    .items
1872                                    .iter()
1873                                    .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
1874                            })
1875                            .map(|item| format!("{}.{name}", item.type_()));
1876                        write!(
1877                            w,
1878                            "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1879                                {}",
1880                            render_rightside(cx, item, render_mode)
1881                        )?;
1882                        if trait_.is_some() {
1883                            // Anchors are only used on trait impls.
1884                            write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1885                        }
1886                        write!(
1887                            w,
1888                            "<h4 class=\"code-header\">{}</h4></section>",
1889                            render_assoc_item(
1890                                item,
1891                                link.anchor(source_id.as_ref().unwrap_or(&id)),
1892                                ItemType::Impl,
1893                                cx,
1894                                render_mode,
1895                            ),
1896                        )?;
1897                    }
1898                }
1899                clean::RequiredAssocConstItem(generics, ty) => {
1900                    let source_id = format!("{item_type}.{name}");
1901                    let id = cx.derive_id(&source_id);
1902                    write!(
1903                        w,
1904                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1905                            {}",
1906                        render_rightside(cx, item, render_mode)
1907                    )?;
1908                    if trait_.is_some() {
1909                        // Anchors are only used on trait impls.
1910                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1911                    }
1912                    write!(
1913                        w,
1914                        "<h4 class=\"code-header\">{}</h4></section>",
1915                        assoc_const(
1916                            item,
1917                            generics,
1918                            ty,
1919                            AssocConstValue::None,
1920                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
1921                            0,
1922                            cx,
1923                        ),
1924                    )?;
1925                }
1926                clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
1927                    let source_id = format!("{item_type}.{name}");
1928                    let id = cx.derive_id(&source_id);
1929                    write!(
1930                        w,
1931                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1932                            {}",
1933                        render_rightside(cx, item, render_mode),
1934                    )?;
1935                    if trait_.is_some() {
1936                        // Anchors are only used on trait impls.
1937                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1938                    }
1939                    write!(
1940                        w,
1941                        "<h4 class=\"code-header\">{}</h4></section>",
1942                        assoc_const(
1943                            item,
1944                            &ci.generics,
1945                            &ci.type_,
1946                            match item.kind {
1947                                clean::ProvidedAssocConstItem(_) =>
1948                                    AssocConstValue::TraitDefault(&ci.kind),
1949                                clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
1950                                _ => unreachable!(),
1951                            },
1952                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
1953                            0,
1954                            cx,
1955                        ),
1956                    )?;
1957                }
1958                clean::RequiredAssocTypeItem(generics, bounds) => {
1959                    let source_id = format!("{item_type}.{name}");
1960                    let id = cx.derive_id(&source_id);
1961                    write!(
1962                        w,
1963                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1964                            {}",
1965                        render_rightside(cx, item, render_mode),
1966                    )?;
1967                    if trait_.is_some() {
1968                        // Anchors are only used on trait impls.
1969                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1970                    }
1971                    write!(
1972                        w,
1973                        "<h4 class=\"code-header\">{}</h4></section>",
1974                        assoc_type(
1975                            item,
1976                            generics,
1977                            bounds,
1978                            None,
1979                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
1980                            0,
1981                            cx,
1982                        ),
1983                    )?;
1984                }
1985                clean::AssocTypeItem(tydef, _bounds) => {
1986                    let source_id = format!("{item_type}.{name}");
1987                    let id = cx.derive_id(&source_id);
1988                    write!(
1989                        w,
1990                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1991                            {}",
1992                        render_rightside(cx, item, render_mode),
1993                    )?;
1994                    if trait_.is_some() {
1995                        // Anchors are only used on trait impls.
1996                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1997                    }
1998                    write!(
1999                        w,
2000                        "<h4 class=\"code-header\">{}</h4></section>",
2001                        assoc_type(
2002                            item,
2003                            &tydef.generics,
2004                            &[], // intentionally leaving out bounds
2005                            Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
2006                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
2007                            0,
2008                            cx,
2009                        ),
2010                    )?;
2011                }
2012                clean::StrippedItem(..) => return Ok(()),
2013                _ => panic!("can't make docs for trait item with name {:?}", item.name),
2014            }
2015
2016            w.write_str(&info_buffer)?;
2017            if toggled {
2018                write!(w, "</summary>{doc_buffer}</details>")?;
2019            }
2020            Ok(())
2021        }
2022
2023        let mut impl_items = String::new();
2024        let mut default_impl_items = String::new();
2025        let impl_ = i.inner_impl();
2026
2027        // Impl items are grouped by kinds:
2028        //
2029        // 1. Constants
2030        // 2. Types
2031        // 3. Functions
2032        //
2033        // This order is because you can have associated constants used in associated types (like array
2034        // length), and both in associated functions. So with this order, when reading from top to
2035        // bottom, you should see items definitions before they're actually used most of the time.
2036        let mut assoc_types = Vec::new();
2037        let mut methods = Vec::new();
2038
2039        if !impl_.is_negative_trait_impl() {
2040            for impl_item in &impl_.items {
2041                match impl_item.kind {
2042                    clean::MethodItem(..) | clean::RequiredMethodItem(..) => {
2043                        methods.push(impl_item)
2044                    }
2045                    clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => {
2046                        assoc_types.push(impl_item)
2047                    }
2048                    clean::RequiredAssocConstItem(..)
2049                    | clean::ProvidedAssocConstItem(_)
2050                    | clean::ImplAssocConstItem(_) => {
2051                        // We render it directly since they're supposed to come first.
2052                        doc_impl_item(
2053                            &mut default_impl_items,
2054                            &mut impl_items,
2055                            cx,
2056                            impl_item,
2057                            if trait_.is_some() { &i.impl_item } else { parent },
2058                            link,
2059                            render_mode,
2060                            false,
2061                            trait_,
2062                            rendering_params,
2063                        )?;
2064                    }
2065                    _ => {}
2066                }
2067            }
2068
2069            for assoc_type in assoc_types {
2070                doc_impl_item(
2071                    &mut default_impl_items,
2072                    &mut impl_items,
2073                    cx,
2074                    assoc_type,
2075                    if trait_.is_some() { &i.impl_item } else { parent },
2076                    link,
2077                    render_mode,
2078                    false,
2079                    trait_,
2080                    rendering_params,
2081                )?;
2082            }
2083            for method in methods {
2084                doc_impl_item(
2085                    &mut default_impl_items,
2086                    &mut impl_items,
2087                    cx,
2088                    method,
2089                    if trait_.is_some() { &i.impl_item } else { parent },
2090                    link,
2091                    render_mode,
2092                    false,
2093                    trait_,
2094                    rendering_params,
2095                )?;
2096            }
2097        }
2098
2099        fn render_default_items(
2100            mut boring: impl fmt::Write,
2101            mut interesting: impl fmt::Write,
2102            cx: &Context<'_>,
2103            t: &clean::Trait,
2104            i: &clean::Impl,
2105            parent: &clean::Item,
2106            render_mode: RenderMode,
2107            rendering_params: ImplRenderingParameters,
2108        ) -> fmt::Result {
2109            for trait_item in &t.items {
2110                // Skip over any default trait items that are impossible to reference
2111                // (e.g. if it has a `Self: Sized` bound on an unsized type).
2112                if let Some(impl_def_id) = parent.item_id.as_def_id()
2113                    && let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
2114                    && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id))
2115                {
2116                    continue;
2117                }
2118
2119                let n = trait_item.name;
2120                if i.items.iter().any(|m| m.name == n) {
2121                    continue;
2122                }
2123                let did = i.trait_.as_ref().unwrap().def_id();
2124                let provided_methods = i.provided_trait_methods(cx.tcx());
2125                let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
2126
2127                doc_impl_item(
2128                    &mut boring,
2129                    &mut interesting,
2130                    cx,
2131                    trait_item,
2132                    parent,
2133                    assoc_link,
2134                    render_mode,
2135                    true,
2136                    Some(t),
2137                    rendering_params,
2138                )?;
2139            }
2140            Ok(())
2141        }
2142
2143        // If we've implemented a trait, then also emit documentation for all
2144        // default items which weren't overridden in the implementation block.
2145        // We don't emit documentation for default items if they appear in the
2146        // Implementations on Foreign Types or Implementors sections.
2147        if rendering_params.show_default_items
2148            && let Some(t) = trait_
2149            && !impl_.is_negative_trait_impl()
2150        {
2151            render_default_items(
2152                &mut default_impl_items,
2153                &mut impl_items,
2154                cx,
2155                t,
2156                impl_,
2157                &i.impl_item,
2158                render_mode,
2159                rendering_params,
2160            )?;
2161        }
2162        if render_mode == RenderMode::Normal {
2163            let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
2164            let deprecation_attr = if impl_.is_deprecated
2165                || trait_.is_some_and(|trait_| trait_.is_deprecated(cx.tcx()))
2166            {
2167                " deprecated"
2168            } else {
2169                ""
2170            };
2171            if toggled {
2172                close_tags.push("</details>");
2173                write!(
2174                    w,
2175                    "<details class=\"toggle implementors-toggle{deprecation_attr}\"{}>\
2176                        <summary>",
2177                    if rendering_params.toggle_open_by_default { " open" } else { "" }
2178                )?;
2179            }
2180
2181            let (before_dox, after_dox) = i
2182                .impl_item
2183                .opt_doc_value()
2184                .map(|dox| {
2185                    Markdown {
2186                        content: &dox,
2187                        links: &i.impl_item.links(cx),
2188                        ids: &mut cx.id_map.borrow_mut(),
2189                        error_codes: cx.shared.codes,
2190                        edition: cx.shared.edition(),
2191                        playground: &cx.shared.playground,
2192                        heading_offset: HeadingOffset::H4,
2193                    }
2194                    .split_summary_and_content()
2195                })
2196                .unwrap_or((None, None));
2197
2198            write!(
2199                w,
2200                "{}",
2201                render_impl_summary(
2202                    cx,
2203                    i,
2204                    parent,
2205                    rendering_params.show_def_docs,
2206                    use_absolute,
2207                    aliases,
2208                    before_dox.as_deref(),
2209                    trait_.is_none() && impl_.items.is_empty(),
2210                )
2211            )?;
2212            if toggled {
2213                w.write_str("</summary>")?;
2214            }
2215
2216            if before_dox.is_some()
2217                && let Some(after_dox) = after_dox
2218            {
2219                write!(w, "<div class=\"docblock\">{after_dox}</div>")?;
2220            }
2221
2222            if !default_impl_items.is_empty() || !impl_items.is_empty() {
2223                w.write_str("<div class=\"impl-items\">")?;
2224                close_tags.push("</div>");
2225            }
2226        }
2227        if !default_impl_items.is_empty() || !impl_items.is_empty() {
2228            w.write_str(&default_impl_items)?;
2229            w.write_str(&impl_items)?;
2230        }
2231        for tag in close_tags.into_iter().rev() {
2232            w.write_str(tag)?;
2233        }
2234        Ok(())
2235    })
2236}
2237
2238// Render the items that appear on the right side of methods, impls, and
2239// associated types. For example "1.0.0 (const: 1.39.0) · source".
2240fn render_rightside(
2241    cx: &Context<'_>,
2242    item: &clean::Item,
2243    render_mode: RenderMode,
2244) -> impl fmt::Display {
2245    let tcx = cx.tcx();
2246
2247    fmt::from_fn(move |w| {
2248        // FIXME: Once https://github.com/rust-lang/rust/issues/143874 is implemented, we can remove
2249        // this condition.
2250        let const_stability = match render_mode {
2251            RenderMode::Normal => item.const_stability(tcx),
2252            RenderMode::ForDeref { .. } => None,
2253        };
2254        let src_href = cx.src_href(item);
2255        let stability = render_stability_since_raw_with_extra(
2256            item.stable_since(tcx),
2257            const_stability,
2258            if src_href.is_some() { "" } else { " rightside" },
2259        );
2260
2261        match (stability, src_href) {
2262            (Some(stability), Some(link)) => {
2263                write!(
2264                    w,
2265                    "<span class=\"rightside\">{stability} · <a class=\"src\" href=\"{link}\">Source</a></span>",
2266                )
2267            }
2268            (Some(stability), None) => {
2269                write!(w, "{stability}")
2270            }
2271            (None, Some(link)) => {
2272                write!(w, "<a class=\"src rightside\" href=\"{link}\">Source</a>")
2273            }
2274            (None, None) => Ok(()),
2275        }
2276    })
2277}
2278
2279fn render_impl_summary(
2280    cx: &Context<'_>,
2281    i: &Impl,
2282    parent: &clean::Item,
2283    show_def_docs: bool,
2284    use_absolute: Option<bool>,
2285    // This argument is used to reference same type with different paths to avoid duplication
2286    // in documentation pages for trait with automatic implementations like "Send" and "Sync".
2287    aliases: &[String],
2288    doc: Option<&str>,
2289    impl_is_empty: bool,
2290) -> impl fmt::Display {
2291    fmt::from_fn(move |w| {
2292        let inner_impl = i.inner_impl();
2293        let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
2294        let aliases = (!aliases.is_empty())
2295            .then_some(fmt::from_fn(|f| {
2296                write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f)))
2297            }))
2298            .maybe_display();
2299        write!(
2300            w,
2301            "<section id=\"{id}\" class=\"impl\"{aliases}>\
2302                {}\
2303                <a href=\"#{id}\" class=\"anchor\">§</a>\
2304                <h3 class=\"code-header\">",
2305            render_rightside(cx, &i.impl_item, RenderMode::Normal)
2306        )?;
2307
2308        if let Some(use_absolute) = use_absolute {
2309            write!(w, "{}", print_impl(inner_impl, use_absolute, cx))?;
2310            if show_def_docs {
2311                for it in &inner_impl.items {
2312                    if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
2313                        write!(
2314                            w,
2315                            "<div class=\"where\">  {};</div>",
2316                            assoc_type(
2317                                it,
2318                                &tydef.generics,
2319                                &[], // intentionally leaving out bounds
2320                                Some(&tydef.type_),
2321                                AssocItemLink::Anchor(None),
2322                                0,
2323                                cx,
2324                            )
2325                        )?;
2326                    }
2327                }
2328            }
2329        } else {
2330            write!(w, "{}", print_impl(inner_impl, false, cx))?;
2331        }
2332        w.write_str("</h3>")?;
2333
2334        let is_trait = inner_impl.trait_.is_some();
2335        if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) {
2336            write!(
2337                w,
2338                "<span class=\"item-info\">\
2339                    <div class=\"stab portability\">{portability}</div>\
2340                </span>",
2341            )?;
2342        }
2343
2344        if let Some(doc) = doc {
2345            if impl_is_empty {
2346                w.write_str(
2347                    "\
2348<div class=\"item-info\">\
2349    <div class=\"stab empty-impl\">This impl block contains no public items.</div>\
2350</div>",
2351                )?;
2352            }
2353            write!(w, "<div class=\"docblock\">{doc}</div>")?;
2354        }
2355
2356        w.write_str("</section>")
2357    })
2358}
2359
2360pub(crate) fn small_url_encode(s: String) -> String {
2361    // These characters don't need to be escaped in a URI.
2362    // See https://url.spec.whatwg.org/#query-percent-encode-set
2363    // and https://url.spec.whatwg.org/#urlencoded-parsing
2364    // and https://url.spec.whatwg.org/#url-code-points
2365    fn dont_escape(c: u8) -> bool {
2366        c.is_ascii_alphanumeric()
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            || c == b':'
2380            || c == b'?'
2381            // As described in urlencoded-parsing, the
2382            // first `=` is the one that separates key from
2383            // value. Following `=`s are part of the value.
2384            || c == b'='
2385    }
2386    let mut st = String::new();
2387    let mut last_match = 0;
2388    for (idx, b) in s.bytes().enumerate() {
2389        if dont_escape(b) {
2390            continue;
2391        }
2392
2393        if last_match != idx {
2394            // Invariant: `idx` must be the first byte in a character at this point.
2395            st += &s[last_match..idx];
2396        }
2397        if b == b' ' {
2398            // URL queries are decoded with + replaced with SP.
2399            // While the same is not true for hashes, rustdoc only needs to be
2400            // consistent with itself when encoding them.
2401            st += "+";
2402        } else {
2403            write!(st, "%{b:02X}").unwrap();
2404        }
2405        // Invariant: if the current byte is not at the start of a multi-byte character,
2406        // we need to get down here so that when the next turn of the loop comes around,
2407        // last_match winds up equalling idx.
2408        //
2409        // In other words, dont_escape must always return `false` in multi-byte character.
2410        last_match = idx + 1;
2411    }
2412
2413    if last_match != 0 {
2414        st += &s[last_match..];
2415        st
2416    } else {
2417        s
2418    }
2419}
2420
2421fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String {
2422    use rustc_middle::ty::print::with_forced_trimmed_paths;
2423    let (type_, trait_) = match impl_id {
2424        ItemId::Auto { trait_, for_ } => {
2425            let ty = tcx.type_of(for_).skip_binder();
2426            (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
2427        }
2428        ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
2429            if let Some(trait_ref) = tcx.impl_opt_trait_ref(impl_id) {
2430                let trait_ref = trait_ref.skip_binder();
2431                (trait_ref.self_ty(), Some(trait_ref))
2432            } else {
2433                (tcx.type_of(impl_id).skip_binder(), None)
2434            }
2435        }
2436    };
2437    with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
2438        format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
2439    } else {
2440        format!("impl-{type_}")
2441    }))
2442}
2443
2444fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2445    match item.kind {
2446        clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
2447            // Alternative format produces no URLs,
2448            // so this parameter does nothing.
2449            Some((
2450                format!("{:#}", print_type(&i.for_, cx)),
2451                get_id_for_impl(cx.tcx(), item.item_id),
2452            ))
2453        }
2454        _ => None,
2455    }
2456}
2457
2458/// Returns the list of implementations for the primitive reference type, filtering out any
2459/// implementations that are on concrete or partially generic types, only keeping implementations
2460/// of the form `impl<T> Trait for &T`.
2461pub(crate) fn get_filtered_impls_for_reference<'a>(
2462    shared: &'a SharedContext<'_>,
2463    it: &clean::Item,
2464) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
2465    let def_id = it.item_id.expect_def_id();
2466    // If the reference primitive is somehow not defined, exit early.
2467    let Some(v) = shared.cache.impls.get(&def_id) else {
2468        return (Vec::new(), Vec::new(), Vec::new());
2469    };
2470    // Since there is no "direct implementation" on the reference primitive type, we filter out
2471    // every implementation which isn't a trait implementation.
2472    let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
2473    let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
2474        traits.partition(|t| t.inner_impl().kind.is_auto());
2475
2476    let (blanket_impl, concrete): (Vec<&Impl>, _) =
2477        concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
2478    // Now we keep only references over full generic types.
2479    let concrete: Vec<_> = concrete
2480        .into_iter()
2481        .filter(|t| match t.inner_impl().for_ {
2482            clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
2483            _ => false,
2484        })
2485        .collect();
2486
2487    (concrete, synthetic, blanket_impl)
2488}
2489
2490#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2491pub(crate) enum ItemSection {
2492    Reexports,
2493    PrimitiveTypes,
2494    Modules,
2495    Macros,
2496    Structs,
2497    Enums,
2498    Constants,
2499    Statics,
2500    Traits,
2501    Functions,
2502    TypeAliases,
2503    Unions,
2504    Implementations,
2505    TypeMethods,
2506    Methods,
2507    StructFields,
2508    Variants,
2509    AssociatedTypes,
2510    AssociatedConstants,
2511    ForeignTypes,
2512    Keywords,
2513    Attributes,
2514    AttributeMacros,
2515    DeriveMacros,
2516    TraitAliases,
2517}
2518
2519impl ItemSection {
2520    const ALL: &'static [Self] = {
2521        use ItemSection::*;
2522        // NOTE: The order here affects the order in the UI.
2523        // Keep this synchronized with addSidebarItems in main.js
2524        &[
2525            Reexports,
2526            PrimitiveTypes,
2527            Modules,
2528            Macros,
2529            Structs,
2530            Enums,
2531            Constants,
2532            Statics,
2533            Traits,
2534            Functions,
2535            TypeAliases,
2536            Unions,
2537            Implementations,
2538            TypeMethods,
2539            Methods,
2540            StructFields,
2541            Variants,
2542            AssociatedTypes,
2543            AssociatedConstants,
2544            ForeignTypes,
2545            Keywords,
2546            Attributes,
2547            AttributeMacros,
2548            DeriveMacros,
2549            TraitAliases,
2550        ]
2551    };
2552
2553    fn id(self) -> &'static str {
2554        match self {
2555            Self::Reexports => "reexports",
2556            Self::Modules => "modules",
2557            Self::Structs => "structs",
2558            Self::Unions => "unions",
2559            Self::Enums => "enums",
2560            Self::Functions => "functions",
2561            Self::TypeAliases => "types",
2562            Self::Statics => "statics",
2563            Self::Constants => "constants",
2564            Self::Traits => "traits",
2565            Self::Implementations => "impls",
2566            Self::TypeMethods => "tymethods",
2567            Self::Methods => "methods",
2568            Self::StructFields => "fields",
2569            Self::Variants => "variants",
2570            Self::Macros => "macros",
2571            Self::PrimitiveTypes => "primitives",
2572            Self::AssociatedTypes => "associated-types",
2573            Self::AssociatedConstants => "associated-consts",
2574            Self::ForeignTypes => "foreign-types",
2575            Self::Keywords => "keywords",
2576            Self::Attributes => "attributes",
2577            Self::AttributeMacros => "attributes",
2578            Self::DeriveMacros => "derives",
2579            Self::TraitAliases => "trait-aliases",
2580        }
2581    }
2582
2583    fn name(self) -> &'static str {
2584        match self {
2585            Self::Reexports => "Re-exports",
2586            Self::Modules => "Modules",
2587            Self::Structs => "Structs",
2588            Self::Unions => "Unions",
2589            Self::Enums => "Enums",
2590            Self::Functions => "Functions",
2591            Self::TypeAliases => "Type Aliases",
2592            Self::Statics => "Statics",
2593            Self::Constants => "Constants",
2594            Self::Traits => "Traits",
2595            Self::Implementations => "Implementations",
2596            Self::TypeMethods => "Type Methods",
2597            Self::Methods => "Methods",
2598            Self::StructFields => "Struct Fields",
2599            Self::Variants => "Variants",
2600            Self::Macros => "Macros",
2601            Self::PrimitiveTypes => "Primitive Types",
2602            Self::AssociatedTypes => "Associated Types",
2603            Self::AssociatedConstants => "Associated Constants",
2604            Self::ForeignTypes => "Foreign Types",
2605            Self::Keywords => "Keywords",
2606            Self::Attributes => "Attributes",
2607            Self::AttributeMacros => "Attribute Macros",
2608            Self::DeriveMacros => "Derive Macros",
2609            Self::TraitAliases => "Trait Aliases",
2610        }
2611    }
2612}
2613
2614fn item_ty_to_section(ty: ItemType) -> ItemSection {
2615    match ty {
2616        ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
2617        ItemType::Module => ItemSection::Modules,
2618        ItemType::Struct => ItemSection::Structs,
2619        ItemType::Union => ItemSection::Unions,
2620        ItemType::Enum => ItemSection::Enums,
2621        ItemType::Function => ItemSection::Functions,
2622        ItemType::TypeAlias => ItemSection::TypeAliases,
2623        ItemType::Static => ItemSection::Statics,
2624        ItemType::Constant => ItemSection::Constants,
2625        ItemType::Trait => ItemSection::Traits,
2626        ItemType::Impl => ItemSection::Implementations,
2627        ItemType::TyMethod => ItemSection::TypeMethods,
2628        ItemType::Method => ItemSection::Methods,
2629        ItemType::StructField => ItemSection::StructFields,
2630        ItemType::Variant => ItemSection::Variants,
2631        ItemType::Macro => ItemSection::Macros,
2632        ItemType::Primitive => ItemSection::PrimitiveTypes,
2633        ItemType::AssocType => ItemSection::AssociatedTypes,
2634        ItemType::AssocConst => ItemSection::AssociatedConstants,
2635        ItemType::ForeignType => ItemSection::ForeignTypes,
2636        ItemType::Keyword => ItemSection::Keywords,
2637        ItemType::Attribute => ItemSection::Attributes,
2638        ItemType::ProcAttribute => ItemSection::AttributeMacros,
2639        ItemType::ProcDerive => ItemSection::DeriveMacros,
2640        ItemType::TraitAlias => ItemSection::TraitAliases,
2641    }
2642}
2643
2644/// Returns a list of all paths used in the type.
2645/// This is used to help deduplicate imported impls
2646/// for reexported types. If any of the contained
2647/// types are re-exported, we don't use the corresponding
2648/// entry from the js file, as inlining will have already
2649/// picked up the impl
2650fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec<String> {
2651    let mut out = Vec::new();
2652    let mut visited = FxHashSet::default();
2653    let mut work = VecDeque::new();
2654
2655    let mut process_path = |did: DefId| {
2656        let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
2657        let fqp = cache.exact_paths.get(&did).or_else(get_extern);
2658
2659        if let Some(path) = fqp {
2660            out.push(join_path_syms(path));
2661        }
2662    };
2663
2664    work.push_back(first_ty);
2665
2666    while let Some(ty) = work.pop_front() {
2667        if !visited.insert(ty) {
2668            continue;
2669        }
2670
2671        match ty {
2672            clean::Type::Path { path } => process_path(path.def_id()),
2673            clean::Type::Tuple(tys) => {
2674                work.extend(tys.iter());
2675            }
2676            clean::Type::Slice(ty) => {
2677                work.push_back(ty);
2678            }
2679            clean::Type::Array(ty, _) => {
2680                work.push_back(ty);
2681            }
2682            clean::Type::RawPointer(_, ty) => {
2683                work.push_back(ty);
2684            }
2685            clean::Type::BorrowedRef { type_, .. } => {
2686                work.push_back(type_);
2687            }
2688            clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
2689                work.push_back(self_type);
2690                if let Some(trait_) = trait_ {
2691                    process_path(trait_.def_id());
2692                }
2693            }
2694            _ => {}
2695        }
2696    }
2697    out
2698}
2699
2700const MAX_FULL_EXAMPLES: usize = 5;
2701const NUM_VISIBLE_LINES: usize = 10;
2702
2703/// Generates the HTML for example call locations generated via the --scrape-examples flag.
2704fn render_call_locations<W: fmt::Write>(
2705    mut w: W,
2706    cx: &Context<'_>,
2707    item: &clean::Item,
2708) -> fmt::Result {
2709    let tcx = cx.tcx();
2710    let def_id = item.item_id.expect_def_id();
2711    let key = tcx.def_path_hash(def_id);
2712    let Some(call_locations) = cx.shared.call_locations.get(&key) else { return Ok(()) };
2713
2714    // Generate a unique ID so users can link to this section for a given method
2715    let id = cx.derive_id("scraped-examples");
2716    write!(
2717        &mut w,
2718        "<div class=\"docblock scraped-example-list\">\
2719          <span></span>\
2720          <h5 id=\"{id}\">\
2721             <a href=\"#{id}\">Examples found in repository</a>\
2722             <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
2723          </h5>",
2724        root_path = cx.root_path(),
2725        id = id
2726    )?;
2727
2728    // Create a URL to a particular location in a reverse-dependency's source file
2729    let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
2730        let (line_lo, line_hi) = loc.call_expr.line_span;
2731        let (anchor, title) = if line_lo == line_hi {
2732            ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
2733        } else {
2734            (
2735                format!("{}-{}", line_lo + 1, line_hi + 1),
2736                format!("lines {}-{}", line_lo + 1, line_hi + 1),
2737            )
2738        };
2739        let url = format!("{}{}#{anchor}", cx.root_path(), call_data.url);
2740        (url, title)
2741    };
2742
2743    // Generate the HTML for a single example, being the title and code block
2744    let write_example = |w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
2745        let contents = match fs::read_to_string(path) {
2746            Ok(contents) => contents,
2747            Err(err) => {
2748                let span = item.span(tcx).map_or(DUMMY_SP, |span| span.inner());
2749                tcx.dcx().span_err(span, format!("failed to read file {}: {err}", path.display()));
2750                return false;
2751            }
2752        };
2753
2754        // To reduce file sizes, we only want to embed the source code needed to understand the example, not
2755        // the entire file. So we find the smallest byte range that covers all items enclosing examples.
2756        assert!(!call_data.locations.is_empty());
2757        let min_loc =
2758            call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2759        let byte_min = min_loc.enclosing_item.byte_span.0;
2760        let line_min = min_loc.enclosing_item.line_span.0;
2761        let max_loc =
2762            call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
2763        let byte_max = max_loc.enclosing_item.byte_span.1;
2764        let line_max = max_loc.enclosing_item.line_span.1;
2765
2766        // The output code is limited to that byte range.
2767        let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
2768
2769        // The call locations need to be updated to reflect that the size of the program has changed.
2770        // Specifically, the ranges are all subtracted by `byte_min` since that's the new zero point.
2771        let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
2772            .locations
2773            .iter()
2774            .map(|loc| {
2775                let (byte_lo, byte_hi) = loc.call_ident.byte_span;
2776                let (line_lo, line_hi) = loc.call_expr.line_span;
2777                let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
2778
2779                let line_range = (line_lo - line_min, line_hi - line_min);
2780                let (line_url, line_title) = link_to_loc(call_data, loc);
2781
2782                (byte_range, (line_range, line_url, line_title))
2783            })
2784            .unzip();
2785
2786        let (_, init_url, init_title) = &line_ranges[0];
2787        let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
2788        let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
2789
2790        // For scraped examples, we don't need a real span from the SourceMap.
2791        // The URL is already provided in ScrapedInfo, and sources::print_src
2792        // will use that directly. We use DUMMY_SP as a placeholder.
2793        // Note: DUMMY_SP is safe here because href_from_span won't be called
2794        // for scraped examples.
2795        let file_span = rustc_span::DUMMY_SP;
2796
2797        let mut decoration_info = FxIndexMap::default();
2798        decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
2799        decoration_info.insert("highlight", byte_ranges);
2800
2801        sources::print_src(
2802            w,
2803            contents_subset,
2804            file_span,
2805            cx,
2806            &cx.root_path(),
2807            &highlight::DecorationInfo(decoration_info),
2808            &sources::SourceContext::Embedded(sources::ScrapedInfo {
2809                needs_expansion,
2810                offset: line_min,
2811                name: &call_data.display_name,
2812                url: init_url,
2813                title: init_title,
2814                locations: locations_encoded,
2815            }),
2816        )
2817        .unwrap();
2818
2819        true
2820    };
2821
2822    // The call locations are output in sequence, so that sequence needs to be determined.
2823    // Ideally the most "relevant" examples would be shown first, but there's no general algorithm
2824    // for determining relevance. We instead proxy relevance with the following heuristics:
2825    //   1. Code written to be an example is better than code not written to be an example, e.g.
2826    //      a snippet from examples/foo.rs is better than src/lib.rs. We don't know the Cargo
2827    //      directory structure in Rustdoc, so we proxy this by prioritizing code that comes from
2828    //      a --crate-type bin.
2829    //   2. Smaller examples are better than large examples. So we prioritize snippets that have
2830    //      the smallest number of lines in their enclosing item.
2831    //   3. Finally we sort by the displayed file name, which is arbitrary but prevents the
2832    //      ordering of examples from randomly changing between Rustdoc invocations.
2833    let ordered_locations = {
2834        fn sort_criterion<'a>(
2835            (_, call_data): &(&PathBuf, &'a CallData),
2836        ) -> (bool, u32, &'a String) {
2837            // Use the first location because that's what the user will see initially
2838            let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2839            (!call_data.is_bin, hi - lo, &call_data.display_name)
2840        }
2841
2842        let mut locs = call_locations.iter().collect::<Vec<_>>();
2843        locs.sort_by_key(sort_criterion);
2844        locs
2845    };
2846
2847    let mut it = ordered_locations.into_iter().peekable();
2848
2849    // An example may fail to write if its source can't be read for some reason, so this method
2850    // continues iterating until a write succeeds
2851    let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
2852        for example in it.by_ref() {
2853            if write_example(&mut *w, example) {
2854                break;
2855            }
2856        }
2857    };
2858
2859    // Write just one example that's visible by default in the method's description.
2860    write_and_skip_failure(&mut w, &mut it);
2861
2862    // Then add the remaining examples in a hidden section.
2863    if it.peek().is_some() {
2864        write!(
2865            w,
2866            "<details class=\"toggle more-examples-toggle\">\
2867                  <summary class=\"hideme\">\
2868                     <span>More examples</span>\
2869                  </summary>\
2870                  <div class=\"hide-more\">Hide additional examples</div>\
2871                  <div class=\"more-scraped-examples\">\
2872                    <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
2873        )?;
2874
2875        // Only generate inline code for MAX_FULL_EXAMPLES number of examples. Otherwise we could
2876        // make the page arbitrarily huge!
2877        for _ in 0..MAX_FULL_EXAMPLES {
2878            write_and_skip_failure(&mut w, &mut it);
2879        }
2880
2881        // For the remaining examples, generate a <ul> containing links to the source files.
2882        if it.peek().is_some() {
2883            w.write_str(
2884                r#"<div class="example-links">Additional examples can be found in:<br><ul>"#,
2885            )?;
2886            it.try_for_each(|(_, call_data)| {
2887                let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
2888                write!(
2889                    w,
2890                    r#"<li><a href="{url}">{name}</a></li>"#,
2891                    url = url,
2892                    name = call_data.display_name
2893                )
2894            })?;
2895            w.write_str("</ul></div>")?;
2896        }
2897
2898        w.write_str("</div></details>")?;
2899    }
2900
2901    w.write_str("</div>")
2902}
2903
2904fn render_attributes_in_code(
2905    w: &mut impl fmt::Write,
2906    item: &clean::Item,
2907    prefix: &str,
2908    cx: &Context<'_>,
2909) -> fmt::Result {
2910    render_attributes_in_code_with_options(w, item, prefix, cx, true, "")
2911}
2912
2913pub(super) fn render_attributes_in_code_with_options(
2914    w: &mut impl fmt::Write,
2915    item: &clean::Item,
2916    prefix: &str,
2917    cx: &Context<'_>,
2918    render_doc_hidden: bool,
2919    open_tag: &str,
2920) -> fmt::Result {
2921    w.write_str(open_tag)?;
2922    if render_doc_hidden && item.is_doc_hidden() {
2923        render_code_attribute(prefix, "#[doc(hidden)]", w)?;
2924    }
2925    for attr in &item.attrs.other_attrs {
2926        let hir::Attribute::Parsed(kind) = attr else { continue };
2927        let attr = match kind {
2928            AttributeKind::LinkSection { name, .. } => {
2929                Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}"))))
2930            }
2931            AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"),
2932            AttributeKind::ExportName { name, .. } => {
2933                Cow::Owned(format!("#[unsafe(export_name = {})]", Escape(&format!("{name:?}"))))
2934            }
2935            AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"),
2936            _ => continue,
2937        };
2938        render_code_attribute(prefix, attr.as_ref(), w)?;
2939    }
2940
2941    if let Some(def_id) = item.def_id()
2942        && let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id)
2943    {
2944        render_code_attribute(prefix, &repr, w)?;
2945    }
2946    Ok(())
2947}
2948
2949fn render_repr_attribute_in_code(
2950    w: &mut impl fmt::Write,
2951    cx: &Context<'_>,
2952    def_id: DefId,
2953) -> fmt::Result {
2954    if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) {
2955        render_code_attribute("", &repr, w)?;
2956    }
2957    Ok(())
2958}
2959
2960fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) -> fmt::Result {
2961    write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>")
2962}
2963
2964/// Compute the *public* `#[repr]` of the item given by `DefId`.
2965///
2966/// Read more about it here:
2967/// <https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type>.
2968fn repr_attribute<'tcx>(
2969    tcx: TyCtxt<'tcx>,
2970    cache: &Cache,
2971    def_id: DefId,
2972) -> Option<Cow<'static, str>> {
2973    let adt = match tcx.def_kind(def_id) {
2974        DefKind::Struct | DefKind::Enum | DefKind::Union => tcx.adt_def(def_id),
2975        _ => return None,
2976    };
2977    let repr = adt.repr();
2978
2979    let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id);
2980    let is_public_field = |field: &ty::FieldDef| {
2981        (cache.document_private || field.vis.is_public()) && is_visible(field.did)
2982    };
2983
2984    if repr.transparent() {
2985        // The transparent repr is public iff the non-1-ZST field is public and visible or
2986        // – in case all fields are 1-ZST fields — at least one field is public and visible.
2987        let is_public = 'is_public: {
2988            // `#[repr(transparent)]` can only be applied to structs and single-variant enums.
2989            let var = adt.variant(rustc_abi::FIRST_VARIANT); // the first and only variant
2990
2991            if !is_visible(var.def_id) {
2992                break 'is_public false;
2993            }
2994
2995            // Side note: There can only ever be one or zero non-1-ZST fields.
2996            let non_1zst_field = var.fields.iter().find(|field| {
2997                let ty = ty::TypingEnv::post_analysis(tcx, field.did)
2998                    .as_query_input(tcx.type_of(field.did).instantiate_identity());
2999                tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst())
3000            });
3001
3002            match non_1zst_field {
3003                Some(field) => is_public_field(field),
3004                None => var.fields.is_empty() || var.fields.iter().any(is_public_field),
3005            }
3006        };
3007
3008        // Since the transparent repr can't have any other reprs or
3009        // repr modifiers beside it, we can safely return early here.
3010        return is_public.then(|| "#[repr(transparent)]".into());
3011    }
3012
3013    // Fast path which avoids looking through the variants and fields in
3014    // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
3015    // FIXME: This check is not very robust / forward compatible!
3016    if !repr.c()
3017        && !repr.simd()
3018        && repr.int.is_none()
3019        && repr.pack.is_none()
3020        && repr.align.is_none()
3021    {
3022        return None;
3023    }
3024
3025    // The repr is public iff all components are public and visible.
3026    let is_public = adt
3027        .variants()
3028        .iter()
3029        .all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field));
3030    if !is_public {
3031        return None;
3032    }
3033
3034    let mut result = Vec::<Cow<'_, _>>::new();
3035
3036    if repr.c() {
3037        result.push("C".into());
3038    }
3039    if repr.simd() {
3040        result.push("simd".into());
3041    }
3042    if let Some(int) = repr.int {
3043        let prefix = if int.is_signed() { 'i' } else { 'u' };
3044        let int = match int {
3045            rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"),
3046            rustc_abi::IntegerType::Fixed(int, _) => {
3047                format!("{prefix}{}", int.size().bytes() * 8)
3048            }
3049        };
3050        result.push(int.into());
3051    }
3052
3053    // Render modifiers last.
3054    if let Some(pack) = repr.pack {
3055        result.push(format!("packed({})", pack.bytes()).into());
3056    }
3057    if let Some(align) = repr.align {
3058        result.push(format!("align({})", align.bytes()).into());
3059    }
3060
3061    (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into())
3062}