Skip to main content

rustdoc/html/render/
mod.rs

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