Skip to main content

rustdoc/html/render/
print_item.rs

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