rustdoc/html/render/
print_item.rs

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