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
1279                        .tcx()
1280                        .type_of(it.def_id().unwrap())
1281                        .instantiate_identity()
1282                        .skip_norm_wip();
1283                    let enum_def_id = ty.ty_adt_def().unwrap().did();
1284
1285                    DisplayEnum {
1286                        variants,
1287                        generics: &t.generics,
1288                        is_non_exhaustive: *is_non_exhaustive,
1289                        def_id: enum_def_id,
1290                    }
1291                    .render_into(cx, it, true, w)?;
1292                }
1293                clean::TypeAliasInnerType::Union { fields } => {
1294                    let ty = cx
1295                        .tcx()
1296                        .type_of(it.def_id().unwrap())
1297                        .instantiate_identity()
1298                        .skip_norm_wip();
1299                    let union_def_id = ty.ty_adt_def().unwrap().did();
1300
1301                    ItemUnion {
1302                        cx,
1303                        it,
1304                        fields,
1305                        generics: &t.generics,
1306                        is_type_alias: true,
1307                        def_id: union_def_id,
1308                    }
1309                    .render_into(w)?;
1310                }
1311                clean::TypeAliasInnerType::Struct { ctor_kind, fields } => {
1312                    let ty = cx
1313                        .tcx()
1314                        .type_of(it.def_id().unwrap())
1315                        .instantiate_identity()
1316                        .skip_norm_wip();
1317                    let struct_def_id = ty.ty_adt_def().unwrap().did();
1318
1319                    DisplayStruct {
1320                        ctor_kind: *ctor_kind,
1321                        generics: &t.generics,
1322                        fields,
1323                        def_id: struct_def_id,
1324                    }
1325                    .render_into(cx, it, true, w)?;
1326                }
1327            }
1328        } else {
1329            let def_id = it.item_id.expect_def_id();
1330            // Render any items associated directly to this alias, as otherwise they
1331            // won't be visible anywhere in the docs. It would be nice to also show
1332            // associated items from the aliased type (see discussion in #32077), but
1333            // we need #14072 to make sense of the generics.
1334            write!(
1335                w,
1336                "{}{}",
1337                render_assoc_items(cx, it, def_id, AssocItemRender::All),
1338                document_type_layout(cx, def_id)
1339            )?;
1340        }
1341
1342        // [RUSTDOCIMPL] type.impl
1343        //
1344        // Include type definitions from the alias target type.
1345        //
1346        // Earlier versions of this code worked by having `render_assoc_items`
1347        // include this data directly. That generates *O*`(types*impls)` of HTML
1348        // text, and some real crates have a lot of types and impls.
1349        //
1350        // To create the same UX without generating half a gigabyte of HTML for a
1351        // crate that only contains 20 megabytes of actual documentation[^115718],
1352        // rustdoc stashes these type-alias-inlined docs in a [JSONP]
1353        // "database-lite". The file itself is generated in `write_shared.rs`,
1354        // and hooks into functions provided by `main.js`.
1355        //
1356        // The format of `trait.impl` and `type.impl` JS files are superficially
1357        // similar. Each line, except the JSONP wrapper itself, belongs to a crate,
1358        // and they are otherwise separate (rustdoc should be idempotent). The
1359        // "meat" of the file is HTML strings, so the frontend code is very simple.
1360        // Links are relative to the doc root, though, so the frontend needs to fix
1361        // that up, and inlined docs can reuse these files.
1362        //
1363        // However, there are a few differences, caused by the sophisticated
1364        // features that type aliases have. Consider this crate graph:
1365        //
1366        // ```text
1367        //  ---------------------------------
1368        //  | crate A: struct Foo<T>        |
1369        //  |          type Bar = Foo<i32>  |
1370        //  |          impl X for Foo<i8>   |
1371        //  |          impl Y for Foo<i32>  |
1372        //  ---------------------------------
1373        //      |
1374        //  ----------------------------------
1375        //  | crate B: type Baz = A::Foo<i8> |
1376        //  |          type Xyy = A::Foo<i8> |
1377        //  |          impl Z for Xyy        |
1378        //  ----------------------------------
1379        // ```
1380        //
1381        // The type.impl/A/struct.Foo.js JS file has a structure kinda like this:
1382        //
1383        // ```js
1384        // JSONP({
1385        // "A": [["impl Y for Foo<i32>", "Y", "A::Bar"]],
1386        // "B": [["impl X for Foo<i8>", "X", "B::Baz", "B::Xyy"], ["impl Z for Xyy", "Z", "B::Baz"]],
1387        // });
1388        // ```
1389        //
1390        // When the type.impl file is loaded, only the current crate's docs are
1391        // actually used. The main reason to bundle them together is that there's
1392        // enough duplication in them for DEFLATE to remove the redundancy.
1393        //
1394        // The contents of a crate are a list of impl blocks, themselves
1395        // represented as lists. The first item in the sublist is the HTML block,
1396        // the second item is the name of the trait (which goes in the sidebar),
1397        // and all others are the names of type aliases that successfully match.
1398        //
1399        // This way:
1400        //
1401        // - There's no need to generate these files for types that have no aliases
1402        //   in the current crate. If a dependent crate makes a type alias, it'll
1403        //   take care of generating its own docs.
1404        // - There's no need to reimplement parts of the type checker in
1405        //   JavaScript. The Rust backend does the checking, and includes its
1406        //   results in the file.
1407        // - Docs defined directly on the type alias are dropped directly in the
1408        //   HTML by `render_assoc_items`, and are accessible without JavaScript.
1409        //   The JSONP file will not list impl items that are known to be part
1410        //   of the main HTML file already.
1411        //
1412        // [JSONP]: https://en.wikipedia.org/wiki/JSONP
1413        // [^115718]: https://github.com/rust-lang/rust/issues/115718
1414        let cache = &cx.shared.cache;
1415        if let Some(target_did) = t.type_.def_id(cache)
1416            && let get_extern = { || cache.external_paths.get(&target_did) }
1417            && let Some(&(ref target_fqp, target_type)) =
1418                cache.paths.get(&target_did).or_else(get_extern)
1419            && target_type.is_adt() // primitives cannot be inlined
1420            && let Some(self_did) = it.item_id.as_def_id()
1421            && let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) }
1422            && let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local)
1423        {
1424            let mut js_src_path: UrlPartsBuilder =
1425                iter::repeat_n("..", cx.current.len()).chain(iter::once("type.impl")).collect();
1426            js_src_path.extend(target_fqp[..target_fqp.len() - 1].iter().copied());
1427            js_src_path.push_fmt(format_args!("{target_type}.{}.js", target_fqp.last().unwrap()));
1428            let self_path = join_path_syms(self_fqp);
1429            write!(
1430                w,
1431                "<script src=\"{src}\" data-self-path=\"{self_path}\" async></script>",
1432                src = js_src_path.finish(),
1433            )?;
1434        }
1435        Ok(())
1436    })
1437}
1438
1439#[derive(Template)]
1440#[template(path = "item_union.html")]
1441struct ItemUnion<'a, 'cx> {
1442    cx: &'a Context<'cx>,
1443    it: &'a clean::Item,
1444    fields: &'a [clean::Item],
1445    generics: &'a clean::Generics,
1446    is_type_alias: bool,
1447    def_id: DefId,
1448}
1449
1450impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
1451    fn document(&self) -> impl fmt::Display {
1452        document(self.cx, self.it, None, HeadingOffset::H2)
1453    }
1454
1455    fn document_type_layout(&self) -> impl fmt::Display {
1456        let def_id = self.it.item_id.expect_def_id();
1457        document_type_layout(self.cx, def_id)
1458    }
1459
1460    fn render_assoc_items(&self) -> impl fmt::Display {
1461        let def_id = self.it.item_id.expect_def_id();
1462        render_assoc_items(self.cx, self.it, def_id, AssocItemRender::All)
1463    }
1464
1465    fn render_union(&self) -> impl Display {
1466        render_union(
1467            self.it,
1468            Some(self.generics),
1469            self.fields,
1470            self.def_id,
1471            self.is_type_alias,
1472            self.cx,
1473        )
1474    }
1475
1476    fn print_field_attrs(&self, field: &'a clean::Item) -> impl Display {
1477        fmt::from_fn(move |w| {
1478            render_attributes_in_code(w, field, "", self.cx)?;
1479            Ok(())
1480        })
1481    }
1482
1483    fn document_field(&self, field: &'a clean::Item) -> impl Display {
1484        document(self.cx, field, Some(self.it), HeadingOffset::H3)
1485    }
1486
1487    fn stability_field(&self, field: &clean::Item) -> Option<String> {
1488        field.stability_class(self.cx.tcx())
1489    }
1490
1491    fn print_ty(&self, ty: &'a clean::Type) -> impl Display {
1492        print_type(ty, self.cx)
1493    }
1494
1495    // FIXME (GuillaumeGomez): When <https://github.com/askama-rs/askama/issues/452> is implemented,
1496    // we can replace the returned value with:
1497    //
1498    // `iter::Peekable<impl Iterator<Item = (&'a clean::Item, &'a clean::Type)>>`
1499    //
1500    // And update `item_union.html`.
1501    fn fields_iter(&self) -> impl Iterator<Item = (&'a clean::Item, &'a clean::Type)> {
1502        self.fields.iter().filter_map(|f| match f.kind {
1503            clean::StructFieldItem(ref ty) => Some((f, ty)),
1504            _ => None,
1505        })
1506    }
1507}
1508
1509fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display {
1510    fmt::from_fn(|w| {
1511        ItemUnion {
1512            cx,
1513            it,
1514            fields: &s.fields,
1515            generics: &s.generics,
1516            is_type_alias: false,
1517            def_id: it.def_id().unwrap(),
1518        }
1519        .render_into(w)?;
1520        Ok(())
1521    })
1522}
1523
1524fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Display {
1525    fmt::from_fn(|f| {
1526        if !s.is_empty()
1527            && s.iter().all(|field| {
1528                matches!(field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
1529            })
1530        {
1531            return f.write_str("<span class=\"comment\">/* private fields */</span>");
1532        }
1533
1534        s.iter()
1535            .map(|ty| {
1536                fmt::from_fn(|f| match ty.kind {
1537                    clean::StrippedItem(box clean::StructFieldItem(_)) => f.write_str("_"),
1538                    clean::StructFieldItem(ref ty) => write!(f, "{}", print_type(ty, cx)),
1539                    _ => unreachable!(),
1540                })
1541            })
1542            .joined(", ", f)
1543    })
1544}
1545
1546struct DisplayEnum<'clean> {
1547    variants: &'clean IndexVec<VariantIdx, clean::Item>,
1548    generics: &'clean clean::Generics,
1549    is_non_exhaustive: bool,
1550    def_id: DefId,
1551}
1552
1553impl<'clean> DisplayEnum<'clean> {
1554    fn render_into<W: fmt::Write>(
1555        self,
1556        cx: &Context<'_>,
1557        it: &clean::Item,
1558        is_type_alias: bool,
1559        w: &mut W,
1560    ) -> fmt::Result {
1561        let non_stripped_variant_count = self.variants.iter().filter(|i| !i.is_stripped()).count();
1562        let variants_len = self.variants.len();
1563        let has_stripped_entries = variants_len != non_stripped_variant_count;
1564
1565        wrap_item(w, |w| {
1566            if is_type_alias {
1567                // For now the only attributes we render for type aliases are `repr` attributes.
1568                render_repr_attribute_in_code(w, cx, self.def_id)?;
1569            } else {
1570                render_attributes_in_code(w, it, "", cx)?;
1571            }
1572            write!(
1573                w,
1574                "{}enum {}{}{}",
1575                visibility_print_with_space(it, cx),
1576                it.name.unwrap(),
1577                print_generics(&self.generics, cx),
1578                render_enum_fields(
1579                    cx,
1580                    Some(self.generics),
1581                    self.variants,
1582                    non_stripped_variant_count,
1583                    has_stripped_entries,
1584                    self.is_non_exhaustive,
1585                    self.def_id,
1586                ),
1587            )
1588        })?;
1589
1590        let def_id = it.item_id.expect_def_id();
1591        let layout_def_id = if is_type_alias {
1592            self.def_id
1593        } else {
1594            write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1595            // We don't return the same `DefId` since the layout size of the type alias might be
1596            // different since we might have more information on the generics.
1597            def_id
1598        };
1599
1600        if non_stripped_variant_count != 0 {
1601            write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?;
1602        }
1603        write!(
1604            w,
1605            "{}{}",
1606            render_assoc_items(cx, it, def_id, AssocItemRender::All),
1607            document_type_layout(cx, layout_def_id)
1608        )
1609    }
1610}
1611
1612fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display {
1613    fmt::from_fn(|w| {
1614        DisplayEnum {
1615            variants: &e.variants,
1616            generics: &e.generics,
1617            is_non_exhaustive: it.is_non_exhaustive(),
1618            def_id: it.def_id().unwrap(),
1619        }
1620        .render_into(cx, it, false, w)
1621    })
1622}
1623
1624/// It'll return false if any variant is not a C-like variant. Otherwise it'll return true if at
1625/// least one of them has an explicit discriminant or if the enum has `#[repr(C)]` or an integer
1626/// `repr`.
1627fn should_show_enum_discriminant(
1628    cx: &Context<'_>,
1629    enum_def_id: DefId,
1630    variants: &IndexVec<VariantIdx, clean::Item>,
1631) -> bool {
1632    let mut has_variants_with_value = false;
1633    for variant in variants {
1634        if let clean::VariantItem(ref var) = variant.kind
1635            && matches!(var.kind, clean::VariantKind::CLike)
1636        {
1637            has_variants_with_value |= var.discriminant.is_some();
1638        } else {
1639            return false;
1640        }
1641    }
1642    if has_variants_with_value {
1643        return true;
1644    }
1645    let repr = cx.tcx().adt_def(enum_def_id).repr();
1646    repr.c() || repr.int.is_some()
1647}
1648
1649fn display_c_like_variant(
1650    cx: &Context<'_>,
1651    item: &clean::Item,
1652    variant: &clean::Variant,
1653    index: VariantIdx,
1654    should_show_enum_discriminant: bool,
1655    enum_def_id: DefId,
1656) -> impl fmt::Display {
1657    fmt::from_fn(move |w| {
1658        let name = item.name.unwrap();
1659        if let Some(ref value) = variant.discriminant {
1660            write!(w, "{} = {}", name.as_str(), value.value(cx.tcx(), true))?;
1661        } else if should_show_enum_discriminant {
1662            let adt_def = cx.tcx().adt_def(enum_def_id);
1663            let discr = adt_def.discriminant_for_variant(cx.tcx(), index);
1664            // Use `discr`'s `Display` impl to render the value with the correct
1665            // signedness, including proper sign-extension for signed types.
1666            write!(w, "{} = {}", name.as_str(), discr)?;
1667        } else {
1668            write!(w, "{name}")?;
1669        }
1670        Ok(())
1671    })
1672}
1673
1674fn render_enum_fields(
1675    cx: &Context<'_>,
1676    g: Option<&clean::Generics>,
1677    variants: &IndexVec<VariantIdx, clean::Item>,
1678    count_variants: usize,
1679    has_stripped_entries: bool,
1680    is_non_exhaustive: bool,
1681    enum_def_id: DefId,
1682) -> impl fmt::Display {
1683    fmt::from_fn(move |w| {
1684        let should_show_enum_discriminant =
1685            should_show_enum_discriminant(cx, enum_def_id, variants);
1686        if let Some(generics) = g
1687            && let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline)
1688        {
1689            write!(w, "{where_clause}")?;
1690        } else {
1691            // If there wasn't a `where` clause, we add a whitespace.
1692            w.write_char(' ')?;
1693        }
1694
1695        let variants_stripped = has_stripped_entries;
1696        if count_variants == 0 && !variants_stripped {
1697            w.write_str("{}")
1698        } else {
1699            w.write_str("{\n")?;
1700            let toggle = should_hide_fields(count_variants);
1701            if toggle {
1702                toggle_open(&mut *w, format_args!("{count_variants} variants"));
1703            }
1704            const TAB: &str = "    ";
1705            for (index, v) in variants.iter_enumerated() {
1706                if v.is_stripped() {
1707                    continue;
1708                }
1709                render_attributes_in_code(w, v, TAB, cx)?;
1710                w.write_str(TAB)?;
1711                match v.kind {
1712                    clean::VariantItem(ref var) => match var.kind {
1713                        clean::VariantKind::CLike => {
1714                            write!(
1715                                w,
1716                                "{}",
1717                                display_c_like_variant(
1718                                    cx,
1719                                    v,
1720                                    var,
1721                                    index,
1722                                    should_show_enum_discriminant,
1723                                    enum_def_id,
1724                                )
1725                            )?;
1726                        }
1727                        clean::VariantKind::Tuple(ref s) => {
1728                            write!(w, "{}({})", v.name.unwrap(), print_tuple_struct_fields(cx, s))?;
1729                        }
1730                        clean::VariantKind::Struct(ref s) => {
1731                            write!(
1732                                w,
1733                                "{}",
1734                                render_struct(v, None, None, &s.fields, TAB, false, cx)
1735                            )?;
1736                        }
1737                    },
1738                    _ => unreachable!(),
1739                }
1740                w.write_str(",\n")?;
1741            }
1742
1743            if variants_stripped && !is_non_exhaustive {
1744                w.write_str("    <span class=\"comment\">// some variants omitted</span>\n")?;
1745            }
1746            if toggle {
1747                toggle_close(&mut *w);
1748            }
1749            w.write_str("}")
1750        }
1751    })
1752}
1753
1754fn item_variants(
1755    cx: &Context<'_>,
1756    it: &clean::Item,
1757    variants: &IndexVec<VariantIdx, clean::Item>,
1758    enum_def_id: DefId,
1759) -> impl fmt::Display {
1760    fmt::from_fn(move |w| {
1761        let tcx = cx.tcx();
1762        write!(
1763            w,
1764            "{}",
1765            write_section_heading(
1766                &format!("Variants{}", document_non_exhaustive_header(it)),
1767                "variants",
1768                Some("variants"),
1769                format!("{}<div class=\"variants\">", document_non_exhaustive(it)),
1770            ),
1771        )?;
1772
1773        let should_show_enum_discriminant =
1774            should_show_enum_discriminant(cx, enum_def_id, variants);
1775        for (index, variant) in variants.iter_enumerated() {
1776            if variant.is_stripped() {
1777                continue;
1778            }
1779            let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
1780            write!(
1781                w,
1782                "<section id=\"{id}\" class=\"variant\">\
1783                    <a href=\"#{id}\" class=\"anchor\">§</a>\
1784                    {}\
1785                    <h3 class=\"code-header\">",
1786                render_stability_since_raw_with_extra(
1787                    variant.stable_since(tcx),
1788                    variant.const_stability(tcx),
1789                    " rightside",
1790                )
1791                .maybe_display()
1792            )?;
1793            render_attributes_in_code(w, variant, "", cx)?;
1794            if let clean::VariantItem(ref var) = variant.kind
1795                && let clean::VariantKind::CLike = var.kind
1796            {
1797                write!(
1798                    w,
1799                    "{}",
1800                    display_c_like_variant(
1801                        cx,
1802                        variant,
1803                        var,
1804                        index,
1805                        should_show_enum_discriminant,
1806                        enum_def_id,
1807                    )
1808                )?;
1809            } else {
1810                w.write_str(variant.name.unwrap().as_str())?;
1811            }
1812
1813            let clean::VariantItem(variant_data) = &variant.kind else { unreachable!() };
1814
1815            if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
1816                write!(w, "({})", print_tuple_struct_fields(cx, s))?;
1817            }
1818            w.write_str("</h3></section>")?;
1819
1820            write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4))?;
1821
1822            let heading_and_fields = match &variant_data.kind {
1823                clean::VariantKind::Struct(s) => {
1824                    // If there is no field to display, no need to add the heading.
1825                    if s.fields.iter().any(|f| !f.is_doc_hidden()) {
1826                        Some(("Fields", &s.fields))
1827                    } else {
1828                        None
1829                    }
1830                }
1831                clean::VariantKind::Tuple(fields) => {
1832                    // Documentation on tuple variant fields is rare, so to reduce noise we only emit
1833                    // the section if at least one field is documented.
1834                    if fields.iter().any(|f| !f.doc_value().is_empty()) {
1835                        Some(("Tuple Fields", fields))
1836                    } else {
1837                        None
1838                    }
1839                }
1840                clean::VariantKind::CLike => None,
1841            };
1842
1843            if let Some((heading, fields)) = heading_and_fields {
1844                let variant_id =
1845                    cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
1846                write!(
1847                    w,
1848                    "<div class=\"sub-variant\" id=\"{variant_id}\">\
1849                        <h4>{heading}</h4>\
1850                        {}",
1851                    document_non_exhaustive(variant)
1852                )?;
1853                for field in fields {
1854                    match field.kind {
1855                        clean::StrippedItem(box clean::StructFieldItem(_)) => {}
1856                        clean::StructFieldItem(ref ty) => {
1857                            let id = cx.derive_id(format!(
1858                                "variant.{}.field.{}",
1859                                variant.name.unwrap(),
1860                                field.name.unwrap()
1861                            ));
1862                            write!(
1863                                w,
1864                                "<div class=\"sub-variant-field\">\
1865                                    <span id=\"{id}\" class=\"section-header\">\
1866                                        <a href=\"#{id}\" class=\"anchor field\">§</a>\
1867                                        <code>"
1868                            )?;
1869                            render_attributes_in_code(w, field, "", cx)?;
1870                            write!(
1871                                w,
1872                                "{f}: {t}</code>\
1873                                    </span>\
1874                                    {doc}\
1875                                </div>",
1876                                f = field.name.unwrap(),
1877                                t = print_type(ty, cx),
1878                                doc = document(cx, field, Some(variant), HeadingOffset::H5),
1879                            )?;
1880                        }
1881                        _ => unreachable!(),
1882                    }
1883                }
1884                w.write_str("</div>")?;
1885            }
1886        }
1887        w.write_str("</div>")
1888    })
1889}
1890
1891fn item_macro(cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) -> impl fmt::Display {
1892    fmt::from_fn(|w| {
1893        wrap_item(w, |w| {
1894            render_attributes_in_code(w, it, "", cx)?;
1895            if !t.macro_rules {
1896                write!(w, "{}", visibility_print_with_space(it, cx))?;
1897            }
1898            write!(w, "{}", Escape(&t.source))
1899        })?;
1900        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
1901    })
1902}
1903
1904fn item_proc_macro(cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) -> impl fmt::Display {
1905    fmt::from_fn(|w| {
1906        wrap_item(w, |w| {
1907            let name = it.name.expect("proc-macros always have names");
1908            match m.kind {
1909                MacroKind::Bang => {
1910                    write!(w, "{name}!() {{ <span class=\"comment\">/* proc-macro */</span> }}")?;
1911                }
1912                MacroKind::Attr => {
1913                    write!(w, "#[{name}]")?;
1914                }
1915                MacroKind::Derive => {
1916                    write!(w, "#[derive({name})]")?;
1917                    if !m.helpers.is_empty() {
1918                        w.write_str(
1919                            "\n{\n    \
1920                            <span class=\"comment\">// Attributes available to this derive:</span>\n",
1921                        )?;
1922                        for attr in &m.helpers {
1923                            writeln!(w, "    #[{attr}]")?;
1924                        }
1925                        w.write_str("}\n")?;
1926                    }
1927                }
1928            }
1929            fmt::Result::Ok(())
1930        })?;
1931        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
1932    })
1933}
1934
1935fn item_primitive(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
1936    fmt::from_fn(|w| {
1937        let def_id = it.item_id.expect_def_id();
1938        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1939        if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
1940            write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All))
1941        } else {
1942            // We handle the "reference" primitive type on its own because we only want to list
1943            // implementations on generic types.
1944            let (concrete, synthetic, blanket_impl) =
1945                get_filtered_impls_for_reference(&cx.shared, it);
1946
1947            render_all_impls(w, cx, it, &concrete, &synthetic, &blanket_impl)
1948        }
1949    })
1950}
1951
1952fn item_constant(
1953    cx: &Context<'_>,
1954    it: &clean::Item,
1955    generics: &clean::Generics,
1956    ty: &clean::Type,
1957    c: &clean::ConstantKind,
1958) -> impl fmt::Display {
1959    fmt::from_fn(|w| {
1960        wrap_item(w, |w| {
1961            let tcx = cx.tcx();
1962            render_attributes_in_code(w, it, "", cx)?;
1963
1964            write!(
1965                w,
1966                "{vis}const {name}{generics}: {typ}{where_clause}",
1967                vis = visibility_print_with_space(it, cx),
1968                name = it.name.unwrap(),
1969                generics = print_generics(generics, cx),
1970                typ = print_type(ty, cx),
1971                where_clause =
1972                    print_where_clause(generics, cx, 0, Ending::NoNewline).maybe_display(),
1973            )?;
1974
1975            // FIXME: The code below now prints
1976            //            ` = _; // 100i32`
1977            //        if the expression is
1978            //            `50 + 50`
1979            //        which looks just wrong.
1980            //        Should we print
1981            //            ` = 100i32;`
1982            //        instead?
1983
1984            let value = c.value(tcx);
1985            let is_literal = c.is_literal(tcx);
1986            let expr = c.expr(tcx);
1987            if value.is_some() || is_literal {
1988                write!(w, " = {expr};", expr = Escape(&expr))?;
1989            } else {
1990                w.write_str(";")?;
1991            }
1992
1993            if !is_literal && let Some(value) = &value {
1994                let value_lowercase = value.to_lowercase();
1995                let expr_lowercase = expr.to_lowercase();
1996
1997                if value_lowercase != expr_lowercase
1998                    && value_lowercase.trim_end_matches("i32") != expr_lowercase
1999                {
2000                    write!(w, " // {value}", value = Escape(value))?;
2001                }
2002            }
2003            Ok::<(), fmt::Error>(())
2004        })?;
2005
2006        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
2007    })
2008}
2009
2010struct DisplayStruct<'a> {
2011    ctor_kind: Option<CtorKind>,
2012    generics: &'a clean::Generics,
2013    fields: &'a [clean::Item],
2014    def_id: DefId,
2015}
2016
2017impl<'a> DisplayStruct<'a> {
2018    fn render_into<W: fmt::Write>(
2019        self,
2020        cx: &Context<'_>,
2021        it: &clean::Item,
2022        is_type_alias: bool,
2023        w: &mut W,
2024    ) -> fmt::Result {
2025        wrap_item(w, |w| {
2026            if is_type_alias {
2027                // For now the only attributes we render for type aliases are `repr` attributes.
2028                render_repr_attribute_in_code(w, cx, self.def_id)?;
2029            } else {
2030                render_attributes_in_code(w, it, "", cx)?;
2031            }
2032            write!(
2033                w,
2034                "{}",
2035                render_struct(it, Some(self.generics), self.ctor_kind, self.fields, "", true, cx)
2036            )
2037        })?;
2038
2039        if !is_type_alias {
2040            write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
2041        }
2042
2043        let def_id = it.item_id.expect_def_id();
2044        write!(
2045            w,
2046            "{}{}{}",
2047            item_fields(cx, it, self.fields, self.ctor_kind),
2048            render_assoc_items(cx, it, def_id, AssocItemRender::All),
2049            document_type_layout(cx, def_id),
2050        )
2051    }
2052}
2053
2054fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display {
2055    fmt::from_fn(|w| {
2056        DisplayStruct {
2057            ctor_kind: s.ctor_kind,
2058            generics: &s.generics,
2059            fields: s.fields.as_slice(),
2060            def_id: it.def_id().unwrap(),
2061        }
2062        .render_into(cx, it, false, w)
2063    })
2064}
2065
2066fn item_fields(
2067    cx: &Context<'_>,
2068    it: &clean::Item,
2069    fields: &[clean::Item],
2070    ctor_kind: Option<CtorKind>,
2071) -> impl fmt::Display {
2072    fmt::from_fn(move |w| {
2073        let mut fields = fields
2074            .iter()
2075            .filter_map(|f| match f.kind {
2076                clean::StructFieldItem(ref ty) => Some((f, ty)),
2077                _ => None,
2078            })
2079            .peekable();
2080        if let None | Some(CtorKind::Fn) = ctor_kind
2081            && fields.peek().is_some()
2082        {
2083            let title = format!(
2084                "{}{}",
2085                if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
2086                document_non_exhaustive_header(it),
2087            );
2088            write!(
2089                w,
2090                "{}",
2091                write_section_heading(
2092                    &title,
2093                    "fields",
2094                    Some("fields"),
2095                    document_non_exhaustive(it)
2096                )
2097            )?;
2098            for (index, (field, ty)) in fields.enumerate() {
2099                let field_name =
2100                    field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string());
2101                let id = cx.derive_id(format!("{typ}.{field_name}", typ = ItemType::StructField));
2102                write!(
2103                    w,
2104                    "<span id=\"{id}\" class=\"{item_type} section-header\">\
2105                        <a href=\"#{id}\" class=\"anchor field\">§</a>\
2106                        <code>",
2107                    item_type = ItemType::StructField,
2108                )?;
2109                render_attributes_in_code(w, field, "", cx)?;
2110                write!(
2111                    w,
2112                    "{field_name}: {ty}</code>\
2113                    </span>\
2114                    {doc}",
2115                    ty = print_type(ty, cx),
2116                    doc = document(cx, field, Some(it), HeadingOffset::H3),
2117                )?;
2118            }
2119        }
2120        Ok(())
2121    })
2122}
2123
2124fn item_static(
2125    cx: &Context<'_>,
2126    it: &clean::Item,
2127    s: &clean::Static,
2128    safety: Option<hir::Safety>,
2129) -> impl fmt::Display {
2130    fmt::from_fn(move |w| {
2131        wrap_item(w, |w| {
2132            render_attributes_in_code(w, it, "", cx)?;
2133            write!(
2134                w,
2135                "{vis}{safe}static {mutability}{name}: {typ}",
2136                vis = visibility_print_with_space(it, cx),
2137                safe = safety.map(|safe| safe.prefix_str()).unwrap_or(""),
2138                mutability = s.mutability.print_with_space(),
2139                name = it.name.unwrap(),
2140                typ = print_type(&s.type_, cx)
2141            )
2142        })?;
2143
2144        write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
2145    })
2146}
2147
2148fn item_foreign_type(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
2149    fmt::from_fn(|w| {
2150        wrap_item(w, |w| {
2151            w.write_str("extern {\n")?;
2152            render_attributes_in_code(w, it, "", cx)?;
2153            write!(w, "    {}type {};\n}}", visibility_print_with_space(it, cx), it.name.unwrap())
2154        })?;
2155
2156        write!(
2157            w,
2158            "{}{}",
2159            document(cx, it, None, HeadingOffset::H2),
2160            render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
2161        )
2162    })
2163}
2164
2165fn item_keyword_or_attribute(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
2166    document(cx, it, None, HeadingOffset::H2)
2167}
2168
2169/// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order).
2170///
2171/// This code is copied from [`rustfmt`], and should probably be released as a crate at some point.
2172///
2173/// [`rustfmt`]:https://github.com/rust-lang/rustfmt/blob/rustfmt-2.0.0-rc.2/src/formatting/reorder.rs#L32
2174pub(crate) fn compare_names(left: &str, right: &str) -> Ordering {
2175    let mut left = left.chars().peekable();
2176    let mut right = right.chars().peekable();
2177
2178    loop {
2179        // The strings are equal so far and not inside a number in both sides
2180        let (l, r) = match (left.next(), right.next()) {
2181            // Is this the end of both strings?
2182            (None, None) => return Ordering::Equal,
2183            // If for one, the shorter one is considered smaller
2184            (None, Some(_)) => return Ordering::Less,
2185            (Some(_), None) => return Ordering::Greater,
2186            (Some(l), Some(r)) => (l, r),
2187        };
2188        let next_ordering = match (l.to_digit(10), r.to_digit(10)) {
2189            // If neither is a digit, just compare them
2190            (None, None) => Ord::cmp(&l, &r),
2191            // The one with shorter non-digit run is smaller
2192            // For `strverscmp` it's smaller iff next char in longer is greater than digits
2193            (None, Some(_)) => Ordering::Greater,
2194            (Some(_), None) => Ordering::Less,
2195            // If both start numbers, we have to compare the numbers
2196            (Some(l), Some(r)) => {
2197                if l == 0 || r == 0 {
2198                    // Fraction mode: compare as if there was leading `0.`
2199                    let ordering = Ord::cmp(&l, &r);
2200                    if ordering != Ordering::Equal {
2201                        return ordering;
2202                    }
2203                    loop {
2204                        // Get next pair
2205                        let (l, r) = match (left.peek(), right.peek()) {
2206                            // Is this the end of both strings?
2207                            (None, None) => return Ordering::Equal,
2208                            // If for one, the shorter one is considered smaller
2209                            (None, Some(_)) => return Ordering::Less,
2210                            (Some(_), None) => return Ordering::Greater,
2211                            (Some(l), Some(r)) => (l, r),
2212                        };
2213                        // Are they digits?
2214                        match (l.to_digit(10), r.to_digit(10)) {
2215                            // If out of digits, use the stored ordering due to equal length
2216                            (None, None) => break Ordering::Equal,
2217                            // If one is shorter, it's smaller
2218                            (None, Some(_)) => return Ordering::Less,
2219                            (Some(_), None) => return Ordering::Greater,
2220                            // If both are digits, consume them and take into account
2221                            (Some(l), Some(r)) => {
2222                                left.next();
2223                                right.next();
2224                                let ordering = Ord::cmp(&l, &r);
2225                                if ordering != Ordering::Equal {
2226                                    return ordering;
2227                                }
2228                            }
2229                        }
2230                    }
2231                } else {
2232                    // Integer mode
2233                    let mut same_length_ordering = Ord::cmp(&l, &r);
2234                    loop {
2235                        // Get next pair
2236                        let (l, r) = match (left.peek(), right.peek()) {
2237                            // Is this the end of both strings?
2238                            (None, None) => return same_length_ordering,
2239                            // If for one, the shorter one is considered smaller
2240                            (None, Some(_)) => return Ordering::Less,
2241                            (Some(_), None) => return Ordering::Greater,
2242                            (Some(l), Some(r)) => (l, r),
2243                        };
2244                        // Are they digits?
2245                        match (l.to_digit(10), r.to_digit(10)) {
2246                            // If out of digits, use the stored ordering due to equal length
2247                            (None, None) => break same_length_ordering,
2248                            // If one is shorter, it's smaller
2249                            (None, Some(_)) => return Ordering::Less,
2250                            (Some(_), None) => return Ordering::Greater,
2251                            // If both are digits, consume them and take into account
2252                            (Some(l), Some(r)) => {
2253                                left.next();
2254                                right.next();
2255                                same_length_ordering = same_length_ordering.then(Ord::cmp(&l, &r));
2256                            }
2257                        }
2258                    }
2259                }
2260            }
2261        };
2262        if next_ordering != Ordering::Equal {
2263            return next_ordering;
2264        }
2265    }
2266}
2267
2268pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
2269    let mut s = join_path_syms(&cx.current);
2270    s.push_str("::");
2271    s.push_str(item.name.unwrap().as_str());
2272    s
2273}
2274
2275pub(super) fn print_item_path(ty: ItemType, name: &str) -> impl Display {
2276    fmt::from_fn(move |f| match ty {
2277        ItemType::Module => write!(f, "{}index.html", ensure_trailing_slash(name)),
2278        _ => write!(f, "{ty}.{name}.html"),
2279    })
2280}
2281
2282fn print_bounds(
2283    bounds: &[clean::GenericBound],
2284    trait_alias: bool,
2285    cx: &Context<'_>,
2286) -> impl Display {
2287    (!bounds.is_empty())
2288        .then_some(fmt::from_fn(move |f| {
2289            let has_lots_of_bounds = bounds.len() > 2;
2290            let inter_str = if has_lots_of_bounds { "\n    + " } else { " + " };
2291            if !trait_alias {
2292                if has_lots_of_bounds {
2293                    f.write_str(":\n    ")?;
2294                } else {
2295                    f.write_str(": ")?;
2296                }
2297            }
2298
2299            bounds.iter().map(|p| print_generic_bound(p, cx)).joined(inter_str, f)
2300        }))
2301        .maybe_display()
2302}
2303
2304fn wrap_item<W, F>(w: &mut W, f: F) -> fmt::Result
2305where
2306    W: fmt::Write,
2307    F: FnOnce(&mut W) -> fmt::Result,
2308{
2309    w.write_str(r#"<pre class="rust item-decl"><code>"#)?;
2310    f(w)?;
2311    w.write_str("</code></pre>")
2312}
2313
2314#[derive(PartialEq, Eq)]
2315struct ImplString {
2316    rendered: String,
2317    is_negative: bool,
2318}
2319
2320impl ImplString {
2321    fn new(i: &Impl, cx: &Context<'_>) -> ImplString {
2322        let impl_ = i.inner_impl();
2323        ImplString {
2324            is_negative: impl_.is_negative_trait_impl(),
2325            rendered: format!("{}", print_impl(impl_, false, cx)),
2326        }
2327    }
2328}
2329
2330impl PartialOrd for ImplString {
2331    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2332        Some(Ord::cmp(self, other))
2333    }
2334}
2335
2336impl Ord for ImplString {
2337    fn cmp(&self, other: &Self) -> Ordering {
2338        // We sort negative impls first.
2339        match (self.is_negative, other.is_negative) {
2340            (false, true) => Ordering::Greater,
2341            (true, false) => Ordering::Less,
2342            _ => compare_names(&self.rendered, &other.rendered),
2343        }
2344    }
2345}
2346
2347fn render_implementor(
2348    cx: &Context<'_>,
2349    implementor: &Impl,
2350    trait_: &clean::Item,
2351    implementor_dups: &FxHashMap<Symbol, (DefId, bool)>,
2352    aliases: &[String],
2353) -> impl fmt::Display {
2354    // If there's already another implementor that has the same abridged name, use the
2355    // full path, for example in `std::iter::ExactSizeIterator`
2356    let use_absolute = match implementor.inner_impl().for_ {
2357        clean::Type::Path { ref path, .. }
2358        | clean::BorrowedRef { type_: box clean::Type::Path { ref path, .. }, .. }
2359            if !path.is_assoc_ty() =>
2360        {
2361            implementor_dups[&path.last()].1
2362        }
2363        _ => false,
2364    };
2365    render_impl(
2366        cx,
2367        implementor,
2368        trait_,
2369        AssocItemLink::Anchor(None),
2370        RenderMode::Normal,
2371        Some(use_absolute),
2372        aliases,
2373        ImplRenderingParameters {
2374            show_def_docs: false,
2375            show_default_items: false,
2376            show_non_assoc_items: false,
2377            toggle_open_by_default: false,
2378        },
2379    )
2380}
2381
2382fn render_union(
2383    it: &clean::Item,
2384    g: Option<&clean::Generics>,
2385    fields: &[clean::Item],
2386    def_id: DefId,
2387    is_type_alias: bool,
2388    cx: &Context<'_>,
2389) -> impl Display {
2390    fmt::from_fn(move |mut f| {
2391        if is_type_alias {
2392            // For now the only attributes we render for type aliases are `repr` attributes.
2393            render_repr_attribute_in_code(f, cx, def_id)?;
2394        } else {
2395            render_attributes_in_code(f, it, "", cx)?;
2396        }
2397        write!(f, "{}union {}", visibility_print_with_space(it, cx), it.name.unwrap(),)?;
2398
2399        let where_displayed = if let Some(generics) = g {
2400            write!(f, "{}", print_generics(generics, cx))?;
2401            if let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline) {
2402                write!(f, "{where_clause}")?;
2403                true
2404            } else {
2405                false
2406            }
2407        } else {
2408            false
2409        };
2410
2411        // If there wasn't a `where` clause, we add a whitespace.
2412        if !where_displayed {
2413            f.write_str(" ")?;
2414        }
2415
2416        writeln!(f, "{{")?;
2417        let count_fields =
2418            fields.iter().filter(|field| matches!(field.kind, clean::StructFieldItem(..))).count();
2419        let toggle = should_hide_fields(count_fields);
2420        if toggle {
2421            toggle_open(&mut f, format_args!("{count_fields} fields"));
2422        }
2423
2424        for field in fields {
2425            if let clean::StructFieldItem(ref ty) = field.kind {
2426                render_attributes_in_code(&mut f, field, "    ", cx)?;
2427                writeln!(
2428                    f,
2429                    "    {}{}: {},",
2430                    visibility_print_with_space(field, cx),
2431                    field.name.unwrap(),
2432                    print_type(ty, cx)
2433                )?;
2434            }
2435        }
2436
2437        if it.has_stripped_entries().unwrap() {
2438            writeln!(f, "    <span class=\"comment\">/* private fields */</span>")?;
2439        }
2440        if toggle {
2441            toggle_close(&mut f);
2442        }
2443        f.write_str("}").unwrap();
2444        Ok(())
2445    })
2446}
2447
2448fn render_struct(
2449    it: &clean::Item,
2450    g: Option<&clean::Generics>,
2451    ty: Option<CtorKind>,
2452    fields: &[clean::Item],
2453    tab: &str,
2454    structhead: bool,
2455    cx: &Context<'_>,
2456) -> impl fmt::Display {
2457    fmt::from_fn(move |w| {
2458        write!(
2459            w,
2460            "{}{}{}",
2461            visibility_print_with_space(it, cx),
2462            if structhead { "struct " } else { "" },
2463            it.name.unwrap()
2464        )?;
2465        if let Some(g) = g {
2466            write!(w, "{}", print_generics(g, cx))?;
2467        }
2468        write!(
2469            w,
2470            "{}",
2471            render_struct_fields(
2472                g,
2473                ty,
2474                fields,
2475                tab,
2476                structhead,
2477                it.has_stripped_entries().unwrap_or(false),
2478                cx,
2479            )
2480        )
2481    })
2482}
2483
2484fn render_struct_fields(
2485    g: Option<&clean::Generics>,
2486    ty: Option<CtorKind>,
2487    fields: &[clean::Item],
2488    tab: &str,
2489    structhead: bool,
2490    has_stripped_entries: bool,
2491    cx: &Context<'_>,
2492) -> impl fmt::Display {
2493    fmt::from_fn(move |w| {
2494        match ty {
2495            None => {
2496                let where_displayed = if let Some(generics) = g
2497                    && let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline)
2498                {
2499                    write!(w, "{where_clause}")?;
2500                    true
2501                } else {
2502                    false
2503                };
2504
2505                // If there wasn't a `where` clause, we add a whitespace.
2506                if !where_displayed {
2507                    w.write_str(" {")?;
2508                } else {
2509                    w.write_str("{")?;
2510                }
2511                let count_fields =
2512                    fields.iter().filter(|f| matches!(f.kind, clean::StructFieldItem(..))).count();
2513                let has_visible_fields = count_fields > 0;
2514                let toggle = should_hide_fields(count_fields);
2515                if toggle {
2516                    toggle_open(&mut *w, format_args!("{count_fields} fields"));
2517                }
2518                if has_visible_fields {
2519                    writeln!(w)?;
2520                }
2521                for field in fields {
2522                    if let clean::StructFieldItem(ref ty) = field.kind {
2523                        render_attributes_in_code(w, field, &format!("{tab}    "), cx)?;
2524                        writeln!(
2525                            w,
2526                            "{tab}    {vis}{name}: {ty},",
2527                            vis = visibility_print_with_space(field, cx),
2528                            name = field.name.unwrap(),
2529                            ty = print_type(ty, cx)
2530                        )?;
2531                    }
2532                }
2533
2534                if has_visible_fields {
2535                    if has_stripped_entries {
2536                        writeln!(
2537                            w,
2538                            "{tab}    <span class=\"comment\">/* private fields */</span>"
2539                        )?;
2540                    }
2541                    write!(w, "{tab}")?;
2542                } else if has_stripped_entries {
2543                    write!(w, " <span class=\"comment\">/* private fields */</span> ")?;
2544                }
2545                if toggle {
2546                    toggle_close(&mut *w);
2547                }
2548                w.write_str("}")?;
2549            }
2550            Some(CtorKind::Fn) => {
2551                w.write_str("(")?;
2552                if !fields.is_empty()
2553                    && fields.iter().all(|field| {
2554                        matches!(field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
2555                    })
2556                {
2557                    write!(w, "<span class=\"comment\">/* private fields */</span>")?;
2558                } else {
2559                    for (i, field) in fields.iter().enumerate() {
2560                        if i > 0 {
2561                            w.write_str(", ")?;
2562                        }
2563                        match field.kind {
2564                            clean::StrippedItem(box clean::StructFieldItem(..)) => {
2565                                write!(w, "_")?;
2566                            }
2567                            clean::StructFieldItem(ref ty) => {
2568                                write!(
2569                                    w,
2570                                    "{}{}",
2571                                    visibility_print_with_space(field, cx),
2572                                    print_type(ty, cx),
2573                                )?;
2574                            }
2575                            _ => unreachable!(),
2576                        }
2577                    }
2578                }
2579                w.write_str(")")?;
2580                if let Some(g) = g {
2581                    write!(
2582                        w,
2583                        "{}",
2584                        print_where_clause(g, cx, 0, Ending::NoNewline).maybe_display()
2585                    )?;
2586                }
2587                // We only want a ";" when we are displaying a tuple struct, not a variant tuple struct.
2588                if structhead {
2589                    w.write_str(";")?;
2590                }
2591            }
2592            Some(CtorKind::Const) => {
2593                // Needed for PhantomData.
2594                if let Some(g) = g {
2595                    write!(
2596                        w,
2597                        "{}",
2598                        print_where_clause(g, cx, 0, Ending::NoNewline).maybe_display()
2599                    )?;
2600                }
2601                w.write_str(";")?;
2602            }
2603        }
2604        Ok(())
2605    })
2606}
2607
2608fn document_non_exhaustive_header(item: &clean::Item) -> &str {
2609    if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
2610}
2611
2612fn document_non_exhaustive(item: &clean::Item) -> impl Display {
2613    fmt::from_fn(|f| {
2614        if item.is_non_exhaustive() {
2615            write!(
2616                f,
2617                "<details class=\"toggle non-exhaustive\">\
2618                    <summary class=\"hideme\"><span>{}</span></summary>\
2619                    <div class=\"docblock\">",
2620                {
2621                    if item.is_struct() {
2622                        "This struct is marked as non-exhaustive"
2623                    } else if item.is_enum() {
2624                        "This enum is marked as non-exhaustive"
2625                    } else if item.is_variant() {
2626                        "This variant is marked as non-exhaustive"
2627                    } else {
2628                        "This type is marked as non-exhaustive"
2629                    }
2630                }
2631            )?;
2632
2633            if item.is_struct() {
2634                f.write_str(
2635                    "Non-exhaustive structs could have additional fields added in future. \
2636                    Therefore, non-exhaustive structs cannot be constructed in external crates \
2637                    using the traditional <code>Struct { .. }</code> syntax; cannot be \
2638                    matched against without a wildcard <code>..</code>; and \
2639                    struct update syntax will not work.",
2640                )?;
2641            } else if item.is_enum() {
2642                f.write_str(
2643                    "Non-exhaustive enums could have additional variants added in future. \
2644                    Therefore, when matching against variants of non-exhaustive enums, an \
2645                    extra wildcard arm must be added to account for any future variants.",
2646                )?;
2647            } else if item.is_variant() {
2648                f.write_str(
2649                    "Non-exhaustive enum variants could have additional fields added in future. \
2650                    Therefore, non-exhaustive enum variants cannot be constructed in external \
2651                    crates and cannot be matched against.",
2652                )?;
2653            } else {
2654                f.write_str(
2655                    "This type will require a wildcard arm in any match statements or constructors.",
2656                )?;
2657            }
2658
2659            f.write_str("</div></details>")?;
2660        }
2661        Ok(())
2662    })
2663}
2664
2665fn pluralize(count: usize) -> &'static str {
2666    if count > 1 { "s" } else { "" }
2667}