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