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        write!(
950            w,
951            "{}",
952            write_section_heading(
953                "Dyn Compatibility",
954                "dyn-compatibility",
955                None,
956                format!(
957                    "<div class=\"dyn-compatibility-info\"><p>This trait {} \
958                    <a href=\"{base}/reference/items/traits.html#dyn-compatibility\">dyn compatible</a>.</p>\
959                    <p><i>In older versions of Rust, dyn compatibility was called \"object safety\".</i></p></div>",
960                    if t.is_dyn_compatible(cx.tcx()) { "<b>is</b>" } else { "is <b>not</b>" },
961                    base = crate::clean::utils::DOC_RUST_LANG_ORG_VERSION
962                ),
963            ),
964        )?;
965
966        if let Some(implementors) = cx.shared.cache.implementors.get(&it.item_id.expect_def_id()) {
967            // The DefId is for the first Type found with that name. The bool is
968            // if any Types with the same name but different DefId have been found.
969            let mut implementor_dups: FxHashMap<Symbol, (DefId, bool)> = FxHashMap::default();
970            for implementor in implementors {
971                if let Some(did) =
972                    implementor.inner_impl().for_.without_borrowed_ref().def_id(&cx.shared.cache)
973                    && !did.is_local()
974                {
975                    extern_crates.insert(did.krate);
976                }
977                match implementor.inner_impl().for_.without_borrowed_ref() {
978                    clean::Type::Path { path } if !path.is_assoc_ty() => {
979                        let did = path.def_id();
980                        let &mut (prev_did, ref mut has_duplicates) =
981                            implementor_dups.entry(path.last()).or_insert((did, false));
982                        if prev_did != did {
983                            *has_duplicates = true;
984                        }
985                    }
986                    _ => {}
987                }
988            }
989
990            let (local, mut foreign) =
991                implementors.iter().partition::<Vec<_>, _>(|i| i.is_on_local_type(cx));
992
993            let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) =
994                local.iter().partition(|i| i.inner_impl().kind.is_auto());
995
996            synthetic.sort_by_cached_key(|i| ImplString::new(i, cx));
997            concrete.sort_by_cached_key(|i| ImplString::new(i, cx));
998            foreign.sort_by_cached_key(|i| ImplString::new(i, cx));
999
1000            if !foreign.is_empty() {
1001                write!(
1002                    w,
1003                    "{}",
1004                    write_section_heading(
1005                        "Implementations on Foreign Types",
1006                        "foreign-impls",
1007                        None,
1008                        ""
1009                    )
1010                )?;
1011
1012                for implementor in foreign {
1013                    let provided_methods = implementor.inner_impl().provided_trait_methods(tcx);
1014                    let assoc_link =
1015                        AssocItemLink::GotoSource(implementor.impl_item.item_id, &provided_methods);
1016                    write!(
1017                        w,
1018                        "{}",
1019                        render_impl(
1020                            cx,
1021                            implementor,
1022                            it,
1023                            assoc_link,
1024                            RenderMode::Normal,
1025                            None,
1026                            &[],
1027                            ImplRenderingParameters {
1028                                show_def_docs: false,
1029                                show_default_items: false,
1030                                show_non_assoc_items: true,
1031                                toggle_open_by_default: false,
1032                            },
1033                        )
1034                    )?;
1035                }
1036            }
1037
1038            write!(
1039                w,
1040                "{}",
1041                write_section_heading(
1042                    "Implementors",
1043                    "implementors",
1044                    None,
1045                    "<div id=\"implementors-list\">",
1046                )
1047            )?;
1048            let mut negative_marker = NegativeMarker::new();
1049            for implementor in concrete {
1050                negative_marker.insert_if_needed(w, implementor)?;
1051                write!(w, "{}", render_implementor(cx, implementor, it, &implementor_dups, &[]))?;
1052            }
1053            w.write_str("</div>")?;
1054
1055            if t.is_auto(tcx) {
1056                write!(
1057                    w,
1058                    "{}",
1059                    write_section_heading(
1060                        "Auto implementors",
1061                        "synthetic-implementors",
1062                        None,
1063                        "<div id=\"synthetic-implementors-list\">",
1064                    )
1065                )?;
1066                let mut negative_marker = NegativeMarker::new();
1067                for implementor in synthetic {
1068                    negative_marker.insert_if_needed(w, implementor)?;
1069                    write!(
1070                        w,
1071                        "{}",
1072                        render_implementor(
1073                            cx,
1074                            implementor,
1075                            it,
1076                            &implementor_dups,
1077                            &collect_paths_for_type(
1078                                &implementor.inner_impl().for_,
1079                                &cx.shared.cache,
1080                            ),
1081                        )
1082                    )?;
1083                }
1084                w.write_str("</div>")?;
1085            }
1086        } else {
1087            // even without any implementations to write in, we still want the heading and list, so the
1088            // implementors javascript file pulled in below has somewhere to write the impls into
1089            write!(
1090                w,
1091                "{}",
1092                write_section_heading(
1093                    "Implementors",
1094                    "implementors",
1095                    None,
1096                    "<div id=\"implementors-list\"></div>",
1097                )
1098            )?;
1099
1100            if t.is_auto(tcx) {
1101                write!(
1102                    w,
1103                    "{}",
1104                    write_section_heading(
1105                        "Auto implementors",
1106                        "synthetic-implementors",
1107                        None,
1108                        "<div id=\"synthetic-implementors-list\"></div>",
1109                    )
1110                )?;
1111            }
1112        }
1113
1114        // [RUSTDOCIMPL] trait.impl
1115        //
1116        // Include implementors in crates that depend on the current crate.
1117        //
1118        // This is complicated by the way rustdoc is invoked, which is basically
1119        // the same way rustc is invoked: it gets called, one at a time, for each
1120        // crate. When building the rustdocs for the current crate, rustdoc can
1121        // see crate metadata for its dependencies, but cannot see metadata for its
1122        // dependents.
1123        //
1124        // To make this work, we generate a "hook" at this stage, and our
1125        // dependents can "plug in" to it when they build. For simplicity's sake,
1126        // it's [JSONP]: a JavaScript file with the data we need (and can parse),
1127        // surrounded by a tiny wrapper that the Rust side ignores, but allows the
1128        // JavaScript side to include without having to worry about Same Origin
1129        // Policy. The code for *that* is in `write_shared.rs`.
1130        //
1131        // This is further complicated by `#[doc(inline)]`. We want all copies
1132        // of an inlined trait to reference the same JS file, to address complex
1133        // dependency graphs like this one (lower crates depend on higher crates):
1134        //
1135        // ```text
1136        //  --------------------------------------------
1137        //  |            crate A: trait Foo            |
1138        //  --------------------------------------------
1139        //      |                               |
1140        //  --------------------------------    |
1141        //  | crate B: impl A::Foo for Bar |    |
1142        //  --------------------------------    |
1143        //      |                               |
1144        //  ---------------------------------------------
1145        //  | crate C: #[doc(inline)] use A::Foo as Baz |
1146        //  |          impl Baz for Quux                |
1147        //  ---------------------------------------------
1148        // ```
1149        //
1150        // Basically, we want `C::Baz` and `A::Foo` to show the same set of
1151        // impls, which is easier if they both treat `/trait.impl/A/trait.Foo.js`
1152        // as the Single Source of Truth.
1153        //
1154        // We also want the `impl Baz for Quux` to be written to
1155        // `trait.Foo.js`. However, when we generate plain HTML for `C::Baz`,
1156        // we're going to want to generate plain HTML for `impl Baz for Quux` too,
1157        // because that'll load faster, and it's better for SEO. And we don't want
1158        // the same impl to show up twice on the same page.
1159        //
1160        // To make this work, the trait.impl/A/trait.Foo.js JS file has a structure kinda
1161        // like this:
1162        //
1163        // ```js
1164        // JSONP({
1165        // "B": {"impl A::Foo for Bar"},
1166        // "C": {"impl Baz for Quux"},
1167        // });
1168        // ```
1169        //
1170        // First of all, this means we can rebuild a crate, and it'll replace its own
1171        // data if something changes. That is, `rustdoc` is idempotent. The other
1172        // advantage is that we can list the crates that get included in the HTML,
1173        // and ignore them when doing the JavaScript-based part of rendering.
1174        // So C's HTML will have something like this:
1175        //
1176        // ```html
1177        // <script src="/trait.impl/A/trait.Foo.js"
1178        //     data-ignore-extern-crates="A,B" async></script>
1179        // ```
1180        //
1181        // And, when the JS runs, anything in data-ignore-extern-crates is known
1182        // to already be in the HTML, and will be ignored.
1183        //
1184        // [JSONP]: https://en.wikipedia.org/wiki/JSONP
1185        let mut js_src_path: UrlPartsBuilder =
1186            iter::repeat_n("..", cx.current.len()).chain(iter::once("trait.impl")).collect();
1187        if let Some(did) = it.item_id.as_def_id()
1188            && let get_extern = { || cx.shared.cache.external_paths.get(&did).map(|s| &s.0) }
1189            && let Some(fqp) = cx.shared.cache.exact_paths.get(&did).or_else(get_extern)
1190        {
1191            js_src_path.extend(fqp[..fqp.len() - 1].iter().copied());
1192            js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), fqp.last().unwrap()));
1193        } else {
1194            js_src_path.extend(cx.current.iter().copied());
1195            js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), it.name.unwrap()));
1196        }
1197        let extern_crates = fmt::from_fn(|f| {
1198            if !extern_crates.is_empty() {
1199                f.write_str(" data-ignore-extern-crates=\"")?;
1200                extern_crates.iter().map(|&cnum| tcx.crate_name(cnum)).joined(",", f)?;
1201                f.write_str("\"")?;
1202            }
1203            Ok(())
1204        });
1205        write!(
1206            w,
1207            "<script src=\"{src}\"{extern_crates} async></script>",
1208            src = js_src_path.finish()
1209        )
1210    })
1211}
1212
1213fn item_trait_alias(
1214    cx: &Context<'_>,
1215    it: &clean::Item,
1216    t: &clean::TraitAlias,
1217) -> impl fmt::Display {
1218    fmt::from_fn(|w| {
1219        wrap_item(w, |w| {
1220            render_attributes_in_code(w, it, "", cx)?;
1221            write!(
1222                w,
1223                "trait {name}{generics} = {bounds}{where_clause};",
1224                name = it.name.unwrap(),
1225                generics = print_generics(&t.generics, cx),
1226                bounds = print_bounds(&t.bounds, true, cx),
1227                where_clause =
1228                    print_where_clause(&t.generics, cx, 0, Ending::NoNewline).maybe_display(),
1229            )
1230        })?;
1231
1232        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1233        // Render any items associated directly to this alias, as otherwise they
1234        // won't be visible anywhere in the docs. It would be nice to also show
1235        // associated items from the aliased type (see discussion in #32077), but
1236        // we need #14072 to make sense of the generics.
1237        write!(
1238            w,
1239            "{}",
1240            render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
1241        )
1242    })
1243}
1244
1245fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> impl fmt::Display {
1246    fmt::from_fn(|w| {
1247        wrap_item(w, |w| {
1248            render_attributes_in_code(w, it, "", cx)?;
1249            write!(
1250                w,
1251                "{vis}type {name}{generics}{where_clause} = {type_};",
1252                vis = visibility_print_with_space(it, cx),
1253                name = it.name.unwrap(),
1254                generics = print_generics(&t.generics, cx),
1255                where_clause =
1256                    print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display(),
1257                type_ = print_type(&t.type_, cx),
1258            )
1259        })?;
1260
1261        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1262
1263        if let Some(inner_type) = &t.inner_type {
1264            write!(w, "{}", write_section_heading("Aliased Type", "aliased-type", None, ""),)?;
1265
1266            match inner_type {
1267                clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
1268                    let ty = cx
1269                        .tcx()
1270                        .type_of(it.def_id().unwrap())
1271                        .instantiate_identity()
1272                        .skip_norm_wip();
1273                    let enum_def_id = ty.ty_adt_def().unwrap().did();
1274
1275                    DisplayEnum {
1276                        variants,
1277                        generics: &t.generics,
1278                        is_non_exhaustive: *is_non_exhaustive,
1279                        def_id: enum_def_id,
1280                    }
1281                    .render_into(cx, it, true, w)?;
1282                }
1283                clean::TypeAliasInnerType::Union { fields } => {
1284                    let ty = cx
1285                        .tcx()
1286                        .type_of(it.def_id().unwrap())
1287                        .instantiate_identity()
1288                        .skip_norm_wip();
1289                    let union_def_id = ty.ty_adt_def().unwrap().did();
1290
1291                    ItemUnion {
1292                        cx,
1293                        it,
1294                        fields,
1295                        generics: &t.generics,
1296                        is_type_alias: true,
1297                        def_id: union_def_id,
1298                    }
1299                    .render_into(w)?;
1300                }
1301                clean::TypeAliasInnerType::Struct { ctor_kind, fields } => {
1302                    let ty = cx
1303                        .tcx()
1304                        .type_of(it.def_id().unwrap())
1305                        .instantiate_identity()
1306                        .skip_norm_wip();
1307                    let struct_def_id = ty.ty_adt_def().unwrap().did();
1308
1309                    DisplayStruct {
1310                        ctor_kind: *ctor_kind,
1311                        generics: &t.generics,
1312                        fields,
1313                        def_id: struct_def_id,
1314                    }
1315                    .render_into(cx, it, true, w)?;
1316                }
1317            }
1318        } else {
1319            let def_id = it.item_id.expect_def_id();
1320            // Render any items associated directly to this alias, as otherwise they
1321            // won't be visible anywhere in the docs. It would be nice to also show
1322            // associated items from the aliased type (see discussion in #32077), but
1323            // we need #14072 to make sense of the generics.
1324            write!(
1325                w,
1326                "{}{}",
1327                render_assoc_items(cx, it, def_id, AssocItemRender::All),
1328                document_type_layout(cx, def_id)
1329            )?;
1330        }
1331
1332        // [RUSTDOCIMPL] type.impl
1333        //
1334        // Include type definitions from the alias target type.
1335        //
1336        // Earlier versions of this code worked by having `render_assoc_items`
1337        // include this data directly. That generates *O*`(types*impls)` of HTML
1338        // text, and some real crates have a lot of types and impls.
1339        //
1340        // To create the same UX without generating half a gigabyte of HTML for a
1341        // crate that only contains 20 megabytes of actual documentation[^115718],
1342        // rustdoc stashes these type-alias-inlined docs in a [JSONP]
1343        // "database-lite". The file itself is generated in `write_shared.rs`,
1344        // and hooks into functions provided by `main.js`.
1345        //
1346        // The format of `trait.impl` and `type.impl` JS files are superficially
1347        // similar. Each line, except the JSONP wrapper itself, belongs to a crate,
1348        // and they are otherwise separate (rustdoc should be idempotent). The
1349        // "meat" of the file is HTML strings, so the frontend code is very simple.
1350        // Links are relative to the doc root, though, so the frontend needs to fix
1351        // that up, and inlined docs can reuse these files.
1352        //
1353        // However, there are a few differences, caused by the sophisticated
1354        // features that type aliases have. Consider this crate graph:
1355        //
1356        // ```text
1357        //  ---------------------------------
1358        //  | crate A: struct Foo<T>        |
1359        //  |          type Bar = Foo<i32>  |
1360        //  |          impl X for Foo<i8>   |
1361        //  |          impl Y for Foo<i32>  |
1362        //  ---------------------------------
1363        //      |
1364        //  ----------------------------------
1365        //  | crate B: type Baz = A::Foo<i8> |
1366        //  |          type Xyy = A::Foo<i8> |
1367        //  |          impl Z for Xyy        |
1368        //  ----------------------------------
1369        // ```
1370        //
1371        // The type.impl/A/struct.Foo.js JS file has a structure kinda like this:
1372        //
1373        // ```js
1374        // JSONP({
1375        // "A": [["impl Y for Foo<i32>", "Y", "A::Bar"]],
1376        // "B": [["impl X for Foo<i8>", "X", "B::Baz", "B::Xyy"], ["impl Z for Xyy", "Z", "B::Baz"]],
1377        // });
1378        // ```
1379        //
1380        // When the type.impl file is loaded, only the current crate's docs are
1381        // actually used. The main reason to bundle them together is that there's
1382        // enough duplication in them for DEFLATE to remove the redundancy.
1383        //
1384        // The contents of a crate are a list of impl blocks, themselves
1385        // represented as lists. The first item in the sublist is the HTML block,
1386        // the second item is the name of the trait (which goes in the sidebar),
1387        // and all others are the names of type aliases that successfully match.
1388        //
1389        // This way:
1390        //
1391        // - There's no need to generate these files for types that have no aliases
1392        //   in the current crate. If a dependent crate makes a type alias, it'll
1393        //   take care of generating its own docs.
1394        // - There's no need to reimplement parts of the type checker in
1395        //   JavaScript. The Rust backend does the checking, and includes its
1396        //   results in the file.
1397        // - Docs defined directly on the type alias are dropped directly in the
1398        //   HTML by `render_assoc_items`, and are accessible without JavaScript.
1399        //   The JSONP file will not list impl items that are known to be part
1400        //   of the main HTML file already.
1401        //
1402        // [JSONP]: https://en.wikipedia.org/wiki/JSONP
1403        // [^115718]: https://github.com/rust-lang/rust/issues/115718
1404        let cache = &cx.shared.cache;
1405        if let Some(target_did) = t.type_.def_id(cache)
1406            && let get_extern = { || cache.external_paths.get(&target_did) }
1407            && let Some(&(ref target_fqp, target_type)) =
1408                cache.paths.get(&target_did).or_else(get_extern)
1409            && target_type.is_adt() // primitives cannot be inlined
1410            && let Some(self_did) = it.item_id.as_def_id()
1411            && let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) }
1412            && let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local)
1413        {
1414            let mut js_src_path: UrlPartsBuilder =
1415                iter::repeat_n("..", cx.current.len()).chain(iter::once("type.impl")).collect();
1416            js_src_path.extend(target_fqp[..target_fqp.len() - 1].iter().copied());
1417            js_src_path.push_fmt(format_args!("{target_type}.{}.js", target_fqp.last().unwrap()));
1418            let self_path = join_path_syms(self_fqp);
1419            write!(
1420                w,
1421                "<script src=\"{src}\" data-self-path=\"{self_path}\" async></script>",
1422                src = js_src_path.finish(),
1423            )?;
1424        }
1425        Ok(())
1426    })
1427}
1428
1429#[derive(Template)]
1430#[template(path = "item_union.html")]
1431struct ItemUnion<'a, 'cx> {
1432    cx: &'a Context<'cx>,
1433    it: &'a clean::Item,
1434    fields: &'a [clean::Item],
1435    generics: &'a clean::Generics,
1436    is_type_alias: bool,
1437    def_id: DefId,
1438}
1439
1440impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
1441    fn document(&self) -> impl fmt::Display {
1442        document(self.cx, self.it, None, HeadingOffset::H2)
1443    }
1444
1445    fn document_type_layout(&self) -> impl fmt::Display {
1446        let def_id = self.it.item_id.expect_def_id();
1447        document_type_layout(self.cx, def_id)
1448    }
1449
1450    fn render_assoc_items(&self) -> impl fmt::Display {
1451        let def_id = self.it.item_id.expect_def_id();
1452        render_assoc_items(self.cx, self.it, def_id, AssocItemRender::All)
1453    }
1454
1455    fn render_union(&self) -> impl Display {
1456        render_union(
1457            self.it,
1458            Some(self.generics),
1459            self.fields,
1460            self.def_id,
1461            self.is_type_alias,
1462            self.cx,
1463        )
1464    }
1465
1466    fn print_field_attrs(&self, field: &'a clean::Item) -> impl Display {
1467        fmt::from_fn(move |w| {
1468            render_attributes_in_code(w, field, "", self.cx)?;
1469            Ok(())
1470        })
1471    }
1472
1473    fn document_field(&self, field: &'a clean::Item) -> impl Display {
1474        document(self.cx, field, Some(self.it), HeadingOffset::H3)
1475    }
1476
1477    fn stability_field(&self, field: &clean::Item) -> Option<String> {
1478        field.stability_class(self.cx.tcx())
1479    }
1480
1481    fn print_ty(&self, ty: &'a clean::Type) -> impl Display {
1482        print_type(ty, self.cx)
1483    }
1484
1485    // FIXME (GuillaumeGomez): When <https://github.com/askama-rs/askama/issues/452> is implemented,
1486    // we can replace the returned value with:
1487    //
1488    // `iter::Peekable<impl Iterator<Item = (&'a clean::Item, &'a clean::Type)>>`
1489    //
1490    // And update `item_union.html`.
1491    fn fields_iter(&self) -> impl Iterator<Item = (&'a clean::Item, &'a clean::Type)> {
1492        self.fields.iter().filter_map(|f| match f.kind {
1493            clean::StructFieldItem(ref ty) => Some((f, ty)),
1494            _ => None,
1495        })
1496    }
1497}
1498
1499fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display {
1500    fmt::from_fn(|w| {
1501        ItemUnion {
1502            cx,
1503            it,
1504            fields: &s.fields,
1505            generics: &s.generics,
1506            is_type_alias: false,
1507            def_id: it.def_id().unwrap(),
1508        }
1509        .render_into(w)?;
1510        Ok(())
1511    })
1512}
1513
1514fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Display {
1515    fmt::from_fn(|f| {
1516        if !s.is_empty()
1517            && s.iter()
1518                .all(|field| matches!(field.kind, clean::StrippedItem(clean::StructFieldItem(..))))
1519        {
1520            return f.write_str("<span class=\"comment\">/* private fields */</span>");
1521        }
1522
1523        s.iter()
1524            .map(|ty| {
1525                fmt::from_fn(|f| match ty.kind {
1526                    clean::StrippedItem(clean::StructFieldItem(_)) => f.write_str("_"),
1527                    clean::StructFieldItem(ref ty) => write!(f, "{}", print_type(ty, cx)),
1528                    _ => unreachable!(),
1529                })
1530            })
1531            .joined(", ", f)
1532    })
1533}
1534
1535struct DisplayEnum<'clean> {
1536    variants: &'clean IndexVec<VariantIdx, clean::Item>,
1537    generics: &'clean clean::Generics,
1538    is_non_exhaustive: bool,
1539    def_id: DefId,
1540}
1541
1542impl<'clean> DisplayEnum<'clean> {
1543    fn render_into<W: fmt::Write>(
1544        self,
1545        cx: &Context<'_>,
1546        it: &clean::Item,
1547        is_type_alias: bool,
1548        w: &mut W,
1549    ) -> fmt::Result {
1550        let non_stripped_variant_count = self.variants.iter().filter(|i| !i.is_stripped()).count();
1551        let variants_len = self.variants.len();
1552        let has_stripped_entries = variants_len != non_stripped_variant_count;
1553
1554        wrap_item(w, |w| {
1555            if is_type_alias {
1556                // For now the only attributes we render for type aliases are `repr` attributes.
1557                render_repr_attribute_in_code(w, cx, self.def_id)?;
1558            } else {
1559                render_attributes_in_code(w, it, "", cx)?;
1560            }
1561            write!(
1562                w,
1563                "{}enum {}{}{}",
1564                visibility_print_with_space(it, cx),
1565                it.name.unwrap(),
1566                print_generics(&self.generics, cx),
1567                render_enum_fields(
1568                    cx,
1569                    Some(self.generics),
1570                    self.variants,
1571                    non_stripped_variant_count,
1572                    has_stripped_entries,
1573                    self.is_non_exhaustive,
1574                    self.def_id,
1575                ),
1576            )
1577        })?;
1578
1579        let def_id = it.item_id.expect_def_id();
1580        let layout_def_id = if is_type_alias {
1581            self.def_id
1582        } else {
1583            write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1584            // We don't return the same `DefId` since the layout size of the type alias might be
1585            // different since we might have more information on the generics.
1586            def_id
1587        };
1588
1589        if non_stripped_variant_count != 0 {
1590            write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?;
1591        }
1592        write!(
1593            w,
1594            "{}{}",
1595            render_assoc_items(cx, it, def_id, AssocItemRender::All),
1596            document_type_layout(cx, layout_def_id)
1597        )
1598    }
1599}
1600
1601fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display {
1602    fmt::from_fn(|w| {
1603        DisplayEnum {
1604            variants: &e.variants,
1605            generics: &e.generics,
1606            is_non_exhaustive: it.is_non_exhaustive(),
1607            def_id: it.def_id().unwrap(),
1608        }
1609        .render_into(cx, it, false, w)
1610    })
1611}
1612
1613/// It'll return false if any variant is not a C-like variant. Otherwise it'll return true if at
1614/// least one of them has an explicit discriminant or if the enum has `#[repr(C)]` or an integer
1615/// `repr`.
1616fn should_show_enum_discriminant(
1617    cx: &Context<'_>,
1618    enum_def_id: DefId,
1619    variants: &IndexVec<VariantIdx, clean::Item>,
1620) -> bool {
1621    let mut has_variants_with_value = false;
1622    for variant in variants {
1623        if let clean::VariantItem(ref var) = variant.kind
1624            && matches!(var.kind, clean::VariantKind::CLike)
1625        {
1626            has_variants_with_value |= var.discriminant.is_some();
1627        } else {
1628            return false;
1629        }
1630    }
1631    if has_variants_with_value {
1632        return true;
1633    }
1634    let repr = cx.tcx().adt_def(enum_def_id).repr();
1635    repr.c() || repr.int.is_some()
1636}
1637
1638fn display_c_like_variant(
1639    cx: &Context<'_>,
1640    item: &clean::Item,
1641    variant: &clean::Variant,
1642    index: VariantIdx,
1643    should_show_enum_discriminant: bool,
1644    enum_def_id: DefId,
1645) -> impl fmt::Display {
1646    fmt::from_fn(move |w| {
1647        let name = item.name.unwrap();
1648        if let Some(ref value) = variant.discriminant {
1649            write!(w, "{} = {}", name.as_str(), value.value(cx.tcx(), true))?;
1650        } else if should_show_enum_discriminant {
1651            let adt_def = cx.tcx().adt_def(enum_def_id);
1652            let discr = adt_def.discriminant_for_variant(cx.tcx(), index);
1653            // Use `discr`'s `Display` impl to render the value with the correct
1654            // signedness, including proper sign-extension for signed types.
1655            write!(w, "{} = {}", name.as_str(), discr)?;
1656        } else {
1657            write!(w, "{name}")?;
1658        }
1659        Ok(())
1660    })
1661}
1662
1663fn render_enum_fields(
1664    cx: &Context<'_>,
1665    g: Option<&clean::Generics>,
1666    variants: &IndexVec<VariantIdx, clean::Item>,
1667    count_variants: usize,
1668    has_stripped_entries: bool,
1669    is_non_exhaustive: bool,
1670    enum_def_id: DefId,
1671) -> impl fmt::Display {
1672    fmt::from_fn(move |w| {
1673        let should_show_enum_discriminant =
1674            should_show_enum_discriminant(cx, enum_def_id, variants);
1675        if let Some(generics) = g
1676            && let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline)
1677        {
1678            write!(w, "{where_clause}")?;
1679        } else {
1680            // If there wasn't a `where` clause, we add a whitespace.
1681            w.write_char(' ')?;
1682        }
1683
1684        let variants_stripped = has_stripped_entries;
1685        if count_variants == 0 && !variants_stripped {
1686            w.write_str("{}")
1687        } else {
1688            w.write_str("{\n")?;
1689            let toggle = should_hide_fields(count_variants);
1690            if toggle {
1691                toggle_open(&mut *w, format_args!("{count_variants} variants"));
1692            }
1693            const TAB: &str = "    ";
1694            for (index, v) in variants.iter_enumerated() {
1695                if v.is_stripped() {
1696                    continue;
1697                }
1698                render_attributes_in_code(w, v, TAB, cx)?;
1699                w.write_str(TAB)?;
1700                match v.kind {
1701                    clean::VariantItem(ref var) => match var.kind {
1702                        clean::VariantKind::CLike => {
1703                            write!(
1704                                w,
1705                                "{}",
1706                                display_c_like_variant(
1707                                    cx,
1708                                    v,
1709                                    var,
1710                                    index,
1711                                    should_show_enum_discriminant,
1712                                    enum_def_id,
1713                                )
1714                            )?;
1715                        }
1716                        clean::VariantKind::Tuple(ref s) => {
1717                            write!(w, "{}({})", v.name.unwrap(), print_tuple_struct_fields(cx, s))?;
1718                        }
1719                        clean::VariantKind::Struct(ref s) => {
1720                            write!(
1721                                w,
1722                                "{}",
1723                                render_struct(v, None, None, &s.fields, TAB, false, cx)
1724                            )?;
1725                        }
1726                    },
1727                    _ => unreachable!(),
1728                }
1729                w.write_str(",\n")?;
1730            }
1731
1732            if variants_stripped && !is_non_exhaustive {
1733                w.write_str("    <span class=\"comment\">// some variants omitted</span>\n")?;
1734            }
1735            if toggle {
1736                toggle_close(&mut *w);
1737            }
1738            w.write_str("}")
1739        }
1740    })
1741}
1742
1743fn item_variants(
1744    cx: &Context<'_>,
1745    it: &clean::Item,
1746    variants: &IndexVec<VariantIdx, clean::Item>,
1747    enum_def_id: DefId,
1748) -> impl fmt::Display {
1749    fmt::from_fn(move |w| {
1750        let tcx = cx.tcx();
1751        write!(
1752            w,
1753            "{}",
1754            write_section_heading(
1755                &format!("Variants{}", document_non_exhaustive_header(it)),
1756                "variants",
1757                Some("variants"),
1758                format!("{}<div class=\"variants\">", document_non_exhaustive(it)),
1759            ),
1760        )?;
1761
1762        let should_show_enum_discriminant =
1763            should_show_enum_discriminant(cx, enum_def_id, variants);
1764        for (index, variant) in variants.iter_enumerated() {
1765            if variant.is_stripped() {
1766                continue;
1767            }
1768            let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
1769            write!(
1770                w,
1771                "<section id=\"{id}\" class=\"variant\">\
1772                    <a href=\"#{id}\" class=\"anchor\">§</a>\
1773                    {}\
1774                    <h3 class=\"code-header\">",
1775                render_stability_since_raw_with_extra(
1776                    variant.stable_since(tcx),
1777                    variant.const_stability(tcx),
1778                    " rightside",
1779                )
1780                .maybe_display()
1781            )?;
1782            render_attributes_in_code(w, variant, "", cx)?;
1783            if let clean::VariantItem(ref var) = variant.kind
1784                && let clean::VariantKind::CLike = var.kind
1785            {
1786                write!(
1787                    w,
1788                    "{}",
1789                    display_c_like_variant(
1790                        cx,
1791                        variant,
1792                        var,
1793                        index,
1794                        should_show_enum_discriminant,
1795                        enum_def_id,
1796                    )
1797                )?;
1798            } else {
1799                w.write_str(variant.name.unwrap().as_str())?;
1800            }
1801
1802            let clean::VariantItem(variant_data) = &variant.kind else { unreachable!() };
1803
1804            if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
1805                write!(w, "({})", print_tuple_struct_fields(cx, s))?;
1806            }
1807            w.write_str("</h3></section>")?;
1808
1809            write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4))?;
1810
1811            let heading_and_fields = match &variant_data.kind {
1812                clean::VariantKind::Struct(s) => {
1813                    // If there is no field to display, no need to add the heading.
1814                    if s.fields.iter().any(|f| !f.is_doc_hidden()) {
1815                        Some(("Fields", &s.fields))
1816                    } else {
1817                        None
1818                    }
1819                }
1820                clean::VariantKind::Tuple(fields) => {
1821                    // Documentation on tuple variant fields is rare, so to reduce noise we only emit
1822                    // the section if at least one field is documented.
1823                    if fields.iter().any(|f| !f.doc_value().is_empty()) {
1824                        Some(("Tuple Fields", fields))
1825                    } else {
1826                        None
1827                    }
1828                }
1829                clean::VariantKind::CLike => None,
1830            };
1831
1832            if let Some((heading, fields)) = heading_and_fields {
1833                let variant_id =
1834                    cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
1835                write!(
1836                    w,
1837                    "<div class=\"sub-variant\" id=\"{variant_id}\">\
1838                        <h4>{heading}</h4>\
1839                        {}",
1840                    document_non_exhaustive(variant)
1841                )?;
1842                for field in fields {
1843                    match field.kind {
1844                        clean::StrippedItem(clean::StructFieldItem(_)) => {}
1845                        clean::StructFieldItem(ref ty) => {
1846                            let id = cx.derive_id(format!(
1847                                "variant.{}.field.{}",
1848                                variant.name.unwrap(),
1849                                field.name.unwrap()
1850                            ));
1851                            write!(
1852                                w,
1853                                "<div class=\"sub-variant-field\">\
1854                                    <span id=\"{id}\" class=\"section-header\">\
1855                                        <a href=\"#{id}\" class=\"anchor field\">§</a>\
1856                                        <code>"
1857                            )?;
1858                            render_attributes_in_code(w, field, "", cx)?;
1859                            write!(
1860                                w,
1861                                "{f}: {t}</code>\
1862                                    </span>\
1863                                    {doc}\
1864                                </div>",
1865                                f = field.name.unwrap(),
1866                                t = print_type(ty, cx),
1867                                doc = document(cx, field, Some(variant), HeadingOffset::H5),
1868                            )?;
1869                        }
1870                        _ => unreachable!(),
1871                    }
1872                }
1873                w.write_str("</div>")?;
1874            }
1875        }
1876        w.write_str("</div>")
1877    })
1878}
1879
1880fn item_macro(
1881    cx: &Context<'_>,
1882    it: &clean::Item,
1883    t: &clean::Macro,
1884    kinds: MacroKinds,
1885) -> impl fmt::Display {
1886    fmt::from_fn(move |w| {
1887        wrap_item(w, |w| {
1888            render_attributes_in_code(w, it, "", cx)?;
1889            if !t.macro_rules {
1890                write!(w, "{}", visibility_print_with_space(it, cx))?;
1891            }
1892            write!(w, "{}", Escape(&t.source))
1893        })?;
1894        if kinds != MacroKinds::BANG {
1895            write!(
1896                w,
1897                "<h3 class='macro-info'>ⓘ This is {} {}</h3>",
1898                kinds.article(),
1899                kinds.descr(),
1900            )?;
1901        }
1902        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
1903    })
1904}
1905
1906fn item_proc_macro(cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) -> impl fmt::Display {
1907    fmt::from_fn(|w| {
1908        wrap_item(w, |w| {
1909            let name = it.name.expect("proc-macros always have names");
1910            match m.kind {
1911                MacroKind::Bang => {
1912                    write!(w, "{name}!() {{ <span class=\"comment\">/* proc-macro */</span> }}")?;
1913                }
1914                MacroKind::Attr => {
1915                    write!(w, "#[{name}]")?;
1916                }
1917                MacroKind::Derive => {
1918                    write!(w, "#[derive({name})]")?;
1919                    if !m.helpers.is_empty() {
1920                        w.write_str(
1921                            "\n{\n    \
1922                            <span class=\"comment\">// Attributes available to this derive:</span>\n",
1923                        )?;
1924                        for attr in &m.helpers {
1925                            writeln!(w, "    #[{attr}]")?;
1926                        }
1927                        w.write_str("}\n")?;
1928                    }
1929                }
1930            }
1931            fmt::Result::Ok(())
1932        })?;
1933        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
1934    })
1935}
1936
1937fn item_primitive(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
1938    fmt::from_fn(|w| {
1939        let def_id = it.item_id.expect_def_id();
1940        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1941        if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
1942            write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All))
1943        } else {
1944            // We handle the "reference" primitive type on its own because we only want to list
1945            // implementations on generic types.
1946            let (concrete, synthetic, blanket_impl) =
1947                get_filtered_impls_for_reference(&cx.shared, it);
1948
1949            render_all_impls(w, cx, it, &concrete, &synthetic, &blanket_impl)
1950        }
1951    })
1952}
1953
1954fn item_constant(
1955    cx: &Context<'_>,
1956    it: &clean::Item,
1957    generics: &clean::Generics,
1958    ty: &clean::Type,
1959    c: &clean::ConstantKind,
1960) -> impl fmt::Display {
1961    fmt::from_fn(|w| {
1962        wrap_item(w, |w| {
1963            let tcx = cx.tcx();
1964            render_attributes_in_code(w, it, "", cx)?;
1965
1966            write!(
1967                w,
1968                "{vis}const {name}{generics}: {typ}{where_clause}",
1969                vis = visibility_print_with_space(it, cx),
1970                name = it.name.unwrap(),
1971                generics = print_generics(generics, cx),
1972                typ = print_type(ty, cx),
1973                where_clause =
1974                    print_where_clause(generics, cx, 0, Ending::NoNewline).maybe_display(),
1975            )?;
1976
1977            // FIXME: The code below now prints
1978            //            ` = _; // 100i32`
1979            //        if the expression is
1980            //            `50 + 50`
1981            //        which looks just wrong.
1982            //        Should we print
1983            //            ` = 100i32;`
1984            //        instead?
1985
1986            let value = c.value(tcx);
1987            let is_literal = c.is_literal(tcx);
1988            let expr = c.expr(tcx);
1989            if value.is_some() || is_literal {
1990                write!(w, " = {expr};", expr = Escape(&expr))?;
1991            } else {
1992                w.write_str(";")?;
1993            }
1994
1995            if !is_literal && let Some(value) = &value {
1996                let value_lowercase = value.to_lowercase();
1997                let expr_lowercase = expr.to_lowercase();
1998
1999                if value_lowercase != expr_lowercase
2000                    && value_lowercase.trim_end_matches("i32") != expr_lowercase
2001                {
2002                    write!(w, " // {value}", value = Escape(value))?;
2003                }
2004            }
2005            Ok::<(), fmt::Error>(())
2006        })?;
2007
2008        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
2009    })
2010}
2011
2012struct DisplayStruct<'a> {
2013    ctor_kind: Option<CtorKind>,
2014    generics: &'a clean::Generics,
2015    fields: &'a [clean::Item],
2016    def_id: DefId,
2017}
2018
2019impl<'a> DisplayStruct<'a> {
2020    fn render_into<W: fmt::Write>(
2021        self,
2022        cx: &Context<'_>,
2023        it: &clean::Item,
2024        is_type_alias: bool,
2025        w: &mut W,
2026    ) -> fmt::Result {
2027        wrap_item(w, |w| {
2028            if is_type_alias {
2029                // For now the only attributes we render for type aliases are `repr` attributes.
2030                render_repr_attribute_in_code(w, cx, self.def_id)?;
2031            } else {
2032                render_attributes_in_code(w, it, "", cx)?;
2033            }
2034            write!(
2035                w,
2036                "{}",
2037                render_struct(it, Some(self.generics), self.ctor_kind, self.fields, "", true, cx)
2038            )
2039        })?;
2040
2041        if !is_type_alias {
2042            write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
2043        }
2044
2045        let def_id = it.item_id.expect_def_id();
2046        write!(
2047            w,
2048            "{}{}{}",
2049            item_fields(cx, it, self.fields, self.ctor_kind),
2050            render_assoc_items(cx, it, def_id, AssocItemRender::All),
2051            document_type_layout(cx, def_id),
2052        )
2053    }
2054}
2055
2056fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display {
2057    fmt::from_fn(|w| {
2058        DisplayStruct {
2059            ctor_kind: s.ctor_kind,
2060            generics: &s.generics,
2061            fields: s.fields.as_slice(),
2062            def_id: it.def_id().unwrap(),
2063        }
2064        .render_into(cx, it, false, w)
2065    })
2066}
2067
2068fn item_fields(
2069    cx: &Context<'_>,
2070    it: &clean::Item,
2071    fields: &[clean::Item],
2072    ctor_kind: Option<CtorKind>,
2073) -> impl fmt::Display {
2074    fmt::from_fn(move |w| {
2075        let mut fields = fields
2076            .iter()
2077            .filter_map(|f| match f.kind {
2078                clean::StructFieldItem(ref ty) => Some((f, ty)),
2079                _ => None,
2080            })
2081            .peekable();
2082        if let None | Some(CtorKind::Fn) = ctor_kind
2083            && fields.peek().is_some()
2084        {
2085            let title = format!(
2086                "{}{}",
2087                if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
2088                document_non_exhaustive_header(it),
2089            );
2090            write!(
2091                w,
2092                "{}",
2093                write_section_heading(
2094                    &title,
2095                    "fields",
2096                    Some("fields"),
2097                    document_non_exhaustive(it)
2098                )
2099            )?;
2100            for (index, (field, ty)) in fields.enumerate() {
2101                let field_name =
2102                    field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string());
2103                let id = cx.derive_id(format!("{typ}.{field_name}", typ = ItemType::StructField));
2104                write!(
2105                    w,
2106                    "<span id=\"{id}\" class=\"{item_type} section-header\">\
2107                        <a href=\"#{id}\" class=\"anchor field\">§</a>\
2108                        <code>",
2109                    item_type = ItemType::StructField,
2110                )?;
2111                render_attributes_in_code(w, field, "", cx)?;
2112                write!(
2113                    w,
2114                    "{field_name}: {ty}</code>\
2115                    </span>\
2116                    {doc}",
2117                    ty = print_type(ty, cx),
2118                    doc = document(cx, field, Some(it), HeadingOffset::H3),
2119                )?;
2120            }
2121        }
2122        Ok(())
2123    })
2124}
2125
2126fn item_static(
2127    cx: &Context<'_>,
2128    it: &clean::Item,
2129    s: &clean::Static,
2130    safety: Option<hir::Safety>,
2131) -> impl fmt::Display {
2132    fmt::from_fn(move |w| {
2133        wrap_item(w, |w| {
2134            render_attributes_in_code(w, it, "", cx)?;
2135            write!(
2136                w,
2137                "{vis}{safe}static {mutability}{name}: {typ}",
2138                vis = visibility_print_with_space(it, cx),
2139                safe = safety.map(|safe| safe.prefix_str()).unwrap_or(""),
2140                mutability = s.mutability.print_with_space(),
2141                name = it.name.unwrap(),
2142                typ = print_type(&s.type_, cx)
2143            )
2144        })?;
2145
2146        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
2147    })
2148}
2149
2150fn item_foreign_type(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
2151    fmt::from_fn(|w| {
2152        wrap_item(w, |w| {
2153            w.write_str("extern {\n")?;
2154            render_attributes_in_code(w, it, "", cx)?;
2155            write!(w, "    {}type {};\n}}", visibility_print_with_space(it, cx), it.name.unwrap())
2156        })?;
2157
2158        write!(
2159            w,
2160            "{}{}",
2161            document(cx, it, None, HeadingOffset::H2),
2162            render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
2163        )
2164    })
2165}
2166
2167fn item_keyword_or_attribute(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
2168    document(cx, it, None, HeadingOffset::H2)
2169}
2170
2171/// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order).
2172///
2173/// This code is copied from [`rustfmt`], and should probably be released as a crate at some point.
2174///
2175/// [`rustfmt`]:https://github.com/rust-lang/rustfmt/blob/rustfmt-2.0.0-rc.2/src/formatting/reorder.rs#L32
2176pub(crate) fn compare_names(left: &str, right: &str) -> Ordering {
2177    let mut left = left.chars().peekable();
2178    let mut right = right.chars().peekable();
2179
2180    loop {
2181        // The strings are equal so far and not inside a number in both sides
2182        let (l, r) = match (left.next(), right.next()) {
2183            // Is this the end of both strings?
2184            (None, None) => return Ordering::Equal,
2185            // If for one, the shorter one is considered smaller
2186            (None, Some(_)) => return Ordering::Less,
2187            (Some(_), None) => return Ordering::Greater,
2188            (Some(l), Some(r)) => (l, r),
2189        };
2190        let next_ordering = match (l.to_digit(10), r.to_digit(10)) {
2191            // If neither is a digit, just compare them
2192            (None, None) => Ord::cmp(&l, &r),
2193            // The one with shorter non-digit run is smaller
2194            // For `strverscmp` it's smaller iff next char in longer is greater than digits
2195            (None, Some(_)) => Ordering::Greater,
2196            (Some(_), None) => Ordering::Less,
2197            // If both start numbers, we have to compare the numbers
2198            (Some(l), Some(r)) => {
2199                if l == 0 || r == 0 {
2200                    // Fraction mode: compare as if there was leading `0.`
2201                    let ordering = Ord::cmp(&l, &r);
2202                    if ordering != Ordering::Equal {
2203                        return ordering;
2204                    }
2205                    loop {
2206                        // Get next pair
2207                        let (l, r) = match (left.peek(), right.peek()) {
2208                            // Is this the end of both strings?
2209                            (None, None) => return Ordering::Equal,
2210                            // If for one, the shorter one is considered smaller
2211                            (None, Some(_)) => return Ordering::Less,
2212                            (Some(_), None) => return Ordering::Greater,
2213                            (Some(l), Some(r)) => (l, r),
2214                        };
2215                        // Are they digits?
2216                        match (l.to_digit(10), r.to_digit(10)) {
2217                            // If out of digits, use the stored ordering due to equal length
2218                            (None, None) => break Ordering::Equal,
2219                            // If one is shorter, it's smaller
2220                            (None, Some(_)) => return Ordering::Less,
2221                            (Some(_), None) => return Ordering::Greater,
2222                            // If both are digits, consume them and take into account
2223                            (Some(l), Some(r)) => {
2224                                left.next();
2225                                right.next();
2226                                let ordering = Ord::cmp(&l, &r);
2227                                if ordering != Ordering::Equal {
2228                                    return ordering;
2229                                }
2230                            }
2231                        }
2232                    }
2233                } else {
2234                    // Integer mode
2235                    let mut same_length_ordering = Ord::cmp(&l, &r);
2236                    loop {
2237                        // Get next pair
2238                        let (l, r) = match (left.peek(), right.peek()) {
2239                            // Is this the end of both strings?
2240                            (None, None) => return same_length_ordering,
2241                            // If for one, the shorter one is considered smaller
2242                            (None, Some(_)) => return Ordering::Less,
2243                            (Some(_), None) => return Ordering::Greater,
2244                            (Some(l), Some(r)) => (l, r),
2245                        };
2246                        // Are they digits?
2247                        match (l.to_digit(10), r.to_digit(10)) {
2248                            // If out of digits, use the stored ordering due to equal length
2249                            (None, None) => break same_length_ordering,
2250                            // If one is shorter, it's smaller
2251                            (None, Some(_)) => return Ordering::Less,
2252                            (Some(_), None) => return Ordering::Greater,
2253                            // If both are digits, consume them and take into account
2254                            (Some(l), Some(r)) => {
2255                                left.next();
2256                                right.next();
2257                                same_length_ordering = same_length_ordering.then(Ord::cmp(&l, &r));
2258                            }
2259                        }
2260                    }
2261                }
2262            }
2263        };
2264        if next_ordering != Ordering::Equal {
2265            return next_ordering;
2266        }
2267    }
2268}
2269
2270pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
2271    let mut s = join_path_syms(&cx.current);
2272    s.push_str("::");
2273    s.push_str(item.name.unwrap().as_str());
2274    s
2275}
2276
2277pub(super) fn print_item_path(item: &clean::Item) -> impl Display {
2278    fmt::from_fn(move |f| match item.kind {
2279        clean::ItemKind::ModuleItem(..) => {
2280            write!(f, "{}index.html", ensure_trailing_slash(item.name.unwrap().as_str()))
2281        }
2282        _ => f.write_str(&item.html_filename()),
2283    })
2284}
2285
2286pub(super) fn print_ty_path(ty: ItemType, name: &str) -> impl Display {
2287    fmt::from_fn(move |f| match ty {
2288        ItemType::Module => write!(f, "{}index.html", ensure_trailing_slash(name)),
2289        _ => write!(f, "{ty}.{name}.html"),
2290    })
2291}
2292
2293fn print_bounds(
2294    bounds: &[clean::GenericBound],
2295    trait_alias: bool,
2296    cx: &Context<'_>,
2297) -> impl Display {
2298    (!bounds.is_empty())
2299        .then_some(fmt::from_fn(move |f| {
2300            let has_lots_of_bounds = bounds.len() > 2;
2301            let inter_str = if has_lots_of_bounds { "\n    + " } else { " + " };
2302            if !trait_alias {
2303                if has_lots_of_bounds {
2304                    f.write_str(":\n    ")?;
2305                } else {
2306                    f.write_str(": ")?;
2307                }
2308            }
2309
2310            bounds.iter().map(|p| print_generic_bound(p, cx)).joined(inter_str, f)
2311        }))
2312        .maybe_display()
2313}
2314
2315fn wrap_item<W, F>(w: &mut W, f: F) -> fmt::Result
2316where
2317    W: fmt::Write,
2318    F: FnOnce(&mut W) -> fmt::Result,
2319{
2320    w.write_str(r#"<pre class="rust item-decl"><code>"#)?;
2321    f(w)?;
2322    w.write_str("</code></pre>")
2323}
2324
2325#[derive(PartialEq, Eq)]
2326struct ImplString {
2327    rendered: String,
2328    is_negative: bool,
2329}
2330
2331impl ImplString {
2332    fn new(i: &Impl, cx: &Context<'_>) -> ImplString {
2333        let impl_ = i.inner_impl();
2334        ImplString {
2335            is_negative: impl_.is_negative_trait_impl(),
2336            rendered: format!("{}", print_impl(impl_, false, cx)),
2337        }
2338    }
2339}
2340
2341impl PartialOrd for ImplString {
2342    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2343        Some(Ord::cmp(self, other))
2344    }
2345}
2346
2347impl Ord for ImplString {
2348    fn cmp(&self, other: &Self) -> Ordering {
2349        // We sort negative impls first.
2350        match (self.is_negative, other.is_negative) {
2351            (false, true) => Ordering::Greater,
2352            (true, false) => Ordering::Less,
2353            _ => compare_names(&self.rendered, &other.rendered),
2354        }
2355    }
2356}
2357
2358fn render_implementor(
2359    cx: &Context<'_>,
2360    implementor: &Impl,
2361    trait_: &clean::Item,
2362    implementor_dups: &FxHashMap<Symbol, (DefId, bool)>,
2363    aliases: &[String],
2364) -> impl fmt::Display {
2365    // If there's already another implementor that has the same abridged name, use the
2366    // full path, for example in `std::iter::ExactSizeIterator`
2367    let use_absolute = match implementor.inner_impl().for_ {
2368        clean::Type::Path { ref path, .. }
2369        | clean::BorrowedRef { type_: clean::Type::Path { ref path, .. }, .. }
2370            if !path.is_assoc_ty() =>
2371        {
2372            implementor_dups[&path.last()].1
2373        }
2374        _ => false,
2375    };
2376    render_impl(
2377        cx,
2378        implementor,
2379        trait_,
2380        AssocItemLink::Anchor(None),
2381        RenderMode::Normal,
2382        Some(use_absolute),
2383        aliases,
2384        ImplRenderingParameters {
2385            show_def_docs: false,
2386            show_default_items: false,
2387            show_non_assoc_items: false,
2388            toggle_open_by_default: false,
2389        },
2390    )
2391}
2392
2393fn render_union(
2394    it: &clean::Item,
2395    g: Option<&clean::Generics>,
2396    fields: &[clean::Item],
2397    def_id: DefId,
2398    is_type_alias: bool,
2399    cx: &Context<'_>,
2400) -> impl Display {
2401    fmt::from_fn(move |mut f| {
2402        if is_type_alias {
2403            // For now the only attributes we render for type aliases are `repr` attributes.
2404            render_repr_attribute_in_code(f, cx, def_id)?;
2405        } else {
2406            render_attributes_in_code(f, it, "", cx)?;
2407        }
2408        write!(f, "{}union {}", visibility_print_with_space(it, cx), it.name.unwrap(),)?;
2409
2410        let where_displayed = if let Some(generics) = g {
2411            write!(f, "{}", print_generics(generics, cx))?;
2412            if let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline) {
2413                write!(f, "{where_clause}")?;
2414                true
2415            } else {
2416                false
2417            }
2418        } else {
2419            false
2420        };
2421
2422        // If there wasn't a `where` clause, we add a whitespace.
2423        if !where_displayed {
2424            f.write_str(" ")?;
2425        }
2426
2427        writeln!(f, "{{")?;
2428        let count_fields =
2429            fields.iter().filter(|field| matches!(field.kind, clean::StructFieldItem(..))).count();
2430        let toggle = should_hide_fields(count_fields);
2431        if toggle {
2432            toggle_open(&mut f, format_args!("{count_fields} fields"));
2433        }
2434
2435        for field in fields {
2436            if let clean::StructFieldItem(ref ty) = field.kind {
2437                render_attributes_in_code(&mut f, field, "    ", cx)?;
2438                writeln!(
2439                    f,
2440                    "    {}{}: {},",
2441                    visibility_print_with_space(field, cx),
2442                    field.name.unwrap(),
2443                    print_type(ty, cx)
2444                )?;
2445            }
2446        }
2447
2448        if it.has_stripped_entries().unwrap() {
2449            writeln!(f, "    <span class=\"comment\">/* private fields */</span>")?;
2450        }
2451        if toggle {
2452            toggle_close(&mut f);
2453        }
2454        f.write_str("}").unwrap();
2455        Ok(())
2456    })
2457}
2458
2459fn render_struct(
2460    it: &clean::Item,
2461    g: Option<&clean::Generics>,
2462    ty: Option<CtorKind>,
2463    fields: &[clean::Item],
2464    tab: &str,
2465    structhead: bool,
2466    cx: &Context<'_>,
2467) -> impl fmt::Display {
2468    fmt::from_fn(move |w| {
2469        write!(
2470            w,
2471            "{}{}{}",
2472            visibility_print_with_space(it, cx),
2473            if structhead { "struct " } else { "" },
2474            it.name.unwrap()
2475        )?;
2476        if let Some(g) = g {
2477            write!(w, "{}", print_generics(g, cx))?;
2478        }
2479        write!(
2480            w,
2481            "{}",
2482            render_struct_fields(
2483                g,
2484                ty,
2485                fields,
2486                tab,
2487                structhead,
2488                it.has_stripped_entries().unwrap_or(false),
2489                cx,
2490            )
2491        )
2492    })
2493}
2494
2495fn render_struct_fields(
2496    g: Option<&clean::Generics>,
2497    ty: Option<CtorKind>,
2498    fields: &[clean::Item],
2499    tab: &str,
2500    structhead: bool,
2501    has_stripped_entries: bool,
2502    cx: &Context<'_>,
2503) -> impl fmt::Display {
2504    fmt::from_fn(move |w| {
2505        match ty {
2506            None => {
2507                let where_displayed = if let Some(generics) = g
2508                    && let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline)
2509                {
2510                    write!(w, "{where_clause}")?;
2511                    true
2512                } else {
2513                    false
2514                };
2515
2516                // If there wasn't a `where` clause, we add a whitespace.
2517                if !where_displayed {
2518                    w.write_str(" {")?;
2519                } else {
2520                    w.write_str("{")?;
2521                }
2522                let count_fields =
2523                    fields.iter().filter(|f| matches!(f.kind, clean::StructFieldItem(..))).count();
2524                let has_visible_fields = count_fields > 0;
2525                let toggle = should_hide_fields(count_fields);
2526                if toggle {
2527                    toggle_open(&mut *w, format_args!("{count_fields} fields"));
2528                }
2529                if has_visible_fields {
2530                    writeln!(w)?;
2531                }
2532                for field in fields {
2533                    if let clean::StructFieldItem(ref ty) = field.kind {
2534                        render_attributes_in_code(w, field, &format!("{tab}    "), cx)?;
2535                        writeln!(
2536                            w,
2537                            "{tab}    {vis}{name}: {ty},",
2538                            vis = visibility_print_with_space(field, cx),
2539                            name = field.name.unwrap(),
2540                            ty = print_type(ty, cx)
2541                        )?;
2542                    }
2543                }
2544
2545                if has_visible_fields {
2546                    if has_stripped_entries {
2547                        writeln!(
2548                            w,
2549                            "{tab}    <span class=\"comment\">/* private fields */</span>"
2550                        )?;
2551                    }
2552                    write!(w, "{tab}")?;
2553                } else if has_stripped_entries {
2554                    write!(w, " <span class=\"comment\">/* private fields */</span> ")?;
2555                }
2556                if toggle {
2557                    toggle_close(&mut *w);
2558                }
2559                w.write_str("}")?;
2560            }
2561            Some(CtorKind::Fn) => {
2562                w.write_str("(")?;
2563                if !fields.is_empty()
2564                    && fields.iter().all(|field| {
2565                        matches!(field.kind, clean::StrippedItem(clean::StructFieldItem(..)))
2566                    })
2567                {
2568                    write!(w, "<span class=\"comment\">/* private fields */</span>")?;
2569                } else {
2570                    for (i, field) in fields.iter().enumerate() {
2571                        if i > 0 {
2572                            w.write_str(", ")?;
2573                        }
2574                        match field.kind {
2575                            clean::StrippedItem(clean::StructFieldItem(..)) => {
2576                                write!(w, "_")?;
2577                            }
2578                            clean::StructFieldItem(ref ty) => {
2579                                write!(
2580                                    w,
2581                                    "{}{}",
2582                                    visibility_print_with_space(field, cx),
2583                                    print_type(ty, cx),
2584                                )?;
2585                            }
2586                            _ => unreachable!(),
2587                        }
2588                    }
2589                }
2590                w.write_str(")")?;
2591                if let Some(g) = g {
2592                    write!(
2593                        w,
2594                        "{}",
2595                        print_where_clause(g, cx, 0, Ending::NoNewline).maybe_display()
2596                    )?;
2597                }
2598                // We only want a ";" when we are displaying a tuple struct, not a variant tuple struct.
2599                if structhead {
2600                    w.write_str(";")?;
2601                }
2602            }
2603            Some(CtorKind::Const) => {
2604                // Needed for PhantomData.
2605                if let Some(g) = g {
2606                    write!(
2607                        w,
2608                        "{}",
2609                        print_where_clause(g, cx, 0, Ending::NoNewline).maybe_display()
2610                    )?;
2611                }
2612                w.write_str(";")?;
2613            }
2614        }
2615        Ok(())
2616    })
2617}
2618
2619fn document_non_exhaustive_header(item: &clean::Item) -> &str {
2620    if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
2621}
2622
2623fn document_non_exhaustive(item: &clean::Item) -> impl Display {
2624    fmt::from_fn(|f| {
2625        if item.is_non_exhaustive() {
2626            write!(
2627                f,
2628                "<details class=\"toggle non-exhaustive\">\
2629                    <summary class=\"hideme\"><span>{}</span></summary>\
2630                    <div class=\"docblock\">",
2631                {
2632                    if item.is_struct() {
2633                        "This struct is marked as non-exhaustive"
2634                    } else if item.is_enum() {
2635                        "This enum is marked as non-exhaustive"
2636                    } else if item.is_variant() {
2637                        "This variant is marked as non-exhaustive"
2638                    } else {
2639                        "This type is marked as non-exhaustive"
2640                    }
2641                }
2642            )?;
2643
2644            if item.is_struct() {
2645                f.write_str(
2646                    "Non-exhaustive structs could have additional fields added in future. \
2647                    Therefore, non-exhaustive structs cannot be constructed in external crates \
2648                    using the traditional <code>Struct { .. }</code> syntax; cannot be \
2649                    matched against without a wildcard <code>..</code>; and \
2650                    struct update syntax will not work.",
2651                )?;
2652            } else if item.is_enum() {
2653                f.write_str(
2654                    "Non-exhaustive enums could have additional variants added in future. \
2655                    Therefore, when matching against variants of non-exhaustive enums, an \
2656                    extra wildcard arm must be added to account for any future variants.",
2657                )?;
2658            } else if item.is_variant() {
2659                f.write_str(
2660                    "Non-exhaustive enum variants could have additional fields added in future. \
2661                    Therefore, non-exhaustive enum variants cannot be constructed in external \
2662                    crates and cannot be matched against.",
2663                )?;
2664            } else {
2665                f.write_str(
2666                    "This type will require a wildcard arm in any match statements or constructors.",
2667                )?;
2668            }
2669
2670            f.write_str("</div></details>")?;
2671        }
2672        Ok(())
2673    })
2674}
2675
2676fn pluralize(count: usize) -> &'static str {
2677    if count > 1 { "s" } else { "" }
2678}