rustdoc/html/render/
print_item.rs

1use std::cmp::Ordering;
2use std::fmt::{self, Display, Write as _};
3use std::iter;
4
5use askama::Template;
6use rustc_abi::VariantIdx;
7use rustc_ast::join_path_syms;
8use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
9use rustc_hir as hir;
10use rustc_hir::def::CtorKind;
11use rustc_hir::def_id::DefId;
12use rustc_index::IndexVec;
13use rustc_middle::ty::{self, TyCtxt};
14use rustc_span::hygiene::MacroKind;
15use rustc_span::symbol::{Symbol, sym};
16use tracing::{debug, info};
17
18use super::type_layout::document_type_layout;
19use super::{
20    AssocItemLink, AssocItemRender, Context, ImplRenderingParameters, RenderMode,
21    collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference,
22    item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls,
23    render_assoc_item, render_assoc_items, render_attributes_in_code, render_impl,
24    render_repr_attribute_in_code, render_rightside, render_stability_since_raw,
25    render_stability_since_raw_with_extra, write_section_heading,
26};
27use crate::clean;
28use crate::config::ModuleSorting;
29use crate::display::{Joined as _, MaybeDisplay as _};
30use crate::formats::Impl;
31use crate::formats::item_type::ItemType;
32use crate::html::escape::{Escape, EscapeBodyTextWithWbr};
33use crate::html::format::{
34    Ending, PrintWithSpace, full_print_fn_decl, print_abi_with_space, print_constness_with_space,
35    print_generic_bound, print_generics, print_impl, print_import, print_type, print_where_clause,
36    visibility_print_with_space,
37};
38use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine};
39use crate::html::render::sidebar::filters;
40use crate::html::render::{document_full, document_item_info};
41use crate::html::url_parts_builder::UrlPartsBuilder;
42
43const ITEM_TABLE_OPEN: &str = "<dl class=\"item-table\">";
44const REEXPORTS_TABLE_OPEN: &str = "<dl class=\"item-table reexports\">";
45const ITEM_TABLE_CLOSE: &str = "</dl>";
46
47// A component in a `use` path, like `string` in std::string::ToString
48struct PathComponent {
49    path: String,
50    name: Symbol,
51}
52
53#[derive(Template)]
54#[template(path = "print_item.html")]
55struct ItemVars<'a> {
56    typ: &'a str,
57    name: &'a str,
58    item_type: &'a str,
59    path_components: Vec<PathComponent>,
60    stability_since_raw: &'a str,
61    src_href: Option<&'a str>,
62}
63
64pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item) -> impl fmt::Display {
65    debug_assert!(!item.is_stripped());
66
67    fmt::from_fn(|buf| {
68        let typ = match item.kind {
69            clean::ModuleItem(_) => {
70                if item.is_crate() {
71                    "Crate "
72                } else {
73                    "Module "
74                }
75            }
76            clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ",
77            clean::TraitItem(..) => "Trait ",
78            clean::StructItem(..) => "Struct ",
79            clean::UnionItem(..) => "Union ",
80            clean::EnumItem(..) => "Enum ",
81            clean::TypeAliasItem(..) => "Type Alias ",
82            clean::MacroItem(..) => "Macro ",
83            clean::ProcMacroItem(ref mac) => match mac.kind {
84                MacroKind::Bang => "Macro ",
85                MacroKind::Attr => "Attribute Macro ",
86                MacroKind::Derive => "Derive Macro ",
87            },
88            clean::PrimitiveItem(..) => "Primitive Type ",
89            clean::StaticItem(..) | clean::ForeignStaticItem(..) => "Static ",
90            clean::ConstantItem(..) => "Constant ",
91            clean::ForeignTypeItem => "Foreign Type ",
92            clean::KeywordItem => "Keyword ",
93            clean::AttributeItem => "Attribute ",
94            clean::TraitAliasItem(..) => "Trait Alias ",
95            _ => {
96                // We don't generate pages for any other type.
97                unreachable!();
98            }
99        };
100        let stability_since_raw =
101            render_stability_since_raw(item.stable_since(cx.tcx()), item.const_stability(cx.tcx()))
102                .maybe_display()
103                .to_string();
104
105        // Write source tag
106        //
107        // When this item is part of a `crate use` in a downstream crate, the
108        // source link in the downstream documentation will actually come back to
109        // this page, and this link will be auto-clicked. The `id` attribute is
110        // used to find the link to auto-click.
111        let src_href =
112            if cx.info.include_sources && !item.is_primitive() { cx.src_href(item) } else { None };
113
114        let path_components = if item.is_fake_item() {
115            vec![]
116        } else {
117            let cur = &cx.current;
118            let amt = if item.is_mod() { cur.len() - 1 } else { cur.len() };
119            cur.iter()
120                .enumerate()
121                .take(amt)
122                .map(|(i, component)| PathComponent {
123                    path: "../".repeat(cur.len() - i - 1),
124                    name: *component,
125                })
126                .collect()
127        };
128
129        let item_vars = ItemVars {
130            typ,
131            name: item.name.as_ref().unwrap().as_str(),
132            item_type: &item.type_().to_string(),
133            path_components,
134            stability_since_raw: &stability_since_raw,
135            src_href: src_href.as_deref(),
136        };
137
138        item_vars.render_into(buf).unwrap();
139
140        match &item.kind {
141            clean::ModuleItem(m) => {
142                write!(buf, "{}", item_module(cx, item, &m.items))
143            }
144            clean::FunctionItem(f) | clean::ForeignFunctionItem(f, _) => {
145                write!(buf, "{}", item_function(cx, item, f))
146            }
147            clean::TraitItem(t) => write!(buf, "{}", item_trait(cx, item, t)),
148            clean::StructItem(s) => {
149                write!(buf, "{}", item_struct(cx, item, s))
150            }
151            clean::UnionItem(s) => write!(buf, "{}", item_union(cx, item, s)),
152            clean::EnumItem(e) => write!(buf, "{}", item_enum(cx, item, e)),
153            clean::TypeAliasItem(t) => {
154                write!(buf, "{}", item_type_alias(cx, item, t))
155            }
156            clean::MacroItem(m) => write!(buf, "{}", item_macro(cx, item, m)),
157            clean::ProcMacroItem(m) => {
158                write!(buf, "{}", item_proc_macro(cx, item, m))
159            }
160            clean::PrimitiveItem(_) => write!(buf, "{}", item_primitive(cx, item)),
161            clean::StaticItem(i) => {
162                write!(buf, "{}", item_static(cx, item, i, None))
163            }
164            clean::ForeignStaticItem(i, safety) => {
165                write!(buf, "{}", item_static(cx, item, i, Some(*safety)))
166            }
167            clean::ConstantItem(ci) => {
168                write!(buf, "{}", item_constant(cx, item, &ci.generics, &ci.type_, &ci.kind))
169            }
170            clean::ForeignTypeItem => {
171                write!(buf, "{}", item_foreign_type(cx, item))
172            }
173            clean::KeywordItem | clean::AttributeItem => {
174                write!(buf, "{}", item_keyword_or_attribute(cx, item))
175            }
176            clean::TraitAliasItem(ta) => {
177                write!(buf, "{}", item_trait_alias(cx, item, ta))
178            }
179            _ => {
180                // We don't generate pages for any other type.
181                unreachable!();
182            }
183        }?;
184
185        // Render notable-traits.js used for all methods in this module.
186        let mut types_with_notable_traits = cx.types_with_notable_traits.borrow_mut();
187        if !types_with_notable_traits.is_empty() {
188            write!(
189                buf,
190                r#"<script type="text/json" id="notable-traits-data">{}</script>"#,
191                notable_traits_json(types_with_notable_traits.iter(), cx),
192            )?;
193            types_with_notable_traits.clear();
194        }
195        Ok(())
196    })
197}
198
199/// For large structs, enums, unions, etc, determine whether to hide their fields
200fn should_hide_fields(n_fields: usize) -> bool {
201    n_fields > 12
202}
203
204fn toggle_open(mut w: impl fmt::Write, text: impl Display) {
205    write!(
206        w,
207        "<details class=\"toggle type-contents-toggle\">\
208            <summary class=\"hideme\">\
209                <span>Show {text}</span>\
210            </summary>",
211    )
212    .unwrap();
213}
214
215fn toggle_close(mut w: impl fmt::Write) {
216    w.write_str("</details>").unwrap();
217}
218
219fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> impl fmt::Display {
220    fmt::from_fn(|w| {
221        write!(w, "{}", document(cx, item, None, HeadingOffset::H2))?;
222
223        let mut not_stripped_items: FxIndexMap<ItemType, Vec<(usize, &clean::Item)>> =
224            FxIndexMap::default();
225
226        for (index, item) in items.iter().filter(|i| !i.is_stripped()).enumerate() {
227            not_stripped_items.entry(item.type_()).or_default().push((index, item));
228        }
229
230        // the order of item types in the listing
231        fn reorder(ty: ItemType) -> u8 {
232            match ty {
233                ItemType::ExternCrate => 0,
234                ItemType::Import => 1,
235                ItemType::Primitive => 2,
236                ItemType::Module => 3,
237                ItemType::Macro => 4,
238                ItemType::Struct => 5,
239                ItemType::Enum => 6,
240                ItemType::Constant => 7,
241                ItemType::Static => 8,
242                ItemType::Trait => 9,
243                ItemType::Function => 10,
244                ItemType::TypeAlias => 12,
245                ItemType::Union => 13,
246                _ => 14 + ty as u8,
247            }
248        }
249
250        fn cmp(i1: &clean::Item, i2: &clean::Item, tcx: TyCtxt<'_>) -> Ordering {
251            let is_stable1 =
252                i1.stability(tcx).as_ref().map(|s| s.level.is_stable()).unwrap_or(true);
253            let is_stable2 =
254                i2.stability(tcx).as_ref().map(|s| s.level.is_stable()).unwrap_or(true);
255            if is_stable1 != is_stable2 {
256                // true is bigger than false in the standard bool ordering,
257                // but we actually want stable items to come first
258                return is_stable2.cmp(&is_stable1);
259            }
260            match (i1.name, i2.name) {
261                (Some(name1), Some(name2)) => compare_names(name1.as_str(), name2.as_str()),
262                (Some(_), None) => Ordering::Greater,
263                (None, Some(_)) => Ordering::Less,
264                (None, None) => Ordering::Equal,
265            }
266        }
267
268        let tcx = cx.tcx();
269
270        match cx.shared.module_sorting {
271            ModuleSorting::Alphabetical => {
272                for items in not_stripped_items.values_mut() {
273                    items.sort_by(|(_, i1), (_, i2)| cmp(i1, i2, tcx));
274                }
275            }
276            ModuleSorting::DeclarationOrder => {}
277        }
278        // This call is to remove re-export duplicates in cases such as:
279        //
280        // ```
281        // pub(crate) mod foo {
282        //     pub(crate) mod bar {
283        //         pub(crate) trait Double { fn foo(); }
284        //     }
285        // }
286        //
287        // pub(crate) use foo::bar::*;
288        // pub(crate) use foo::*;
289        // ```
290        //
291        // `Double` will appear twice in the generated docs.
292        //
293        // FIXME: This code is quite ugly and could be improved. Small issue: DefId
294        // can be identical even if the elements are different (mostly in imports).
295        // So in case this is an import, we keep everything by adding a "unique id"
296        // (which is the position in the vector).
297        for items in not_stripped_items.values_mut() {
298            items.dedup_by_key(|(idx, i)| {
299                (
300                    i.item_id,
301                    if i.name.is_some() { Some(full_path(cx, i)) } else { None },
302                    i.type_(),
303                    if i.is_import() { *idx } else { 0 },
304                )
305            });
306        }
307
308        debug!("{not_stripped_items:?}");
309
310        let mut types = not_stripped_items.keys().copied().collect::<Vec<_>>();
311        types.sort_unstable_by(|a, b| reorder(*a).cmp(&reorder(*b)));
312
313        let mut last_section: Option<super::ItemSection> = None;
314
315        for type_ in types {
316            let my_section = item_ty_to_section(type_);
317
318            // Only render section heading if the section changed
319            if last_section != Some(my_section) {
320                // Close the previous section if there was one
321                if last_section.is_some() {
322                    w.write_str(ITEM_TABLE_CLOSE)?;
323                }
324                let tag = if my_section == super::ItemSection::Reexports {
325                    REEXPORTS_TABLE_OPEN
326                } else {
327                    ITEM_TABLE_OPEN
328                };
329                write!(
330                    w,
331                    "{}",
332                    write_section_heading(
333                        my_section.name(),
334                        &cx.derive_id(my_section.id()),
335                        None,
336                        tag
337                    )
338                )?;
339                last_section = Some(my_section);
340            }
341
342            for (_, myitem) in &not_stripped_items[&type_] {
343                match myitem.kind {
344                    clean::ExternCrateItem { ref src } => {
345                        use crate::html::format::print_anchor;
346
347                        match *src {
348                            Some(src) => {
349                                write!(
350                                    w,
351                                    "<dt><code>{}extern crate {} as {};",
352                                    visibility_print_with_space(myitem, cx),
353                                    print_anchor(myitem.item_id.expect_def_id(), src, cx),
354                                    EscapeBodyTextWithWbr(myitem.name.unwrap().as_str())
355                                )?;
356                            }
357                            None => {
358                                write!(
359                                    w,
360                                    "<dt><code>{}extern crate {};",
361                                    visibility_print_with_space(myitem, cx),
362                                    print_anchor(
363                                        myitem.item_id.expect_def_id(),
364                                        myitem.name.unwrap(),
365                                        cx
366                                    )
367                                )?;
368                            }
369                        }
370                        write!(w, "</code></dt>")?
371                    }
372                    clean::ImportItem(ref import) => {
373                        let stab_tags =
374                            import.source.did.map_or_else(String::new, |import_def_id| {
375                                print_extra_info_tags(tcx, myitem, item, Some(import_def_id))
376                                    .to_string()
377                            });
378                        let id = match import.kind {
379                            clean::ImportKind::Simple(s) => {
380                                format!(" id=\"{}\"", cx.derive_id(format!("reexport.{s}")))
381                            }
382                            clean::ImportKind::Glob => String::new(),
383                        };
384                        write!(
385                            w,
386                            "<dt{id}>\
387                                <code>"
388                        )?;
389                        render_attributes_in_code(w, myitem, "", cx)?;
390                        write!(
391                            w,
392                            "{vis}{imp}</code>{stab_tags}\
393                            </dt>",
394                            vis = visibility_print_with_space(myitem, cx),
395                            imp = print_import(import, cx),
396                        )?;
397                    }
398                    _ => {
399                        if myitem.name.is_none() {
400                            continue;
401                        }
402
403                        let unsafety_flag = match myitem.kind {
404                            clean::FunctionItem(_) | clean::ForeignFunctionItem(..)
405                                if myitem.fn_header(tcx).unwrap().safety
406                                    == hir::HeaderSafety::Normal(hir::Safety::Unsafe) =>
407                            {
408                                "<sup title=\"unsafe function\">⚠</sup>"
409                            }
410                            clean::ForeignStaticItem(_, hir::Safety::Unsafe) => {
411                                "<sup title=\"unsafe static\">⚠</sup>"
412                            }
413                            _ => "",
414                        };
415                        let visibility_and_hidden = match myitem.visibility(tcx) {
416                            Some(ty::Visibility::Restricted(_)) => {
417                                if myitem.is_doc_hidden() {
418                                    // Don't separate with a space when there are two of them
419                                    "<span title=\"Restricted Visibility\">&nbsp;🔒</span><span title=\"Hidden item\">👻</span> "
420                                } else {
421                                    "<span title=\"Restricted Visibility\">&nbsp;🔒</span> "
422                                }
423                            }
424                            _ if myitem.is_doc_hidden() => {
425                                "<span title=\"Hidden item\">&nbsp;👻</span> "
426                            }
427                            _ => "",
428                        };
429
430                        let docs = MarkdownSummaryLine(&myitem.doc_value(), &myitem.links(cx))
431                            .into_string();
432                        let (docs_before, docs_after) =
433                            if docs.is_empty() { ("", "") } else { ("<dd>", "</dd>") };
434                        write!(
435                            w,
436                            "<dt>\
437                                <a class=\"{class}\" href=\"{href}\" title=\"{title1} {title2}\">\
438                                {name}\
439                                </a>\
440                                {visibility_and_hidden}\
441                                {unsafety_flag}\
442                                {stab_tags}\
443                            </dt>\
444                            {docs_before}{docs}{docs_after}",
445                            name = EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()),
446                            visibility_and_hidden = visibility_and_hidden,
447                            stab_tags = print_extra_info_tags(tcx, myitem, item, None),
448                            class = type_,
449                            unsafety_flag = unsafety_flag,
450                            href = print_item_path(type_, myitem.name.unwrap().as_str()),
451                            title1 = myitem.type_(),
452                            title2 = full_path(cx, myitem),
453                        )?;
454                    }
455                }
456            }
457        }
458        // Close the final section
459        if last_section.is_some() {
460            w.write_str(ITEM_TABLE_CLOSE)?;
461        }
462
463        Ok(())
464    })
465}
466
467/// Render the stability, deprecation and portability tags that are displayed in the item's summary
468/// at the module level.
469fn print_extra_info_tags(
470    tcx: TyCtxt<'_>,
471    item: &clean::Item,
472    parent: &clean::Item,
473    import_def_id: Option<DefId>,
474) -> impl Display {
475    fmt::from_fn(move |f| {
476        fn tag_html(class: &str, title: &str, contents: &str) -> impl Display {
477            fmt::from_fn(move |f| {
478                write!(
479                    f,
480                    r#"<wbr><span class="stab {class}" title="{title}">{contents}</span>"#,
481                    title = Escape(title),
482                )
483            })
484        }
485
486        // The trailing space after each tag is to space it properly against the rest of the docs.
487        let deprecation = import_def_id
488            .map_or_else(|| item.deprecation(tcx), |import_did| tcx.lookup_deprecation(import_did));
489        if let Some(depr) = deprecation {
490            let message = if depr.is_in_effect() { "Deprecated" } else { "Deprecation planned" };
491            write!(f, "{}", tag_html("deprecated", "", message))?;
492        }
493
494        // The "rustc_private" crates are permanently unstable so it makes no sense
495        // to render "unstable" everywhere.
496        let stability = import_def_id
497            .map_or_else(|| item.stability(tcx), |import_did| tcx.lookup_stability(import_did));
498        if stability.is_some_and(|s| s.is_unstable() && s.feature != sym::rustc_private) {
499            write!(f, "{}", tag_html("unstable", "", "Experimental"))?;
500        }
501
502        let cfg = match (&item.cfg, parent.cfg.as_ref()) {
503            (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
504            (cfg, _) => cfg.as_deref().cloned(),
505        };
506
507        debug!(
508            "Portability name={name:?} {cfg:?} - {parent_cfg:?} = {cfg:?}",
509            name = item.name,
510            cfg = item.cfg,
511            parent_cfg = parent.cfg
512        );
513        if let Some(ref cfg) = cfg {
514            write!(
515                f,
516                "{}",
517                tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html())
518            )
519        } else {
520            Ok(())
521        }
522    })
523}
524
525fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> impl fmt::Display {
526    fmt::from_fn(|w| {
527        let tcx = cx.tcx();
528        let header = it.fn_header(tcx).expect("printing a function which isn't a function");
529        debug!(
530            "item_function/const: {:?} {:?} {:?} {:?}",
531            it.name,
532            &header.constness,
533            it.stable_since(tcx),
534            it.const_stability(tcx),
535        );
536        let constness = print_constness_with_space(
537            &header.constness,
538            it.stable_since(tcx),
539            it.const_stability(tcx),
540        );
541        let safety = header.safety.print_with_space();
542        let abi = print_abi_with_space(header.abi).to_string();
543        let asyncness = header.asyncness.print_with_space();
544        let visibility = visibility_print_with_space(it, cx).to_string();
545        let name = it.name.unwrap();
546
547        let generics_len = format!("{:#}", print_generics(&f.generics, cx)).len();
548        let header_len = "fn ".len()
549            + visibility.len()
550            + constness.len()
551            + asyncness.len()
552            + safety.len()
553            + abi.len()
554            + name.as_str().len()
555            + generics_len;
556
557        let notable_traits = notable_traits_button(&f.decl.output, cx).maybe_display();
558
559        wrap_item(w, |w| {
560            render_attributes_in_code(w, it, "", cx)?;
561            write!(
562                w,
563                "{vis}{constness}{asyncness}{safety}{abi}fn \
564                {name}{generics}{decl}{notable_traits}{where_clause}",
565                vis = visibility,
566                constness = constness,
567                asyncness = asyncness,
568                safety = safety,
569                abi = abi,
570                name = name,
571                generics = print_generics(&f.generics, cx),
572                where_clause =
573                    print_where_clause(&f.generics, cx, 0, Ending::Newline).maybe_display(),
574                decl = full_print_fn_decl(&f.decl, header_len, 0, cx),
575            )
576        })?;
577        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
578    })
579}
580
581/// Struct used to handle insertion of "negative impl" marker in the generated DOM.
582///
583/// This marker appears once in all trait impl lists to divide negative impls from positive impls.
584struct NegativeMarker {
585    inserted: bool,
586}
587
588impl NegativeMarker {
589    fn new() -> Self {
590        Self { inserted: false }
591    }
592
593    fn insert_if_needed(&mut self, w: &mut fmt::Formatter<'_>, implementor: &Impl) -> fmt::Result {
594        if !self.inserted && !implementor.is_negative_trait_impl() {
595            w.write_str("<div class=\"negative-marker\"></div>")?;
596            self.inserted = true;
597        }
598        Ok(())
599    }
600}
601
602fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt::Display {
603    fmt::from_fn(|w| {
604        let tcx = cx.tcx();
605        let bounds = print_bounds(&t.bounds, false, cx);
606        let required_types =
607            t.items.iter().filter(|m| m.is_required_associated_type()).collect::<Vec<_>>();
608        let provided_types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>();
609        let required_consts =
610            t.items.iter().filter(|m| m.is_required_associated_const()).collect::<Vec<_>>();
611        let provided_consts =
612            t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>();
613        let required_methods = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>();
614        let provided_methods = t.items.iter().filter(|m| m.is_method()).collect::<Vec<_>>();
615        let count_types = required_types.len() + provided_types.len();
616        let count_consts = required_consts.len() + provided_consts.len();
617        let count_methods = required_methods.len() + provided_methods.len();
618        let must_implement_one_of_functions = &tcx.trait_def(t.def_id).must_implement_one_of;
619
620        // Output the trait definition
621        wrap_item(w, |mut w| {
622            render_attributes_in_code(&mut w, it, "", cx)?;
623            write!(
624                w,
625                "{vis}{safety}{is_auto}trait {name}{generics}{bounds}",
626                vis = visibility_print_with_space(it, cx),
627                safety = t.safety(tcx).print_with_space(),
628                is_auto = if t.is_auto(tcx) { "auto " } else { "" },
629                name = it.name.unwrap(),
630                generics = print_generics(&t.generics, cx),
631            )?;
632
633            if !t.generics.where_predicates.is_empty() {
634                write!(
635                    w,
636                    "{}",
637                    print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display()
638                )?;
639            } else {
640                w.write_char(' ')?;
641            }
642
643            if t.items.is_empty() {
644                w.write_str("{ }")
645            } else {
646                // FIXME: we should be using a derived_id for the Anchors here
647                w.write_str("{\n")?;
648                let mut toggle = false;
649
650                // If there are too many associated types, hide _everything_
651                if should_hide_fields(count_types) {
652                    toggle = true;
653                    toggle_open(
654                        &mut w,
655                        format_args!(
656                            "{} associated items",
657                            count_types + count_consts + count_methods
658                        ),
659                    );
660                }
661                for types in [&required_types, &provided_types] {
662                    for t in types {
663                        writeln!(
664                            w,
665                            "{};",
666                            render_assoc_item(
667                                t,
668                                AssocItemLink::Anchor(None),
669                                ItemType::Trait,
670                                cx,
671                                RenderMode::Normal,
672                            )
673                        )?;
674                    }
675                }
676                // If there are too many associated constants, hide everything after them
677                // We also do this if the types + consts is large because otherwise we could
678                // render a bunch of types and _then_ a bunch of consts just because both were
679                // _just_ under the limit
680                if !toggle && should_hide_fields(count_types + count_consts) {
681                    toggle = true;
682                    toggle_open(
683                        &mut w,
684                        format_args!(
685                            "{count_consts} associated constant{plural_const} and \
686                         {count_methods} method{plural_method}",
687                            plural_const = pluralize(count_consts),
688                            plural_method = pluralize(count_methods),
689                        ),
690                    );
691                }
692                if count_types != 0 && (count_consts != 0 || count_methods != 0) {
693                    w.write_str("\n")?;
694                }
695                for consts in [&required_consts, &provided_consts] {
696                    for c in consts {
697                        writeln!(
698                            w,
699                            "{};",
700                            render_assoc_item(
701                                c,
702                                AssocItemLink::Anchor(None),
703                                ItemType::Trait,
704                                cx,
705                                RenderMode::Normal,
706                            )
707                        )?;
708                    }
709                }
710                if !toggle && should_hide_fields(count_methods) {
711                    toggle = true;
712                    toggle_open(&mut w, format_args!("{count_methods} methods"));
713                }
714                if count_consts != 0 && count_methods != 0 {
715                    w.write_str("\n")?;
716                }
717
718                if !required_methods.is_empty() {
719                    writeln!(w, "    // Required method{}", pluralize(required_methods.len()))?;
720                }
721                for (pos, m) in required_methods.iter().enumerate() {
722                    writeln!(
723                        w,
724                        "{};",
725                        render_assoc_item(
726                            m,
727                            AssocItemLink::Anchor(None),
728                            ItemType::Trait,
729                            cx,
730                            RenderMode::Normal,
731                        )
732                    )?;
733
734                    if pos < required_methods.len() - 1 {
735                        w.write_str("<span class=\"item-spacer\"></span>")?;
736                    }
737                }
738                if !required_methods.is_empty() && !provided_methods.is_empty() {
739                    w.write_str("\n")?;
740                }
741
742                if !provided_methods.is_empty() {
743                    writeln!(w, "    // Provided method{}", pluralize(provided_methods.len()))?;
744                }
745                for (pos, m) in provided_methods.iter().enumerate() {
746                    writeln!(
747                        w,
748                        "{} {{ ... }}",
749                        render_assoc_item(
750                            m,
751                            AssocItemLink::Anchor(None),
752                            ItemType::Trait,
753                            cx,
754                            RenderMode::Normal,
755                        )
756                    )?;
757
758                    if pos < provided_methods.len() - 1 {
759                        w.write_str("<span class=\"item-spacer\"></span>")?;
760                    }
761                }
762                if toggle {
763                    toggle_close(&mut w);
764                }
765                w.write_str("}")
766            }
767        })?;
768
769        // Trait documentation
770        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
771
772        fn trait_item(cx: &Context<'_>, m: &clean::Item, t: &clean::Item) -> impl fmt::Display {
773            fmt::from_fn(|w| {
774                let name = m.name.unwrap();
775                info!("Documenting {name} on {ty_name:?}", ty_name = t.name);
776                let item_type = m.type_();
777                let id = cx.derive_id(format!("{item_type}.{name}"));
778
779                let content = document_full(m, cx, HeadingOffset::H5).to_string();
780
781                let toggled = !content.is_empty();
782                if toggled {
783                    let method_toggle_class =
784                        if item_type.is_method() { " method-toggle" } else { "" };
785                    write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>")?;
786                }
787                write!(
788                    w,
789                    "<section id=\"{id}\" class=\"method\">\
790                    {}\
791                    <h4 class=\"code-header\">{}</h4></section>",
792                    render_rightside(cx, m, RenderMode::Normal),
793                    render_assoc_item(
794                        m,
795                        AssocItemLink::Anchor(Some(&id)),
796                        ItemType::Impl,
797                        cx,
798                        RenderMode::Normal,
799                    )
800                )?;
801                document_item_info(cx, m, Some(t)).render_into(w).unwrap();
802                if toggled {
803                    write!(w, "</summary>{content}</details>")?;
804                }
805                Ok(())
806            })
807        }
808
809        if !required_consts.is_empty() {
810            write!(
811                w,
812                "{}",
813                write_section_heading(
814                    "Required Associated Constants",
815                    "required-associated-consts",
816                    None,
817                    "<div class=\"methods\">",
818                )
819            )?;
820            for t in required_consts {
821                write!(w, "{}", trait_item(cx, t, it))?;
822            }
823            w.write_str("</div>")?;
824        }
825        if !provided_consts.is_empty() {
826            write!(
827                w,
828                "{}",
829                write_section_heading(
830                    "Provided Associated Constants",
831                    "provided-associated-consts",
832                    None,
833                    "<div class=\"methods\">",
834                )
835            )?;
836            for t in provided_consts {
837                write!(w, "{}", trait_item(cx, t, it))?;
838            }
839            w.write_str("</div>")?;
840        }
841
842        if !required_types.is_empty() {
843            write!(
844                w,
845                "{}",
846                write_section_heading(
847                    "Required Associated Types",
848                    "required-associated-types",
849                    None,
850                    "<div class=\"methods\">",
851                )
852            )?;
853            for t in required_types {
854                write!(w, "{}", trait_item(cx, t, it))?;
855            }
856            w.write_str("</div>")?;
857        }
858        if !provided_types.is_empty() {
859            write!(
860                w,
861                "{}",
862                write_section_heading(
863                    "Provided Associated Types",
864                    "provided-associated-types",
865                    None,
866                    "<div class=\"methods\">",
867                )
868            )?;
869            for t in provided_types {
870                write!(w, "{}", trait_item(cx, t, it))?;
871            }
872            w.write_str("</div>")?;
873        }
874
875        // Output the documentation for each function individually
876        if !required_methods.is_empty() || must_implement_one_of_functions.is_some() {
877            write!(
878                w,
879                "{}",
880                write_section_heading(
881                    "Required Methods",
882                    "required-methods",
883                    None,
884                    "<div class=\"methods\">",
885                )
886            )?;
887
888            if let Some(list) = must_implement_one_of_functions.as_deref() {
889                write!(
890                    w,
891                    "<div class=\"stab must_implement\">At least one of the `{}` methods is required.</div>",
892                    fmt::from_fn(|f| list.iter().joined("`, `", f)),
893                )?;
894            }
895
896            for m in required_methods {
897                write!(w, "{}", trait_item(cx, m, it))?;
898            }
899            w.write_str("</div>")?;
900        }
901        if !provided_methods.is_empty() {
902            write!(
903                w,
904                "{}",
905                write_section_heading(
906                    "Provided Methods",
907                    "provided-methods",
908                    None,
909                    "<div class=\"methods\">",
910                )
911            )?;
912            for m in provided_methods {
913                write!(w, "{}", trait_item(cx, m, it))?;
914            }
915            w.write_str("</div>")?;
916        }
917
918        // If there are methods directly on this trait object, render them here.
919        write!(
920            w,
921            "{}",
922            render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
923        )?;
924
925        let mut extern_crates = FxIndexSet::default();
926
927        if !t.is_dyn_compatible(cx.tcx()) {
928            write!(
929                w,
930                "{}",
931                write_section_heading(
932                    "Dyn Compatibility",
933                    "dyn-compatibility",
934                    None,
935                    format!(
936                        "<div class=\"dyn-compatibility-info\"><p>This trait is <b>not</b> \
937                        <a href=\"{base}/reference/items/traits.html#dyn-compatibility\">dyn compatible</a>.</p>\
938                        <p><i>In older versions of Rust, dyn compatibility was called \"object safety\", \
939                        so this trait is not object safe.</i></p></div>",
940                        base = crate::clean::utils::DOC_RUST_LANG_ORG_VERSION
941                    ),
942                ),
943            )?;
944        }
945
946        if let Some(implementors) = cx.shared.cache.implementors.get(&it.item_id.expect_def_id()) {
947            // The DefId is for the first Type found with that name. The bool is
948            // if any Types with the same name but different DefId have been found.
949            let mut implementor_dups: FxHashMap<Symbol, (DefId, bool)> = FxHashMap::default();
950            for implementor in implementors {
951                if let Some(did) =
952                    implementor.inner_impl().for_.without_borrowed_ref().def_id(&cx.shared.cache)
953                    && !did.is_local()
954                {
955                    extern_crates.insert(did.krate);
956                }
957                match implementor.inner_impl().for_.without_borrowed_ref() {
958                    clean::Type::Path { path } if !path.is_assoc_ty() => {
959                        let did = path.def_id();
960                        let &mut (prev_did, ref mut has_duplicates) =
961                            implementor_dups.entry(path.last()).or_insert((did, false));
962                        if prev_did != did {
963                            *has_duplicates = true;
964                        }
965                    }
966                    _ => {}
967                }
968            }
969
970            let (local, mut foreign) =
971                implementors.iter().partition::<Vec<_>, _>(|i| i.is_on_local_type(cx));
972
973            let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) =
974                local.iter().partition(|i| i.inner_impl().kind.is_auto());
975
976            synthetic.sort_by_cached_key(|i| ImplString::new(i, cx));
977            concrete.sort_by_cached_key(|i| ImplString::new(i, cx));
978            foreign.sort_by_cached_key(|i| ImplString::new(i, cx));
979
980            if !foreign.is_empty() {
981                write!(
982                    w,
983                    "{}",
984                    write_section_heading(
985                        "Implementations on Foreign Types",
986                        "foreign-impls",
987                        None,
988                        ""
989                    )
990                )?;
991
992                for implementor in foreign {
993                    let provided_methods = implementor.inner_impl().provided_trait_methods(tcx);
994                    let assoc_link =
995                        AssocItemLink::GotoSource(implementor.impl_item.item_id, &provided_methods);
996                    write!(
997                        w,
998                        "{}",
999                        render_impl(
1000                            cx,
1001                            implementor,
1002                            it,
1003                            assoc_link,
1004                            RenderMode::Normal,
1005                            None,
1006                            &[],
1007                            ImplRenderingParameters {
1008                                show_def_docs: false,
1009                                show_default_items: false,
1010                                show_non_assoc_items: true,
1011                                toggle_open_by_default: false,
1012                            },
1013                        )
1014                    )?;
1015                }
1016            }
1017
1018            write!(
1019                w,
1020                "{}",
1021                write_section_heading(
1022                    "Implementors",
1023                    "implementors",
1024                    None,
1025                    "<div id=\"implementors-list\">",
1026                )
1027            )?;
1028            let mut negative_marker = NegativeMarker::new();
1029            for implementor in concrete {
1030                negative_marker.insert_if_needed(w, implementor)?;
1031                write!(w, "{}", render_implementor(cx, implementor, it, &implementor_dups, &[]))?;
1032            }
1033            w.write_str("</div>")?;
1034
1035            if t.is_auto(tcx) {
1036                write!(
1037                    w,
1038                    "{}",
1039                    write_section_heading(
1040                        "Auto implementors",
1041                        "synthetic-implementors",
1042                        None,
1043                        "<div id=\"synthetic-implementors-list\">",
1044                    )
1045                )?;
1046                let mut negative_marker = NegativeMarker::new();
1047                for implementor in synthetic {
1048                    negative_marker.insert_if_needed(w, implementor)?;
1049                    write!(
1050                        w,
1051                        "{}",
1052                        render_implementor(
1053                            cx,
1054                            implementor,
1055                            it,
1056                            &implementor_dups,
1057                            &collect_paths_for_type(
1058                                &implementor.inner_impl().for_,
1059                                &cx.shared.cache,
1060                            ),
1061                        )
1062                    )?;
1063                }
1064                w.write_str("</div>")?;
1065            }
1066        } else {
1067            // even without any implementations to write in, we still want the heading and list, so the
1068            // implementors javascript file pulled in below has somewhere to write the impls into
1069            write!(
1070                w,
1071                "{}",
1072                write_section_heading(
1073                    "Implementors",
1074                    "implementors",
1075                    None,
1076                    "<div id=\"implementors-list\"></div>",
1077                )
1078            )?;
1079
1080            if t.is_auto(tcx) {
1081                write!(
1082                    w,
1083                    "{}",
1084                    write_section_heading(
1085                        "Auto implementors",
1086                        "synthetic-implementors",
1087                        None,
1088                        "<div id=\"synthetic-implementors-list\"></div>",
1089                    )
1090                )?;
1091            }
1092        }
1093
1094        // [RUSTDOCIMPL] trait.impl
1095        //
1096        // Include implementors in crates that depend on the current crate.
1097        //
1098        // This is complicated by the way rustdoc is invoked, which is basically
1099        // the same way rustc is invoked: it gets called, one at a time, for each
1100        // crate. When building the rustdocs for the current crate, rustdoc can
1101        // see crate metadata for its dependencies, but cannot see metadata for its
1102        // dependents.
1103        //
1104        // To make this work, we generate a "hook" at this stage, and our
1105        // dependents can "plug in" to it when they build. For simplicity's sake,
1106        // it's [JSONP]: a JavaScript file with the data we need (and can parse),
1107        // surrounded by a tiny wrapper that the Rust side ignores, but allows the
1108        // JavaScript side to include without having to worry about Same Origin
1109        // Policy. The code for *that* is in `write_shared.rs`.
1110        //
1111        // This is further complicated by `#[doc(inline)]`. We want all copies
1112        // of an inlined trait to reference the same JS file, to address complex
1113        // dependency graphs like this one (lower crates depend on higher crates):
1114        //
1115        // ```text
1116        //  --------------------------------------------
1117        //  |            crate A: trait Foo            |
1118        //  --------------------------------------------
1119        //      |                               |
1120        //  --------------------------------    |
1121        //  | crate B: impl A::Foo for Bar |    |
1122        //  --------------------------------    |
1123        //      |                               |
1124        //  ---------------------------------------------
1125        //  | crate C: #[doc(inline)] use A::Foo as Baz |
1126        //  |          impl Baz for Quux                |
1127        //  ---------------------------------------------
1128        // ```
1129        //
1130        // Basically, we want `C::Baz` and `A::Foo` to show the same set of
1131        // impls, which is easier if they both treat `/trait.impl/A/trait.Foo.js`
1132        // as the Single Source of Truth.
1133        //
1134        // We also want the `impl Baz for Quux` to be written to
1135        // `trait.Foo.js`. However, when we generate plain HTML for `C::Baz`,
1136        // we're going to want to generate plain HTML for `impl Baz for Quux` too,
1137        // because that'll load faster, and it's better for SEO. And we don't want
1138        // the same impl to show up twice on the same page.
1139        //
1140        // To make this work, the trait.impl/A/trait.Foo.js JS file has a structure kinda
1141        // like this:
1142        //
1143        // ```js
1144        // JSONP({
1145        // "B": {"impl A::Foo for Bar"},
1146        // "C": {"impl Baz for Quux"},
1147        // });
1148        // ```
1149        //
1150        // First of all, this means we can rebuild a crate, and it'll replace its own
1151        // data if something changes. That is, `rustdoc` is idempotent. The other
1152        // advantage is that we can list the crates that get included in the HTML,
1153        // and ignore them when doing the JavaScript-based part of rendering.
1154        // So C's HTML will have something like this:
1155        //
1156        // ```html
1157        // <script src="/trait.impl/A/trait.Foo.js"
1158        //     data-ignore-extern-crates="A,B" async></script>
1159        // ```
1160        //
1161        // And, when the JS runs, anything in data-ignore-extern-crates is known
1162        // to already be in the HTML, and will be ignored.
1163        //
1164        // [JSONP]: https://en.wikipedia.org/wiki/JSONP
1165        let mut js_src_path: UrlPartsBuilder =
1166            iter::repeat_n("..", cx.current.len()).chain(iter::once("trait.impl")).collect();
1167        if let Some(did) = it.item_id.as_def_id()
1168            && let get_extern = { || cx.shared.cache.external_paths.get(&did).map(|s| &s.0) }
1169            && let Some(fqp) = cx.shared.cache.exact_paths.get(&did).or_else(get_extern)
1170        {
1171            js_src_path.extend(fqp[..fqp.len() - 1].iter().copied());
1172            js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), fqp.last().unwrap()));
1173        } else {
1174            js_src_path.extend(cx.current.iter().copied());
1175            js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), it.name.unwrap()));
1176        }
1177        let extern_crates = fmt::from_fn(|f| {
1178            if !extern_crates.is_empty() {
1179                f.write_str(" data-ignore-extern-crates=\"")?;
1180                extern_crates.iter().map(|&cnum| tcx.crate_name(cnum)).joined(",", f)?;
1181                f.write_str("\"")?;
1182            }
1183            Ok(())
1184        });
1185        write!(
1186            w,
1187            "<script src=\"{src}\"{extern_crates} async></script>",
1188            src = js_src_path.finish()
1189        )
1190    })
1191}
1192
1193fn item_trait_alias(
1194    cx: &Context<'_>,
1195    it: &clean::Item,
1196    t: &clean::TraitAlias,
1197) -> impl fmt::Display {
1198    fmt::from_fn(|w| {
1199        wrap_item(w, |w| {
1200            render_attributes_in_code(w, it, "", cx)?;
1201            write!(
1202                w,
1203                "trait {name}{generics} = {bounds}{where_clause};",
1204                name = it.name.unwrap(),
1205                generics = print_generics(&t.generics, cx),
1206                bounds = print_bounds(&t.bounds, true, cx),
1207                where_clause =
1208                    print_where_clause(&t.generics, cx, 0, Ending::NoNewline).maybe_display(),
1209            )
1210        })?;
1211
1212        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1213        // Render any items associated directly to this alias, as otherwise they
1214        // won't be visible anywhere in the docs. It would be nice to also show
1215        // associated items from the aliased type (see discussion in #32077), but
1216        // we need #14072 to make sense of the generics.
1217        write!(
1218            w,
1219            "{}",
1220            render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
1221        )
1222    })
1223}
1224
1225fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> impl fmt::Display {
1226    fmt::from_fn(|w| {
1227        wrap_item(w, |w| {
1228            render_attributes_in_code(w, it, "", cx)?;
1229            write!(
1230                w,
1231                "{vis}type {name}{generics}{where_clause} = {type_};",
1232                vis = visibility_print_with_space(it, cx),
1233                name = it.name.unwrap(),
1234                generics = print_generics(&t.generics, cx),
1235                where_clause =
1236                    print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display(),
1237                type_ = print_type(&t.type_, cx),
1238            )
1239        })?;
1240
1241        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1242
1243        if let Some(inner_type) = &t.inner_type {
1244            write!(w, "{}", write_section_heading("Aliased Type", "aliased-type", None, ""),)?;
1245
1246            match inner_type {
1247                clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
1248                    let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
1249                    let enum_def_id = ty.ty_adt_def().unwrap().did();
1250
1251                    DisplayEnum {
1252                        variants,
1253                        generics: &t.generics,
1254                        is_non_exhaustive: *is_non_exhaustive,
1255                        def_id: enum_def_id,
1256                    }
1257                    .render_into(cx, it, true, w)?;
1258                }
1259                clean::TypeAliasInnerType::Union { fields } => {
1260                    let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
1261                    let union_def_id = ty.ty_adt_def().unwrap().did();
1262
1263                    ItemUnion {
1264                        cx,
1265                        it,
1266                        fields,
1267                        generics: &t.generics,
1268                        is_type_alias: true,
1269                        def_id: union_def_id,
1270                    }
1271                    .render_into(w)?;
1272                }
1273                clean::TypeAliasInnerType::Struct { ctor_kind, fields } => {
1274                    let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
1275                    let struct_def_id = ty.ty_adt_def().unwrap().did();
1276
1277                    DisplayStruct {
1278                        ctor_kind: *ctor_kind,
1279                        generics: &t.generics,
1280                        fields,
1281                        def_id: struct_def_id,
1282                    }
1283                    .render_into(cx, it, true, w)?;
1284                }
1285            }
1286        } else {
1287            let def_id = it.item_id.expect_def_id();
1288            // Render any items associated directly to this alias, as otherwise they
1289            // won't be visible anywhere in the docs. It would be nice to also show
1290            // associated items from the aliased type (see discussion in #32077), but
1291            // we need #14072 to make sense of the generics.
1292            write!(
1293                w,
1294                "{}{}",
1295                render_assoc_items(cx, it, def_id, AssocItemRender::All),
1296                document_type_layout(cx, def_id)
1297            )?;
1298        }
1299
1300        // [RUSTDOCIMPL] type.impl
1301        //
1302        // Include type definitions from the alias target type.
1303        //
1304        // Earlier versions of this code worked by having `render_assoc_items`
1305        // include this data directly. That generates *O*`(types*impls)` of HTML
1306        // text, and some real crates have a lot of types and impls.
1307        //
1308        // To create the same UX without generating half a gigabyte of HTML for a
1309        // crate that only contains 20 megabytes of actual documentation[^115718],
1310        // rustdoc stashes these type-alias-inlined docs in a [JSONP]
1311        // "database-lite". The file itself is generated in `write_shared.rs`,
1312        // and hooks into functions provided by `main.js`.
1313        //
1314        // The format of `trait.impl` and `type.impl` JS files are superficially
1315        // similar. Each line, except the JSONP wrapper itself, belongs to a crate,
1316        // and they are otherwise separate (rustdoc should be idempotent). The
1317        // "meat" of the file is HTML strings, so the frontend code is very simple.
1318        // Links are relative to the doc root, though, so the frontend needs to fix
1319        // that up, and inlined docs can reuse these files.
1320        //
1321        // However, there are a few differences, caused by the sophisticated
1322        // features that type aliases have. Consider this crate graph:
1323        //
1324        // ```text
1325        //  ---------------------------------
1326        //  | crate A: struct Foo<T>        |
1327        //  |          type Bar = Foo<i32>  |
1328        //  |          impl X for Foo<i8>   |
1329        //  |          impl Y for Foo<i32>  |
1330        //  ---------------------------------
1331        //      |
1332        //  ----------------------------------
1333        //  | crate B: type Baz = A::Foo<i8> |
1334        //  |          type Xyy = A::Foo<i8> |
1335        //  |          impl Z for Xyy        |
1336        //  ----------------------------------
1337        // ```
1338        //
1339        // The type.impl/A/struct.Foo.js JS file has a structure kinda like this:
1340        //
1341        // ```js
1342        // JSONP({
1343        // "A": [["impl Y for Foo<i32>", "Y", "A::Bar"]],
1344        // "B": [["impl X for Foo<i8>", "X", "B::Baz", "B::Xyy"], ["impl Z for Xyy", "Z", "B::Baz"]],
1345        // });
1346        // ```
1347        //
1348        // When the type.impl file is loaded, only the current crate's docs are
1349        // actually used. The main reason to bundle them together is that there's
1350        // enough duplication in them for DEFLATE to remove the redundancy.
1351        //
1352        // The contents of a crate are a list of impl blocks, themselves
1353        // represented as lists. The first item in the sublist is the HTML block,
1354        // the second item is the name of the trait (which goes in the sidebar),
1355        // and all others are the names of type aliases that successfully match.
1356        //
1357        // This way:
1358        //
1359        // - There's no need to generate these files for types that have no aliases
1360        //   in the current crate. If a dependent crate makes a type alias, it'll
1361        //   take care of generating its own docs.
1362        // - There's no need to reimplement parts of the type checker in
1363        //   JavaScript. The Rust backend does the checking, and includes its
1364        //   results in the file.
1365        // - Docs defined directly on the type alias are dropped directly in the
1366        //   HTML by `render_assoc_items`, and are accessible without JavaScript.
1367        //   The JSONP file will not list impl items that are known to be part
1368        //   of the main HTML file already.
1369        //
1370        // [JSONP]: https://en.wikipedia.org/wiki/JSONP
1371        // [^115718]: https://github.com/rust-lang/rust/issues/115718
1372        let cache = &cx.shared.cache;
1373        if let Some(target_did) = t.type_.def_id(cache)
1374            && let get_extern = { || cache.external_paths.get(&target_did) }
1375            && let Some(&(ref target_fqp, target_type)) =
1376                cache.paths.get(&target_did).or_else(get_extern)
1377            && target_type.is_adt() // primitives cannot be inlined
1378            && let Some(self_did) = it.item_id.as_def_id()
1379            && let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) }
1380            && let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local)
1381        {
1382            let mut js_src_path: UrlPartsBuilder =
1383                iter::repeat_n("..", cx.current.len()).chain(iter::once("type.impl")).collect();
1384            js_src_path.extend(target_fqp[..target_fqp.len() - 1].iter().copied());
1385            js_src_path.push_fmt(format_args!("{target_type}.{}.js", target_fqp.last().unwrap()));
1386            let self_path = join_path_syms(self_fqp);
1387            write!(
1388                w,
1389                "<script src=\"{src}\" data-self-path=\"{self_path}\" async></script>",
1390                src = js_src_path.finish(),
1391            )?;
1392        }
1393        Ok(())
1394    })
1395}
1396
1397#[derive(Template)]
1398#[template(path = "item_union.html")]
1399struct ItemUnion<'a, 'cx> {
1400    cx: &'a Context<'cx>,
1401    it: &'a clean::Item,
1402    fields: &'a [clean::Item],
1403    generics: &'a clean::Generics,
1404    is_type_alias: bool,
1405    def_id: DefId,
1406}
1407
1408impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
1409    fn document(&self) -> impl fmt::Display {
1410        document(self.cx, self.it, None, HeadingOffset::H2)
1411    }
1412
1413    fn document_type_layout(&self) -> impl fmt::Display {
1414        let def_id = self.it.item_id.expect_def_id();
1415        document_type_layout(self.cx, def_id)
1416    }
1417
1418    fn render_assoc_items(&self) -> impl fmt::Display {
1419        let def_id = self.it.item_id.expect_def_id();
1420        render_assoc_items(self.cx, self.it, def_id, AssocItemRender::All)
1421    }
1422
1423    fn render_union(&self) -> impl Display {
1424        render_union(
1425            self.it,
1426            Some(self.generics),
1427            self.fields,
1428            self.def_id,
1429            self.is_type_alias,
1430            self.cx,
1431        )
1432    }
1433
1434    fn print_field_attrs(&self, field: &'a clean::Item) -> impl Display {
1435        fmt::from_fn(move |w| {
1436            render_attributes_in_code(w, field, "", self.cx)?;
1437            Ok(())
1438        })
1439    }
1440
1441    fn document_field(&self, field: &'a clean::Item) -> impl Display {
1442        document(self.cx, field, Some(self.it), HeadingOffset::H3)
1443    }
1444
1445    fn stability_field(&self, field: &clean::Item) -> Option<String> {
1446        field.stability_class(self.cx.tcx())
1447    }
1448
1449    fn print_ty(&self, ty: &'a clean::Type) -> impl Display {
1450        print_type(ty, self.cx)
1451    }
1452
1453    // FIXME (GuillaumeGomez): When <https://github.com/askama-rs/askama/issues/452> is implemented,
1454    // we can replace the returned value with:
1455    //
1456    // `iter::Peekable<impl Iterator<Item = (&'a clean::Item, &'a clean::Type)>>`
1457    //
1458    // And update `item_union.html`.
1459    fn fields_iter(&self) -> impl Iterator<Item = (&'a clean::Item, &'a clean::Type)> {
1460        self.fields.iter().filter_map(|f| match f.kind {
1461            clean::StructFieldItem(ref ty) => Some((f, ty)),
1462            _ => None,
1463        })
1464    }
1465}
1466
1467fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display {
1468    fmt::from_fn(|w| {
1469        ItemUnion {
1470            cx,
1471            it,
1472            fields: &s.fields,
1473            generics: &s.generics,
1474            is_type_alias: false,
1475            def_id: it.def_id().unwrap(),
1476        }
1477        .render_into(w)?;
1478        Ok(())
1479    })
1480}
1481
1482fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Display {
1483    fmt::from_fn(|f| {
1484        if !s.is_empty()
1485            && s.iter().all(|field| {
1486                matches!(field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
1487            })
1488        {
1489            return f.write_str("<span class=\"comment\">/* private fields */</span>");
1490        }
1491
1492        s.iter()
1493            .map(|ty| {
1494                fmt::from_fn(|f| match ty.kind {
1495                    clean::StrippedItem(box clean::StructFieldItem(_)) => f.write_str("_"),
1496                    clean::StructFieldItem(ref ty) => write!(f, "{}", print_type(ty, cx)),
1497                    _ => unreachable!(),
1498                })
1499            })
1500            .joined(", ", f)
1501    })
1502}
1503
1504struct DisplayEnum<'clean> {
1505    variants: &'clean IndexVec<VariantIdx, clean::Item>,
1506    generics: &'clean clean::Generics,
1507    is_non_exhaustive: bool,
1508    def_id: DefId,
1509}
1510
1511impl<'clean> DisplayEnum<'clean> {
1512    fn render_into<W: fmt::Write>(
1513        self,
1514        cx: &Context<'_>,
1515        it: &clean::Item,
1516        is_type_alias: bool,
1517        w: &mut W,
1518    ) -> fmt::Result {
1519        let non_stripped_variant_count = self.variants.iter().filter(|i| !i.is_stripped()).count();
1520        let variants_len = self.variants.len();
1521        let has_stripped_entries = variants_len != non_stripped_variant_count;
1522
1523        wrap_item(w, |w| {
1524            if is_type_alias {
1525                // For now the only attributes we render for type aliases are `repr` attributes.
1526                render_repr_attribute_in_code(w, cx, self.def_id)?;
1527            } else {
1528                render_attributes_in_code(w, it, "", cx)?;
1529            }
1530            write!(
1531                w,
1532                "{}enum {}{}{}",
1533                visibility_print_with_space(it, cx),
1534                it.name.unwrap(),
1535                print_generics(&self.generics, cx),
1536                render_enum_fields(
1537                    cx,
1538                    Some(self.generics),
1539                    self.variants,
1540                    non_stripped_variant_count,
1541                    has_stripped_entries,
1542                    self.is_non_exhaustive,
1543                    self.def_id,
1544                ),
1545            )
1546        })?;
1547
1548        let def_id = it.item_id.expect_def_id();
1549        let layout_def_id = if is_type_alias {
1550            self.def_id
1551        } else {
1552            write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1553            // We don't return the same `DefId` since the layout size of the type alias might be
1554            // different since we might have more information on the generics.
1555            def_id
1556        };
1557
1558        if non_stripped_variant_count != 0 {
1559            write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?;
1560        }
1561        write!(
1562            w,
1563            "{}{}",
1564            render_assoc_items(cx, it, def_id, AssocItemRender::All),
1565            document_type_layout(cx, layout_def_id)
1566        )
1567    }
1568}
1569
1570fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display {
1571    fmt::from_fn(|w| {
1572        DisplayEnum {
1573            variants: &e.variants,
1574            generics: &e.generics,
1575            is_non_exhaustive: it.is_non_exhaustive(),
1576            def_id: it.def_id().unwrap(),
1577        }
1578        .render_into(cx, it, false, w)
1579    })
1580}
1581
1582/// It'll return false if any variant is not a C-like variant. Otherwise it'll return true if at
1583/// least one of them has an explicit discriminant or if the enum has `#[repr(C)]` or an integer
1584/// `repr`.
1585fn should_show_enum_discriminant(
1586    cx: &Context<'_>,
1587    enum_def_id: DefId,
1588    variants: &IndexVec<VariantIdx, clean::Item>,
1589) -> bool {
1590    let mut has_variants_with_value = false;
1591    for variant in variants {
1592        if let clean::VariantItem(ref var) = variant.kind
1593            && matches!(var.kind, clean::VariantKind::CLike)
1594        {
1595            has_variants_with_value |= var.discriminant.is_some();
1596        } else {
1597            return false;
1598        }
1599    }
1600    if has_variants_with_value {
1601        return true;
1602    }
1603    let repr = cx.tcx().adt_def(enum_def_id).repr();
1604    repr.c() || repr.int.is_some()
1605}
1606
1607fn display_c_like_variant(
1608    cx: &Context<'_>,
1609    item: &clean::Item,
1610    variant: &clean::Variant,
1611    index: VariantIdx,
1612    should_show_enum_discriminant: bool,
1613    enum_def_id: DefId,
1614) -> impl fmt::Display {
1615    fmt::from_fn(move |w| {
1616        let name = item.name.unwrap();
1617        if let Some(ref value) = variant.discriminant {
1618            write!(w, "{} = {}", name.as_str(), value.value(cx.tcx(), true))?;
1619        } else if should_show_enum_discriminant {
1620            let adt_def = cx.tcx().adt_def(enum_def_id);
1621            let discr = adt_def.discriminant_for_variant(cx.tcx(), index);
1622            // Use `discr`'s `Display` impl to render the value with the correct
1623            // signedness, including proper sign-extension for signed types.
1624            write!(w, "{} = {}", name.as_str(), discr)?;
1625        } else {
1626            write!(w, "{name}")?;
1627        }
1628        Ok(())
1629    })
1630}
1631
1632fn render_enum_fields(
1633    cx: &Context<'_>,
1634    g: Option<&clean::Generics>,
1635    variants: &IndexVec<VariantIdx, clean::Item>,
1636    count_variants: usize,
1637    has_stripped_entries: bool,
1638    is_non_exhaustive: bool,
1639    enum_def_id: DefId,
1640) -> impl fmt::Display {
1641    fmt::from_fn(move |w| {
1642        let should_show_enum_discriminant =
1643            should_show_enum_discriminant(cx, enum_def_id, variants);
1644        if let Some(generics) = g
1645            && let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline)
1646        {
1647            write!(w, "{where_clause}")?;
1648        } else {
1649            // If there wasn't a `where` clause, we add a whitespace.
1650            w.write_char(' ')?;
1651        }
1652
1653        let variants_stripped = has_stripped_entries;
1654        if count_variants == 0 && !variants_stripped {
1655            w.write_str("{}")
1656        } else {
1657            w.write_str("{\n")?;
1658            let toggle = should_hide_fields(count_variants);
1659            if toggle {
1660                toggle_open(&mut *w, format_args!("{count_variants} variants"));
1661            }
1662            const TAB: &str = "    ";
1663            for (index, v) in variants.iter_enumerated() {
1664                if v.is_stripped() {
1665                    continue;
1666                }
1667                render_attributes_in_code(w, v, TAB, cx)?;
1668                w.write_str(TAB)?;
1669                match v.kind {
1670                    clean::VariantItem(ref var) => match var.kind {
1671                        clean::VariantKind::CLike => {
1672                            write!(
1673                                w,
1674                                "{}",
1675                                display_c_like_variant(
1676                                    cx,
1677                                    v,
1678                                    var,
1679                                    index,
1680                                    should_show_enum_discriminant,
1681                                    enum_def_id,
1682                                )
1683                            )?;
1684                        }
1685                        clean::VariantKind::Tuple(ref s) => {
1686                            write!(w, "{}({})", v.name.unwrap(), print_tuple_struct_fields(cx, s))?;
1687                        }
1688                        clean::VariantKind::Struct(ref s) => {
1689                            write!(
1690                                w,
1691                                "{}",
1692                                render_struct(v, None, None, &s.fields, TAB, false, cx)
1693                            )?;
1694                        }
1695                    },
1696                    _ => unreachable!(),
1697                }
1698                w.write_str(",\n")?;
1699            }
1700
1701            if variants_stripped && !is_non_exhaustive {
1702                w.write_str("    <span class=\"comment\">// some variants omitted</span>\n")?;
1703            }
1704            if toggle {
1705                toggle_close(&mut *w);
1706            }
1707            w.write_str("}")
1708        }
1709    })
1710}
1711
1712fn item_variants(
1713    cx: &Context<'_>,
1714    it: &clean::Item,
1715    variants: &IndexVec<VariantIdx, clean::Item>,
1716    enum_def_id: DefId,
1717) -> impl fmt::Display {
1718    fmt::from_fn(move |w| {
1719        let tcx = cx.tcx();
1720        write!(
1721            w,
1722            "{}",
1723            write_section_heading(
1724                &format!("Variants{}", document_non_exhaustive_header(it)),
1725                "variants",
1726                Some("variants"),
1727                format!("{}<div class=\"variants\">", document_non_exhaustive(it)),
1728            ),
1729        )?;
1730
1731        let should_show_enum_discriminant =
1732            should_show_enum_discriminant(cx, enum_def_id, variants);
1733        for (index, variant) in variants.iter_enumerated() {
1734            if variant.is_stripped() {
1735                continue;
1736            }
1737            let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
1738            write!(
1739                w,
1740                "<section id=\"{id}\" class=\"variant\">\
1741                    <a href=\"#{id}\" class=\"anchor\">§</a>\
1742                    {}\
1743                    <h3 class=\"code-header\">",
1744                render_stability_since_raw_with_extra(
1745                    variant.stable_since(tcx),
1746                    variant.const_stability(tcx),
1747                    " rightside",
1748                )
1749                .maybe_display()
1750            )?;
1751            render_attributes_in_code(w, variant, "", cx)?;
1752            if let clean::VariantItem(ref var) = variant.kind
1753                && let clean::VariantKind::CLike = var.kind
1754            {
1755                write!(
1756                    w,
1757                    "{}",
1758                    display_c_like_variant(
1759                        cx,
1760                        variant,
1761                        var,
1762                        index,
1763                        should_show_enum_discriminant,
1764                        enum_def_id,
1765                    )
1766                )?;
1767            } else {
1768                w.write_str(variant.name.unwrap().as_str())?;
1769            }
1770
1771            let clean::VariantItem(variant_data) = &variant.kind else { unreachable!() };
1772
1773            if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
1774                write!(w, "({})", print_tuple_struct_fields(cx, s))?;
1775            }
1776            w.write_str("</h3></section>")?;
1777
1778            write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4))?;
1779
1780            let heading_and_fields = match &variant_data.kind {
1781                clean::VariantKind::Struct(s) => {
1782                    // If there is no field to display, no need to add the heading.
1783                    if s.fields.iter().any(|f| !f.is_doc_hidden()) {
1784                        Some(("Fields", &s.fields))
1785                    } else {
1786                        None
1787                    }
1788                }
1789                clean::VariantKind::Tuple(fields) => {
1790                    // Documentation on tuple variant fields is rare, so to reduce noise we only emit
1791                    // the section if at least one field is documented.
1792                    if fields.iter().any(|f| !f.doc_value().is_empty()) {
1793                        Some(("Tuple Fields", fields))
1794                    } else {
1795                        None
1796                    }
1797                }
1798                clean::VariantKind::CLike => None,
1799            };
1800
1801            if let Some((heading, fields)) = heading_and_fields {
1802                let variant_id =
1803                    cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
1804                write!(
1805                    w,
1806                    "<div class=\"sub-variant\" id=\"{variant_id}\">\
1807                        <h4>{heading}</h4>\
1808                        {}",
1809                    document_non_exhaustive(variant)
1810                )?;
1811                for field in fields {
1812                    match field.kind {
1813                        clean::StrippedItem(box clean::StructFieldItem(_)) => {}
1814                        clean::StructFieldItem(ref ty) => {
1815                            let id = cx.derive_id(format!(
1816                                "variant.{}.field.{}",
1817                                variant.name.unwrap(),
1818                                field.name.unwrap()
1819                            ));
1820                            write!(
1821                                w,
1822                                "<div class=\"sub-variant-field\">\
1823                                    <span id=\"{id}\" class=\"section-header\">\
1824                                        <a href=\"#{id}\" class=\"anchor field\">§</a>\
1825                                        <code>"
1826                            )?;
1827                            render_attributes_in_code(w, field, "", cx)?;
1828                            write!(
1829                                w,
1830                                "{f}: {t}</code>\
1831                                    </span>\
1832                                    {doc}\
1833                                </div>",
1834                                f = field.name.unwrap(),
1835                                t = print_type(ty, cx),
1836                                doc = document(cx, field, Some(variant), HeadingOffset::H5),
1837                            )?;
1838                        }
1839                        _ => unreachable!(),
1840                    }
1841                }
1842                w.write_str("</div>")?;
1843            }
1844        }
1845        w.write_str("</div>")
1846    })
1847}
1848
1849fn item_macro(cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) -> impl fmt::Display {
1850    fmt::from_fn(|w| {
1851        wrap_item(w, |w| {
1852            // FIXME: Also print `#[doc(hidden)]` for `macro_rules!` if it `is_doc_hidden`.
1853            render_attributes_in_code(w, it, "", cx)?;
1854            if !t.macro_rules {
1855                write!(w, "{}", visibility_print_with_space(it, cx))?;
1856            }
1857            write!(w, "{}", Escape(&t.source))
1858        })?;
1859        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
1860    })
1861}
1862
1863fn item_proc_macro(cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) -> impl fmt::Display {
1864    fmt::from_fn(|w| {
1865        wrap_item(w, |w| {
1866            let name = it.name.expect("proc-macros always have names");
1867            match m.kind {
1868                MacroKind::Bang => {
1869                    write!(w, "{name}!() {{ <span class=\"comment\">/* proc-macro */</span> }}")?;
1870                }
1871                MacroKind::Attr => {
1872                    write!(w, "#[{name}]")?;
1873                }
1874                MacroKind::Derive => {
1875                    write!(w, "#[derive({name})]")?;
1876                    if !m.helpers.is_empty() {
1877                        w.write_str(
1878                            "\n{\n    \
1879                            <span class=\"comment\">// Attributes available to this derive:</span>\n",
1880                        )?;
1881                        for attr in &m.helpers {
1882                            writeln!(w, "    #[{attr}]")?;
1883                        }
1884                        w.write_str("}\n")?;
1885                    }
1886                }
1887            }
1888            fmt::Result::Ok(())
1889        })?;
1890        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
1891    })
1892}
1893
1894fn item_primitive(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
1895    fmt::from_fn(|w| {
1896        let def_id = it.item_id.expect_def_id();
1897        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1898        if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
1899            write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All))
1900        } else {
1901            // We handle the "reference" primitive type on its own because we only want to list
1902            // implementations on generic types.
1903            let (concrete, synthetic, blanket_impl) =
1904                get_filtered_impls_for_reference(&cx.shared, it);
1905
1906            render_all_impls(w, cx, it, &concrete, &synthetic, &blanket_impl)
1907        }
1908    })
1909}
1910
1911fn item_constant(
1912    cx: &Context<'_>,
1913    it: &clean::Item,
1914    generics: &clean::Generics,
1915    ty: &clean::Type,
1916    c: &clean::ConstantKind,
1917) -> impl fmt::Display {
1918    fmt::from_fn(|w| {
1919        wrap_item(w, |w| {
1920            let tcx = cx.tcx();
1921            render_attributes_in_code(w, it, "", cx)?;
1922
1923            write!(
1924                w,
1925                "{vis}const {name}{generics}: {typ}{where_clause}",
1926                vis = visibility_print_with_space(it, cx),
1927                name = it.name.unwrap(),
1928                generics = print_generics(generics, cx),
1929                typ = print_type(ty, cx),
1930                where_clause =
1931                    print_where_clause(generics, cx, 0, Ending::NoNewline).maybe_display(),
1932            )?;
1933
1934            // FIXME: The code below now prints
1935            //            ` = _; // 100i32`
1936            //        if the expression is
1937            //            `50 + 50`
1938            //        which looks just wrong.
1939            //        Should we print
1940            //            ` = 100i32;`
1941            //        instead?
1942
1943            let value = c.value(tcx);
1944            let is_literal = c.is_literal(tcx);
1945            let expr = c.expr(tcx);
1946            if value.is_some() || is_literal {
1947                write!(w, " = {expr};", expr = Escape(&expr))?;
1948            } else {
1949                w.write_str(";")?;
1950            }
1951
1952            if !is_literal && let Some(value) = &value {
1953                let value_lowercase = value.to_lowercase();
1954                let expr_lowercase = expr.to_lowercase();
1955
1956                if value_lowercase != expr_lowercase
1957                    && value_lowercase.trim_end_matches("i32") != expr_lowercase
1958                {
1959                    write!(w, " // {value}", value = Escape(value))?;
1960                }
1961            }
1962            Ok::<(), fmt::Error>(())
1963        })?;
1964
1965        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
1966    })
1967}
1968
1969struct DisplayStruct<'a> {
1970    ctor_kind: Option<CtorKind>,
1971    generics: &'a clean::Generics,
1972    fields: &'a [clean::Item],
1973    def_id: DefId,
1974}
1975
1976impl<'a> DisplayStruct<'a> {
1977    fn render_into<W: fmt::Write>(
1978        self,
1979        cx: &Context<'_>,
1980        it: &clean::Item,
1981        is_type_alias: bool,
1982        w: &mut W,
1983    ) -> fmt::Result {
1984        wrap_item(w, |w| {
1985            if is_type_alias {
1986                // For now the only attributes we render for type aliases are `repr` attributes.
1987                render_repr_attribute_in_code(w, cx, self.def_id)?;
1988            } else {
1989                render_attributes_in_code(w, it, "", cx)?;
1990            }
1991            write!(
1992                w,
1993                "{}",
1994                render_struct(it, Some(self.generics), self.ctor_kind, self.fields, "", true, cx)
1995            )
1996        })?;
1997
1998        if !is_type_alias {
1999            write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
2000        }
2001
2002        let def_id = it.item_id.expect_def_id();
2003        write!(
2004            w,
2005            "{}{}{}",
2006            item_fields(cx, it, self.fields, self.ctor_kind),
2007            render_assoc_items(cx, it, def_id, AssocItemRender::All),
2008            document_type_layout(cx, def_id),
2009        )
2010    }
2011}
2012
2013fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display {
2014    fmt::from_fn(|w| {
2015        DisplayStruct {
2016            ctor_kind: s.ctor_kind,
2017            generics: &s.generics,
2018            fields: s.fields.as_slice(),
2019            def_id: it.def_id().unwrap(),
2020        }
2021        .render_into(cx, it, false, w)
2022    })
2023}
2024
2025fn item_fields(
2026    cx: &Context<'_>,
2027    it: &clean::Item,
2028    fields: &[clean::Item],
2029    ctor_kind: Option<CtorKind>,
2030) -> impl fmt::Display {
2031    fmt::from_fn(move |w| {
2032        let mut fields = fields
2033            .iter()
2034            .filter_map(|f| match f.kind {
2035                clean::StructFieldItem(ref ty) => Some((f, ty)),
2036                _ => None,
2037            })
2038            .peekable();
2039        if let None | Some(CtorKind::Fn) = ctor_kind
2040            && fields.peek().is_some()
2041        {
2042            let title = format!(
2043                "{}{}",
2044                if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
2045                document_non_exhaustive_header(it),
2046            );
2047            write!(
2048                w,
2049                "{}",
2050                write_section_heading(
2051                    &title,
2052                    "fields",
2053                    Some("fields"),
2054                    document_non_exhaustive(it)
2055                )
2056            )?;
2057            for (index, (field, ty)) in fields.enumerate() {
2058                let field_name =
2059                    field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string());
2060                let id = cx.derive_id(format!("{typ}.{field_name}", typ = ItemType::StructField));
2061                write!(
2062                    w,
2063                    "<span id=\"{id}\" class=\"{item_type} section-header\">\
2064                        <a href=\"#{id}\" class=\"anchor field\">§</a>\
2065                        <code>",
2066                    item_type = ItemType::StructField,
2067                )?;
2068                render_attributes_in_code(w, field, "", cx)?;
2069                write!(
2070                    w,
2071                    "{field_name}: {ty}</code>\
2072                    </span>\
2073                    {doc}",
2074                    ty = print_type(ty, cx),
2075                    doc = document(cx, field, Some(it), HeadingOffset::H3),
2076                )?;
2077            }
2078        }
2079        Ok(())
2080    })
2081}
2082
2083fn item_static(
2084    cx: &Context<'_>,
2085    it: &clean::Item,
2086    s: &clean::Static,
2087    safety: Option<hir::Safety>,
2088) -> impl fmt::Display {
2089    fmt::from_fn(move |w| {
2090        wrap_item(w, |w| {
2091            render_attributes_in_code(w, it, "", cx)?;
2092            write!(
2093                w,
2094                "{vis}{safe}static {mutability}{name}: {typ}",
2095                vis = visibility_print_with_space(it, cx),
2096                safe = safety.map(|safe| safe.prefix_str()).unwrap_or(""),
2097                mutability = s.mutability.print_with_space(),
2098                name = it.name.unwrap(),
2099                typ = print_type(&s.type_, cx)
2100            )
2101        })?;
2102
2103        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
2104    })
2105}
2106
2107fn item_foreign_type(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
2108    fmt::from_fn(|w| {
2109        wrap_item(w, |w| {
2110            w.write_str("extern {\n")?;
2111            render_attributes_in_code(w, it, "", cx)?;
2112            write!(w, "    {}type {};\n}}", visibility_print_with_space(it, cx), it.name.unwrap())
2113        })?;
2114
2115        write!(
2116            w,
2117            "{}{}",
2118            document(cx, it, None, HeadingOffset::H2),
2119            render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
2120        )
2121    })
2122}
2123
2124fn item_keyword_or_attribute(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
2125    document(cx, it, None, HeadingOffset::H2)
2126}
2127
2128/// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order).
2129///
2130/// This code is copied from [`rustfmt`], and should probably be released as a crate at some point.
2131///
2132/// [`rustfmt`]:https://github.com/rust-lang/rustfmt/blob/rustfmt-2.0.0-rc.2/src/formatting/reorder.rs#L32
2133pub(crate) fn compare_names(left: &str, right: &str) -> Ordering {
2134    let mut left = left.chars().peekable();
2135    let mut right = right.chars().peekable();
2136
2137    loop {
2138        // The strings are equal so far and not inside a number in both sides
2139        let (l, r) = match (left.next(), right.next()) {
2140            // Is this the end of both strings?
2141            (None, None) => return Ordering::Equal,
2142            // If for one, the shorter one is considered smaller
2143            (None, Some(_)) => return Ordering::Less,
2144            (Some(_), None) => return Ordering::Greater,
2145            (Some(l), Some(r)) => (l, r),
2146        };
2147        let next_ordering = match (l.to_digit(10), r.to_digit(10)) {
2148            // If neither is a digit, just compare them
2149            (None, None) => Ord::cmp(&l, &r),
2150            // The one with shorter non-digit run is smaller
2151            // For `strverscmp` it's smaller iff next char in longer is greater than digits
2152            (None, Some(_)) => Ordering::Greater,
2153            (Some(_), None) => Ordering::Less,
2154            // If both start numbers, we have to compare the numbers
2155            (Some(l), Some(r)) => {
2156                if l == 0 || r == 0 {
2157                    // Fraction mode: compare as if there was leading `0.`
2158                    let ordering = Ord::cmp(&l, &r);
2159                    if ordering != Ordering::Equal {
2160                        return ordering;
2161                    }
2162                    loop {
2163                        // Get next pair
2164                        let (l, r) = match (left.peek(), right.peek()) {
2165                            // Is this the end of both strings?
2166                            (None, None) => return Ordering::Equal,
2167                            // If for one, the shorter one is considered smaller
2168                            (None, Some(_)) => return Ordering::Less,
2169                            (Some(_), None) => return Ordering::Greater,
2170                            (Some(l), Some(r)) => (l, r),
2171                        };
2172                        // Are they digits?
2173                        match (l.to_digit(10), r.to_digit(10)) {
2174                            // If out of digits, use the stored ordering due to equal length
2175                            (None, None) => break Ordering::Equal,
2176                            // If one is shorter, it's smaller
2177                            (None, Some(_)) => return Ordering::Less,
2178                            (Some(_), None) => return Ordering::Greater,
2179                            // If both are digits, consume them and take into account
2180                            (Some(l), Some(r)) => {
2181                                left.next();
2182                                right.next();
2183                                let ordering = Ord::cmp(&l, &r);
2184                                if ordering != Ordering::Equal {
2185                                    return ordering;
2186                                }
2187                            }
2188                        }
2189                    }
2190                } else {
2191                    // Integer mode
2192                    let mut same_length_ordering = Ord::cmp(&l, &r);
2193                    loop {
2194                        // Get next pair
2195                        let (l, r) = match (left.peek(), right.peek()) {
2196                            // Is this the end of both strings?
2197                            (None, None) => return same_length_ordering,
2198                            // If for one, the shorter one is considered smaller
2199                            (None, Some(_)) => return Ordering::Less,
2200                            (Some(_), None) => return Ordering::Greater,
2201                            (Some(l), Some(r)) => (l, r),
2202                        };
2203                        // Are they digits?
2204                        match (l.to_digit(10), r.to_digit(10)) {
2205                            // If out of digits, use the stored ordering due to equal length
2206                            (None, None) => break same_length_ordering,
2207                            // If one is shorter, it's smaller
2208                            (None, Some(_)) => return Ordering::Less,
2209                            (Some(_), None) => return Ordering::Greater,
2210                            // If both are digits, consume them and take into account
2211                            (Some(l), Some(r)) => {
2212                                left.next();
2213                                right.next();
2214                                same_length_ordering = same_length_ordering.then(Ord::cmp(&l, &r));
2215                            }
2216                        }
2217                    }
2218                }
2219            }
2220        };
2221        if next_ordering != Ordering::Equal {
2222            return next_ordering;
2223        }
2224    }
2225}
2226
2227pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
2228    let mut s = join_path_syms(&cx.current);
2229    s.push_str("::");
2230    s.push_str(item.name.unwrap().as_str());
2231    s
2232}
2233
2234pub(super) fn print_item_path(ty: ItemType, name: &str) -> impl Display {
2235    fmt::from_fn(move |f| match ty {
2236        ItemType::Module => write!(f, "{}index.html", ensure_trailing_slash(name)),
2237        _ => write!(f, "{ty}.{name}.html"),
2238    })
2239}
2240
2241fn print_bounds(
2242    bounds: &[clean::GenericBound],
2243    trait_alias: bool,
2244    cx: &Context<'_>,
2245) -> impl Display {
2246    (!bounds.is_empty())
2247        .then_some(fmt::from_fn(move |f| {
2248            let has_lots_of_bounds = bounds.len() > 2;
2249            let inter_str = if has_lots_of_bounds { "\n    + " } else { " + " };
2250            if !trait_alias {
2251                if has_lots_of_bounds {
2252                    f.write_str(":\n    ")?;
2253                } else {
2254                    f.write_str(": ")?;
2255                }
2256            }
2257
2258            bounds.iter().map(|p| print_generic_bound(p, cx)).joined(inter_str, f)
2259        }))
2260        .maybe_display()
2261}
2262
2263fn wrap_item<W, F>(w: &mut W, f: F) -> fmt::Result
2264where
2265    W: fmt::Write,
2266    F: FnOnce(&mut W) -> fmt::Result,
2267{
2268    w.write_str(r#"<pre class="rust item-decl"><code>"#)?;
2269    f(w)?;
2270    w.write_str("</code></pre>")
2271}
2272
2273#[derive(PartialEq, Eq)]
2274struct ImplString {
2275    rendered: String,
2276    is_negative: bool,
2277}
2278
2279impl ImplString {
2280    fn new(i: &Impl, cx: &Context<'_>) -> ImplString {
2281        let impl_ = i.inner_impl();
2282        ImplString {
2283            is_negative: impl_.is_negative_trait_impl(),
2284            rendered: format!("{}", print_impl(impl_, false, cx)),
2285        }
2286    }
2287}
2288
2289impl PartialOrd for ImplString {
2290    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2291        Some(Ord::cmp(self, other))
2292    }
2293}
2294
2295impl Ord for ImplString {
2296    fn cmp(&self, other: &Self) -> Ordering {
2297        // We sort negative impls first.
2298        match (self.is_negative, other.is_negative) {
2299            (false, true) => Ordering::Greater,
2300            (true, false) => Ordering::Less,
2301            _ => compare_names(&self.rendered, &other.rendered),
2302        }
2303    }
2304}
2305
2306fn render_implementor(
2307    cx: &Context<'_>,
2308    implementor: &Impl,
2309    trait_: &clean::Item,
2310    implementor_dups: &FxHashMap<Symbol, (DefId, bool)>,
2311    aliases: &[String],
2312) -> impl fmt::Display {
2313    // If there's already another implementor that has the same abridged name, use the
2314    // full path, for example in `std::iter::ExactSizeIterator`
2315    let use_absolute = match implementor.inner_impl().for_ {
2316        clean::Type::Path { ref path, .. }
2317        | clean::BorrowedRef { type_: box clean::Type::Path { ref path, .. }, .. }
2318            if !path.is_assoc_ty() =>
2319        {
2320            implementor_dups[&path.last()].1
2321        }
2322        _ => false,
2323    };
2324    render_impl(
2325        cx,
2326        implementor,
2327        trait_,
2328        AssocItemLink::Anchor(None),
2329        RenderMode::Normal,
2330        Some(use_absolute),
2331        aliases,
2332        ImplRenderingParameters {
2333            show_def_docs: false,
2334            show_default_items: false,
2335            show_non_assoc_items: false,
2336            toggle_open_by_default: false,
2337        },
2338    )
2339}
2340
2341fn render_union(
2342    it: &clean::Item,
2343    g: Option<&clean::Generics>,
2344    fields: &[clean::Item],
2345    def_id: DefId,
2346    is_type_alias: bool,
2347    cx: &Context<'_>,
2348) -> impl Display {
2349    fmt::from_fn(move |mut f| {
2350        if is_type_alias {
2351            // For now the only attributes we render for type aliases are `repr` attributes.
2352            render_repr_attribute_in_code(f, cx, def_id)?;
2353        } else {
2354            render_attributes_in_code(f, it, "", cx)?;
2355        }
2356        write!(f, "{}union {}", visibility_print_with_space(it, cx), it.name.unwrap(),)?;
2357
2358        let where_displayed = if let Some(generics) = g {
2359            write!(f, "{}", print_generics(generics, cx))?;
2360            if let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline) {
2361                write!(f, "{where_clause}")?;
2362                true
2363            } else {
2364                false
2365            }
2366        } else {
2367            false
2368        };
2369
2370        // If there wasn't a `where` clause, we add a whitespace.
2371        if !where_displayed {
2372            f.write_str(" ")?;
2373        }
2374
2375        writeln!(f, "{{")?;
2376        let count_fields =
2377            fields.iter().filter(|field| matches!(field.kind, clean::StructFieldItem(..))).count();
2378        let toggle = should_hide_fields(count_fields);
2379        if toggle {
2380            toggle_open(&mut f, format_args!("{count_fields} fields"));
2381        }
2382
2383        for field in fields {
2384            if let clean::StructFieldItem(ref ty) = field.kind {
2385                render_attributes_in_code(&mut f, field, "    ", cx)?;
2386                writeln!(
2387                    f,
2388                    "    {}{}: {},",
2389                    visibility_print_with_space(field, cx),
2390                    field.name.unwrap(),
2391                    print_type(ty, cx)
2392                )?;
2393            }
2394        }
2395
2396        if it.has_stripped_entries().unwrap() {
2397            writeln!(f, "    <span class=\"comment\">/* private fields */</span>")?;
2398        }
2399        if toggle {
2400            toggle_close(&mut f);
2401        }
2402        f.write_str("}").unwrap();
2403        Ok(())
2404    })
2405}
2406
2407fn render_struct(
2408    it: &clean::Item,
2409    g: Option<&clean::Generics>,
2410    ty: Option<CtorKind>,
2411    fields: &[clean::Item],
2412    tab: &str,
2413    structhead: bool,
2414    cx: &Context<'_>,
2415) -> impl fmt::Display {
2416    fmt::from_fn(move |w| {
2417        write!(
2418            w,
2419            "{}{}{}",
2420            visibility_print_with_space(it, cx),
2421            if structhead { "struct " } else { "" },
2422            it.name.unwrap()
2423        )?;
2424        if let Some(g) = g {
2425            write!(w, "{}", print_generics(g, cx))?;
2426        }
2427        write!(
2428            w,
2429            "{}",
2430            render_struct_fields(
2431                g,
2432                ty,
2433                fields,
2434                tab,
2435                structhead,
2436                it.has_stripped_entries().unwrap_or(false),
2437                cx,
2438            )
2439        )
2440    })
2441}
2442
2443fn render_struct_fields(
2444    g: Option<&clean::Generics>,
2445    ty: Option<CtorKind>,
2446    fields: &[clean::Item],
2447    tab: &str,
2448    structhead: bool,
2449    has_stripped_entries: bool,
2450    cx: &Context<'_>,
2451) -> impl fmt::Display {
2452    fmt::from_fn(move |w| {
2453        match ty {
2454            None => {
2455                let where_displayed = if let Some(generics) = g
2456                    && let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline)
2457                {
2458                    write!(w, "{where_clause}")?;
2459                    true
2460                } else {
2461                    false
2462                };
2463
2464                // If there wasn't a `where` clause, we add a whitespace.
2465                if !where_displayed {
2466                    w.write_str(" {")?;
2467                } else {
2468                    w.write_str("{")?;
2469                }
2470                let count_fields =
2471                    fields.iter().filter(|f| matches!(f.kind, clean::StructFieldItem(..))).count();
2472                let has_visible_fields = count_fields > 0;
2473                let toggle = should_hide_fields(count_fields);
2474                if toggle {
2475                    toggle_open(&mut *w, format_args!("{count_fields} fields"));
2476                }
2477                if has_visible_fields {
2478                    writeln!(w)?;
2479                }
2480                for field in fields {
2481                    if let clean::StructFieldItem(ref ty) = field.kind {
2482                        render_attributes_in_code(w, field, &format!("{tab}    "), cx)?;
2483                        writeln!(
2484                            w,
2485                            "{tab}    {vis}{name}: {ty},",
2486                            vis = visibility_print_with_space(field, cx),
2487                            name = field.name.unwrap(),
2488                            ty = print_type(ty, cx)
2489                        )?;
2490                    }
2491                }
2492
2493                if has_visible_fields {
2494                    if has_stripped_entries {
2495                        writeln!(
2496                            w,
2497                            "{tab}    <span class=\"comment\">/* private fields */</span>"
2498                        )?;
2499                    }
2500                    write!(w, "{tab}")?;
2501                } else if has_stripped_entries {
2502                    write!(w, " <span class=\"comment\">/* private fields */</span> ")?;
2503                }
2504                if toggle {
2505                    toggle_close(&mut *w);
2506                }
2507                w.write_str("}")?;
2508            }
2509            Some(CtorKind::Fn) => {
2510                w.write_str("(")?;
2511                if !fields.is_empty()
2512                    && fields.iter().all(|field| {
2513                        matches!(field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
2514                    })
2515                {
2516                    write!(w, "<span class=\"comment\">/* private fields */</span>")?;
2517                } else {
2518                    for (i, field) in fields.iter().enumerate() {
2519                        if i > 0 {
2520                            w.write_str(", ")?;
2521                        }
2522                        match field.kind {
2523                            clean::StrippedItem(box clean::StructFieldItem(..)) => {
2524                                write!(w, "_")?;
2525                            }
2526                            clean::StructFieldItem(ref ty) => {
2527                                write!(
2528                                    w,
2529                                    "{}{}",
2530                                    visibility_print_with_space(field, cx),
2531                                    print_type(ty, cx),
2532                                )?;
2533                            }
2534                            _ => unreachable!(),
2535                        }
2536                    }
2537                }
2538                w.write_str(")")?;
2539                if let Some(g) = g {
2540                    write!(
2541                        w,
2542                        "{}",
2543                        print_where_clause(g, cx, 0, Ending::NoNewline).maybe_display()
2544                    )?;
2545                }
2546                // We only want a ";" when we are displaying a tuple struct, not a variant tuple struct.
2547                if structhead {
2548                    w.write_str(";")?;
2549                }
2550            }
2551            Some(CtorKind::Const) => {
2552                // Needed for PhantomData.
2553                if let Some(g) = g {
2554                    write!(
2555                        w,
2556                        "{}",
2557                        print_where_clause(g, cx, 0, Ending::NoNewline).maybe_display()
2558                    )?;
2559                }
2560                w.write_str(";")?;
2561            }
2562        }
2563        Ok(())
2564    })
2565}
2566
2567fn document_non_exhaustive_header(item: &clean::Item) -> &str {
2568    if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
2569}
2570
2571fn document_non_exhaustive(item: &clean::Item) -> impl Display {
2572    fmt::from_fn(|f| {
2573        if item.is_non_exhaustive() {
2574            write!(
2575                f,
2576                "<details class=\"toggle non-exhaustive\">\
2577                    <summary class=\"hideme\"><span>{}</span></summary>\
2578                    <div class=\"docblock\">",
2579                {
2580                    if item.is_struct() {
2581                        "This struct is marked as non-exhaustive"
2582                    } else if item.is_enum() {
2583                        "This enum is marked as non-exhaustive"
2584                    } else if item.is_variant() {
2585                        "This variant is marked as non-exhaustive"
2586                    } else {
2587                        "This type is marked as non-exhaustive"
2588                    }
2589                }
2590            )?;
2591
2592            if item.is_struct() {
2593                f.write_str(
2594                    "Non-exhaustive structs could have additional fields added in future. \
2595                    Therefore, non-exhaustive structs cannot be constructed in external crates \
2596                    using the traditional <code>Struct { .. }</code> syntax; cannot be \
2597                    matched against without a wildcard <code>..</code>; and \
2598                    struct update syntax will not work.",
2599                )?;
2600            } else if item.is_enum() {
2601                f.write_str(
2602                    "Non-exhaustive enums could have additional variants added in future. \
2603                    Therefore, when matching against variants of non-exhaustive enums, an \
2604                    extra wildcard arm must be added to account for any future variants.",
2605                )?;
2606            } else if item.is_variant() {
2607                f.write_str(
2608                    "Non-exhaustive enum variants could have additional fields added in future. \
2609                    Therefore, non-exhaustive enum variants cannot be constructed in external \
2610                    crates and cannot be matched against.",
2611                )?;
2612            } else {
2613                f.write_str(
2614                    "This type will require a wildcard arm in any match statements or constructors.",
2615                )?;
2616            }
2617
2618            f.write_str("</div></details>")?;
2619        }
2620        Ok(())
2621    })
2622}
2623
2624fn pluralize(count: usize) -> &'static str {
2625    if count > 1 { "s" } else { "" }
2626}