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