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