Skip to main content

rustdoc/html/render/
mod.rs

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