rustdoc/html/render/
print_item.rs

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