Skip to main content

rustdoc/html/render/
print_item.rs

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