Skip to main content

rustdoc/html/render/
mod.rs

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