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