Skip to main content

rustdoc/html/render/
print_item.rs

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