Skip to main content

rustdoc/html/render/
print_item.rs

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