[go: up one dir, main page]

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