Skip to main content

rustdoc/html/render/
print_item.rs

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