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, 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_attributes_in_pre,
24 render_impl, render_repr_attributes_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, print_abi_with_space, print_constness_with_space, print_where_clause,
35 visibility_print_with_space,
36};
37use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine};
38use crate::html::render::sidebar::filters;
39use crate::html::render::{document_full, document_item_info};
40use crate::html::url_parts_builder::UrlPartsBuilder;
41
42macro_rules! item_template {
61 (
62 $(#[$meta:meta])*
63 struct $name:ident<'a, 'cx> {
64 cx: &'a Context<'cx>,
65 it: &'a clean::Item,
66 $($field_name:ident: $field_ty:ty),*,
67 },
68 methods = [$($methods:tt),* $(,)?]
69 ) => {
70 #[derive(Template)]
71 $(#[$meta])*
72 struct $name<'a, 'cx> {
73 cx: &'a Context<'cx>,
74 it: &'a clean::Item,
75 $($field_name: $field_ty),*
76 }
77
78 impl<'a, 'cx: 'a> ItemTemplate<'a, 'cx> for $name<'a, 'cx> {
79 fn item_and_cx(&self) -> (&'a clean::Item, &'a Context<'cx>) {
80 (&self.it, &self.cx)
81 }
82 }
83
84 impl<'a, 'cx: 'a> $name<'a, 'cx> {
85 item_template_methods!($($methods)*);
86 }
87 };
88}
89
90macro_rules! item_template_methods {
94 () => {};
95 (document $($rest:tt)*) => {
96 fn document(&self) -> impl fmt::Display {
97 let (item, cx) = self.item_and_cx();
98 document(cx, item, None, HeadingOffset::H2)
99 }
100 item_template_methods!($($rest)*);
101 };
102 (document_type_layout $($rest:tt)*) => {
103 fn document_type_layout(&self) -> impl fmt::Display {
104 let (item, cx) = self.item_and_cx();
105 let def_id = item.item_id.expect_def_id();
106 document_type_layout(cx, def_id)
107 }
108 item_template_methods!($($rest)*);
109 };
110 (render_attributes_in_pre $($rest:tt)*) => {
111 fn render_attributes_in_pre(&self) -> impl fmt::Display {
112 let (item, cx) = self.item_and_cx();
113 render_attributes_in_pre(item, "", cx)
114 }
115 item_template_methods!($($rest)*);
116 };
117 (render_assoc_items $($rest:tt)*) => {
118 fn render_assoc_items(&self) -> impl fmt::Display {
119 let (item, cx) = self.item_and_cx();
120 let def_id = item.item_id.expect_def_id();
121 render_assoc_items(cx, item, def_id, AssocItemRender::All)
122 }
123 item_template_methods!($($rest)*);
124 };
125 ($method:ident $($rest:tt)*) => {
126 compile_error!(concat!("unknown method: ", stringify!($method)));
127 };
128 ($token:tt $($rest:tt)*) => {
129 compile_error!(concat!("unexpected token: ", stringify!($token)));
130 };
131}
132
133const ITEM_TABLE_OPEN: &str = "<dl class=\"item-table\">";
134const REEXPORTS_TABLE_OPEN: &str = "<dl class=\"item-table reexports\">";
135const ITEM_TABLE_CLOSE: &str = "</dl>";
136
137struct PathComponent {
139 path: String,
140 name: Symbol,
141}
142
143#[derive(Template)]
144#[template(path = "print_item.html")]
145struct ItemVars<'a> {
146 typ: &'a str,
147 name: &'a str,
148 item_type: &'a str,
149 path_components: Vec<PathComponent>,
150 stability_since_raw: &'a str,
151 src_href: Option<&'a str>,
152}
153
154pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item) -> impl fmt::Display {
155 debug_assert!(!item.is_stripped());
156
157 fmt::from_fn(|buf| {
158 let typ = match item.kind {
159 clean::ModuleItem(_) => {
160 if item.is_crate() {
161 "Crate "
162 } else {
163 "Module "
164 }
165 }
166 clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ",
167 clean::TraitItem(..) => "Trait ",
168 clean::StructItem(..) => "Struct ",
169 clean::UnionItem(..) => "Union ",
170 clean::EnumItem(..) => "Enum ",
171 clean::TypeAliasItem(..) => "Type Alias ",
172 clean::MacroItem(..) => "Macro ",
173 clean::ProcMacroItem(ref mac) => match mac.kind {
174 MacroKind::Bang => "Macro ",
175 MacroKind::Attr => "Attribute Macro ",
176 MacroKind::Derive => "Derive Macro ",
177 },
178 clean::PrimitiveItem(..) => "Primitive Type ",
179 clean::StaticItem(..) | clean::ForeignStaticItem(..) => "Static ",
180 clean::ConstantItem(..) => "Constant ",
181 clean::ForeignTypeItem => "Foreign Type ",
182 clean::KeywordItem => "Keyword ",
183 clean::TraitAliasItem(..) => "Trait Alias ",
184 _ => {
185 unreachable!();
187 }
188 };
189 let stability_since_raw =
190 render_stability_since_raw(item.stable_since(cx.tcx()), item.const_stability(cx.tcx()))
191 .maybe_display()
192 .to_string();
193
194 let src_href =
201 if cx.info.include_sources && !item.is_primitive() { cx.src_href(item) } else { None };
202
203 let path_components = if item.is_primitive() || item.is_keyword() {
204 vec![]
205 } else {
206 let cur = &cx.current;
207 let amt = if item.is_mod() { cur.len() - 1 } else { cur.len() };
208 cur.iter()
209 .enumerate()
210 .take(amt)
211 .map(|(i, component)| PathComponent {
212 path: "../".repeat(cur.len() - i - 1),
213 name: *component,
214 })
215 .collect()
216 };
217
218 let item_vars = ItemVars {
219 typ,
220 name: item.name.as_ref().unwrap().as_str(),
221 item_type: &item.type_().to_string(),
222 path_components,
223 stability_since_raw: &stability_since_raw,
224 src_href: src_href.as_deref(),
225 };
226
227 item_vars.render_into(buf).unwrap();
228
229 match &item.kind {
230 clean::ModuleItem(m) => {
231 write!(buf, "{}", item_module(cx, item, &m.items))
232 }
233 clean::FunctionItem(f) | clean::ForeignFunctionItem(f, _) => {
234 write!(buf, "{}", item_function(cx, item, f))
235 }
236 clean::TraitItem(t) => write!(buf, "{}", item_trait(cx, item, t)),
237 clean::StructItem(s) => {
238 write!(buf, "{}", item_struct(cx, item, s))
239 }
240 clean::UnionItem(s) => write!(buf, "{}", item_union(cx, item, s)),
241 clean::EnumItem(e) => write!(buf, "{}", item_enum(cx, item, e)),
242 clean::TypeAliasItem(t) => {
243 write!(buf, "{}", item_type_alias(cx, item, t))
244 }
245 clean::MacroItem(m) => write!(buf, "{}", item_macro(cx, item, m)),
246 clean::ProcMacroItem(m) => {
247 write!(buf, "{}", item_proc_macro(cx, item, m))
248 }
249 clean::PrimitiveItem(_) => write!(buf, "{}", item_primitive(cx, item)),
250 clean::StaticItem(i) => {
251 write!(buf, "{}", item_static(cx, item, i, None))
252 }
253 clean::ForeignStaticItem(i, safety) => {
254 write!(buf, "{}", item_static(cx, item, i, Some(*safety)))
255 }
256 clean::ConstantItem(ci) => {
257 write!(buf, "{}", item_constant(cx, item, &ci.generics, &ci.type_, &ci.kind))
258 }
259 clean::ForeignTypeItem => {
260 write!(buf, "{}", item_foreign_type(cx, item))
261 }
262 clean::KeywordItem => write!(buf, "{}", item_keyword(cx, item)),
263 clean::TraitAliasItem(ta) => {
264 write!(buf, "{}", item_trait_alias(cx, item, ta))
265 }
266 _ => {
267 unreachable!();
269 }
270 }?;
271
272 let mut types_with_notable_traits = cx.types_with_notable_traits.borrow_mut();
274 if !types_with_notable_traits.is_empty() {
275 write!(
276 buf,
277 r#"<script type="text/json" id="notable-traits-data">{}</script>"#,
278 notable_traits_json(types_with_notable_traits.iter(), cx),
279 )?;
280 types_with_notable_traits.clear();
281 }
282 Ok(())
283 })
284}
285
286fn should_hide_fields(n_fields: usize) -> bool {
288 n_fields > 12
289}
290
291fn toggle_open(mut w: impl fmt::Write, text: impl Display) {
292 write!(
293 w,
294 "<details class=\"toggle type-contents-toggle\">\
295 <summary class=\"hideme\">\
296 <span>Show {text}</span>\
297 </summary>",
298 )
299 .unwrap();
300}
301
302fn toggle_close(mut w: impl fmt::Write) {
303 w.write_str("</details>").unwrap();
304}
305
306trait ItemTemplate<'a, 'cx: 'a>: askama::Template + Display {
307 fn item_and_cx(&self) -> (&'a clean::Item, &'a Context<'cx>);
308}
309
310fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> impl fmt::Display {
311 fmt::from_fn(|w| {
312 write!(w, "{}", document(cx, item, None, HeadingOffset::H2))?;
313
314 let mut not_stripped_items =
315 items.iter().filter(|i| !i.is_stripped()).enumerate().collect::<Vec<_>>();
316
317 fn reorder(ty: ItemType) -> u8 {
319 match ty {
320 ItemType::ExternCrate => 0,
321 ItemType::Import => 1,
322 ItemType::Primitive => 2,
323 ItemType::Module => 3,
324 ItemType::Macro => 4,
325 ItemType::Struct => 5,
326 ItemType::Enum => 6,
327 ItemType::Constant => 7,
328 ItemType::Static => 8,
329 ItemType::Trait => 9,
330 ItemType::Function => 10,
331 ItemType::TypeAlias => 12,
332 ItemType::Union => 13,
333 _ => 14 + ty as u8,
334 }
335 }
336
337 fn cmp(i1: &clean::Item, i2: &clean::Item, tcx: TyCtxt<'_>) -> Ordering {
338 let rty1 = reorder(i1.type_());
339 let rty2 = reorder(i2.type_());
340 if rty1 != rty2 {
341 return rty1.cmp(&rty2);
342 }
343 let is_stable1 =
344 i1.stability(tcx).as_ref().map(|s| s.level.is_stable()).unwrap_or(true);
345 let is_stable2 =
346 i2.stability(tcx).as_ref().map(|s| s.level.is_stable()).unwrap_or(true);
347 if is_stable1 != is_stable2 {
348 return is_stable2.cmp(&is_stable1);
351 }
352 match (i1.name, i2.name) {
353 (Some(name1), Some(name2)) => compare_names(name1.as_str(), name2.as_str()),
354 (Some(_), None) => Ordering::Greater,
355 (None, Some(_)) => Ordering::Less,
356 (None, None) => Ordering::Equal,
357 }
358 }
359
360 let tcx = cx.tcx();
361
362 match cx.shared.module_sorting {
363 ModuleSorting::Alphabetical => {
364 not_stripped_items.sort_by(|(_, i1), (_, i2)| cmp(i1, i2, tcx));
365 }
366 ModuleSorting::DeclarationOrder => {}
367 }
368 not_stripped_items.dedup_by_key(|(idx, i)| {
388 (
389 i.item_id,
390 if i.name.is_some() { Some(full_path(cx, i)) } else { None },
391 i.type_(),
392 if i.is_import() { *idx } else { 0 },
393 )
394 });
395
396 debug!("{not_stripped_items:?}");
397 let mut last_section = None;
398
399 for (_, myitem) in ¬_stripped_items {
400 let my_section = item_ty_to_section(myitem.type_());
401 if Some(my_section) != last_section {
402 if last_section.is_some() {
403 w.write_str(ITEM_TABLE_CLOSE)?;
404 }
405 last_section = Some(my_section);
406 let section_id = my_section.id();
407 let tag =
408 if section_id == "reexports" { REEXPORTS_TABLE_OPEN } else { ITEM_TABLE_OPEN };
409 write!(
410 w,
411 "{}",
412 write_section_heading(my_section.name(), &cx.derive_id(section_id), None, tag)
413 )?;
414 }
415
416 match myitem.kind {
417 clean::ExternCrateItem { ref src } => {
418 use crate::html::format::print_anchor;
419
420 match *src {
421 Some(src) => {
422 write!(
423 w,
424 "<dt><code>{}extern crate {} as {};",
425 visibility_print_with_space(myitem, cx),
426 print_anchor(myitem.item_id.expect_def_id(), src, cx),
427 EscapeBodyTextWithWbr(myitem.name.unwrap().as_str())
428 )?;
429 }
430 None => {
431 write!(
432 w,
433 "<dt><code>{}extern crate {};",
434 visibility_print_with_space(myitem, cx),
435 print_anchor(
436 myitem.item_id.expect_def_id(),
437 myitem.name.unwrap(),
438 cx
439 )
440 )?;
441 }
442 }
443 w.write_str("</code></dt>")?;
444 }
445
446 clean::ImportItem(ref import) => {
447 let stab_tags = import.source.did.map_or_else(String::new, |import_def_id| {
448 print_extra_info_tags(tcx, myitem, item, Some(import_def_id)).to_string()
449 });
450
451 let id = match import.kind {
452 clean::ImportKind::Simple(s) => {
453 format!(" id=\"{}\"", cx.derive_id(format!("reexport.{s}")))
454 }
455 clean::ImportKind::Glob => String::new(),
456 };
457 write!(
458 w,
459 "<dt{id}>\
460 <code>{vis}{imp}</code>{stab_tags}\
461 </dt>",
462 vis = visibility_print_with_space(myitem, cx),
463 imp = import.print(cx)
464 )?;
465 }
466
467 _ => {
468 if myitem.name.is_none() {
469 continue;
470 }
471
472 let unsafety_flag = match myitem.kind {
473 clean::FunctionItem(_) | clean::ForeignFunctionItem(..)
474 if myitem.fn_header(tcx).unwrap().safety
475 == hir::HeaderSafety::Normal(hir::Safety::Unsafe) =>
476 {
477 "<sup title=\"unsafe function\">âš </sup>"
478 }
479 clean::ForeignStaticItem(_, hir::Safety::Unsafe) => {
480 "<sup title=\"unsafe static\">âš </sup>"
481 }
482 _ => "",
483 };
484
485 let visibility_and_hidden = match myitem.visibility(tcx) {
486 Some(ty::Visibility::Restricted(_)) => {
487 if myitem.is_doc_hidden() {
488 "<span title=\"Restricted Visibility\"> 🔒</span><span title=\"Hidden item\">👻</span> "
490 } else {
491 "<span title=\"Restricted Visibility\"> 🔒</span> "
492 }
493 }
494 _ if myitem.is_doc_hidden() => {
495 "<span title=\"Hidden item\"> 👻</span> "
496 }
497 _ => "",
498 };
499
500 let docs =
501 MarkdownSummaryLine(&myitem.doc_value(), &myitem.links(cx)).into_string();
502 let (docs_before, docs_after) =
503 if docs.is_empty() { ("", "") } else { ("<dd>", "</dd>") };
504 write!(
505 w,
506 "<dt>\
507 <a class=\"{class}\" href=\"{href}\" title=\"{title1} {title2}\">\
508 {name}\
509 </a>\
510 {visibility_and_hidden}\
511 {unsafety_flag}\
512 {stab_tags}\
513 </dt>\
514 {docs_before}{docs}{docs_after}",
515 name = EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()),
516 visibility_and_hidden = visibility_and_hidden,
517 stab_tags = print_extra_info_tags(tcx, myitem, item, None),
518 class = myitem.type_(),
519 unsafety_flag = unsafety_flag,
520 href = print_item_path(myitem.type_(), myitem.name.unwrap().as_str()),
521 title1 = myitem.type_(),
522 title2 = full_path(cx, myitem),
523 )?;
524 }
525 }
526 }
527
528 if last_section.is_some() {
529 w.write_str(ITEM_TABLE_CLOSE)?;
530 }
531 Ok(())
532 })
533}
534
535fn print_extra_info_tags(
538 tcx: TyCtxt<'_>,
539 item: &clean::Item,
540 parent: &clean::Item,
541 import_def_id: Option<DefId>,
542) -> impl Display {
543 fmt::from_fn(move |f| {
544 fn tag_html(class: &str, title: &str, contents: &str) -> impl Display {
545 fmt::from_fn(move |f| {
546 write!(
547 f,
548 r#"<wbr><span class="stab {class}" title="{title}">{contents}</span>"#,
549 title = Escape(title),
550 )
551 })
552 }
553
554 let deprecation = import_def_id
556 .map_or_else(|| item.deprecation(tcx), |import_did| tcx.lookup_deprecation(import_did));
557 if let Some(depr) = deprecation {
558 let message = if depr.is_in_effect() { "Deprecated" } else { "Deprecation planned" };
559 write!(f, "{}", tag_html("deprecated", "", message))?;
560 }
561
562 let stability = import_def_id
565 .map_or_else(|| item.stability(tcx), |import_did| tcx.lookup_stability(import_did));
566 if stability.is_some_and(|s| s.is_unstable() && s.feature != sym::rustc_private) {
567 write!(f, "{}", tag_html("unstable", "", "Experimental"))?;
568 }
569
570 let cfg = match (&item.cfg, parent.cfg.as_ref()) {
571 (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
572 (cfg, _) => cfg.as_deref().cloned(),
573 };
574
575 debug!(
576 "Portability name={name:?} {cfg:?} - {parent_cfg:?} = {cfg:?}",
577 name = item.name,
578 cfg = item.cfg,
579 parent_cfg = parent.cfg
580 );
581 if let Some(ref cfg) = cfg {
582 write!(
583 f,
584 "{}",
585 tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html())
586 )
587 } else {
588 Ok(())
589 }
590 })
591}
592
593fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> impl fmt::Display {
594 fmt::from_fn(|w| {
595 let tcx = cx.tcx();
596 let header = it.fn_header(tcx).expect("printing a function which isn't a function");
597 debug!(
598 "item_function/const: {:?} {:?} {:?} {:?}",
599 it.name,
600 &header.constness,
601 it.stable_since(tcx),
602 it.const_stability(tcx),
603 );
604 let constness = print_constness_with_space(
605 &header.constness,
606 it.stable_since(tcx),
607 it.const_stability(tcx),
608 );
609 let safety = header.safety.print_with_space();
610 let abi = print_abi_with_space(header.abi).to_string();
611 let asyncness = header.asyncness.print_with_space();
612 let visibility = visibility_print_with_space(it, cx).to_string();
613 let name = it.name.unwrap();
614
615 let generics_len = format!("{:#}", f.generics.print(cx)).len();
616 let header_len = "fn ".len()
617 + visibility.len()
618 + constness.len()
619 + asyncness.len()
620 + safety.len()
621 + abi.len()
622 + name.as_str().len()
623 + generics_len;
624
625 let notable_traits = notable_traits_button(&f.decl.output, cx).maybe_display();
626
627 wrap_item(w, |w| {
628 write!(
629 w,
630 "{attrs}{vis}{constness}{asyncness}{safety}{abi}fn \
631 {name}{generics}{decl}{notable_traits}{where_clause}",
632 attrs = render_attributes_in_pre(it, "", cx),
633 vis = visibility,
634 constness = constness,
635 asyncness = asyncness,
636 safety = safety,
637 abi = abi,
638 name = name,
639 generics = f.generics.print(cx),
640 where_clause =
641 print_where_clause(&f.generics, cx, 0, Ending::Newline).maybe_display(),
642 decl = f.decl.full_print(header_len, 0, cx),
643 )
644 })?;
645 write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
646 })
647}
648
649fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt::Display {
650 fmt::from_fn(|w| {
651 let tcx = cx.tcx();
652 let bounds = print_bounds(&t.bounds, false, cx);
653 let required_types =
654 t.items.iter().filter(|m| m.is_required_associated_type()).collect::<Vec<_>>();
655 let provided_types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>();
656 let required_consts =
657 t.items.iter().filter(|m| m.is_required_associated_const()).collect::<Vec<_>>();
658 let provided_consts =
659 t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>();
660 let required_methods = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>();
661 let provided_methods = t.items.iter().filter(|m| m.is_method()).collect::<Vec<_>>();
662 let count_types = required_types.len() + provided_types.len();
663 let count_consts = required_consts.len() + provided_consts.len();
664 let count_methods = required_methods.len() + provided_methods.len();
665 let must_implement_one_of_functions = &tcx.trait_def(t.def_id).must_implement_one_of;
666
667 wrap_item(w, |mut w| {
669 write!(
670 w,
671 "{attrs}{vis}{safety}{is_auto}trait {name}{generics}{bounds}",
672 attrs = render_attributes_in_pre(it, "", cx),
673 vis = visibility_print_with_space(it, cx),
674 safety = t.safety(tcx).print_with_space(),
675 is_auto = if t.is_auto(tcx) { "auto " } else { "" },
676 name = it.name.unwrap(),
677 generics = t.generics.print(cx),
678 )?;
679
680 if !t.generics.where_predicates.is_empty() {
681 write!(
682 w,
683 "{}",
684 print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display()
685 )?;
686 } else {
687 w.write_char(' ')?;
688 }
689
690 if t.items.is_empty() {
691 w.write_str("{ }")
692 } else {
693 w.write_str("{\n")?;
695 let mut toggle = false;
696
697 if should_hide_fields(count_types) {
699 toggle = true;
700 toggle_open(
701 &mut w,
702 format_args!(
703 "{} associated items",
704 count_types + count_consts + count_methods
705 ),
706 );
707 }
708 for types in [&required_types, &provided_types] {
709 for t in types {
710 writeln!(
711 w,
712 "{};",
713 render_assoc_item(
714 t,
715 AssocItemLink::Anchor(None),
716 ItemType::Trait,
717 cx,
718 RenderMode::Normal,
719 )
720 )?;
721 }
722 }
723 if !toggle && should_hide_fields(count_types + count_consts) {
728 toggle = true;
729 toggle_open(
730 &mut w,
731 format_args!(
732 "{count_consts} associated constant{plural_const} and \
733 {count_methods} method{plural_method}",
734 plural_const = pluralize(count_consts),
735 plural_method = pluralize(count_methods),
736 ),
737 );
738 }
739 if count_types != 0 && (count_consts != 0 || count_methods != 0) {
740 w.write_str("\n")?;
741 }
742 for consts in [&required_consts, &provided_consts] {
743 for c in consts {
744 writeln!(
745 w,
746 "{};",
747 render_assoc_item(
748 c,
749 AssocItemLink::Anchor(None),
750 ItemType::Trait,
751 cx,
752 RenderMode::Normal,
753 )
754 )?;
755 }
756 }
757 if !toggle && should_hide_fields(count_methods) {
758 toggle = true;
759 toggle_open(&mut w, format_args!("{count_methods} methods"));
760 }
761 if count_consts != 0 && count_methods != 0 {
762 w.write_str("\n")?;
763 }
764
765 if !required_methods.is_empty() {
766 writeln!(w, " // Required method{}", pluralize(required_methods.len()))?;
767 }
768 for (pos, m) in required_methods.iter().enumerate() {
769 writeln!(
770 w,
771 "{};",
772 render_assoc_item(
773 m,
774 AssocItemLink::Anchor(None),
775 ItemType::Trait,
776 cx,
777 RenderMode::Normal,
778 )
779 )?;
780
781 if pos < required_methods.len() - 1 {
782 w.write_str("<span class=\"item-spacer\"></span>")?;
783 }
784 }
785 if !required_methods.is_empty() && !provided_methods.is_empty() {
786 w.write_str("\n")?;
787 }
788
789 if !provided_methods.is_empty() {
790 writeln!(w, " // Provided method{}", pluralize(provided_methods.len()))?;
791 }
792 for (pos, m) in provided_methods.iter().enumerate() {
793 writeln!(
794 w,
795 "{} {{ ... }}",
796 render_assoc_item(
797 m,
798 AssocItemLink::Anchor(None),
799 ItemType::Trait,
800 cx,
801 RenderMode::Normal,
802 )
803 )?;
804
805 if pos < provided_methods.len() - 1 {
806 w.write_str("<span class=\"item-spacer\"></span>")?;
807 }
808 }
809 if toggle {
810 toggle_close(&mut w);
811 }
812 w.write_str("}")
813 }
814 })?;
815
816 write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
818
819 fn trait_item(cx: &Context<'_>, m: &clean::Item, t: &clean::Item) -> impl fmt::Display {
820 fmt::from_fn(|w| {
821 let name = m.name.unwrap();
822 info!("Documenting {name} on {ty_name:?}", ty_name = t.name);
823 let item_type = m.type_();
824 let id = cx.derive_id(format!("{item_type}.{name}"));
825
826 let content = document_full(m, cx, HeadingOffset::H5).to_string();
827
828 let toggled = !content.is_empty();
829 if toggled {
830 let method_toggle_class =
831 if item_type.is_method() { " method-toggle" } else { "" };
832 write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>")?;
833 }
834 write!(
835 w,
836 "<section id=\"{id}\" class=\"method\">\
837 {}\
838 <h4 class=\"code-header\">{}</h4></section>",
839 render_rightside(cx, m, RenderMode::Normal),
840 render_assoc_item(
841 m,
842 AssocItemLink::Anchor(Some(&id)),
843 ItemType::Impl,
844 cx,
845 RenderMode::Normal,
846 )
847 )?;
848 document_item_info(cx, m, Some(t)).render_into(w).unwrap();
849 if toggled {
850 write!(w, "</summary>{content}</details>")?;
851 }
852 Ok(())
853 })
854 }
855
856 if !required_consts.is_empty() {
857 write!(
858 w,
859 "{}",
860 write_section_heading(
861 "Required Associated Constants",
862 "required-associated-consts",
863 None,
864 "<div class=\"methods\">",
865 )
866 )?;
867 for t in required_consts {
868 write!(w, "{}", trait_item(cx, t, it))?;
869 }
870 w.write_str("</div>")?;
871 }
872 if !provided_consts.is_empty() {
873 write!(
874 w,
875 "{}",
876 write_section_heading(
877 "Provided Associated Constants",
878 "provided-associated-consts",
879 None,
880 "<div class=\"methods\">",
881 )
882 )?;
883 for t in provided_consts {
884 write!(w, "{}", trait_item(cx, t, it))?;
885 }
886 w.write_str("</div>")?;
887 }
888
889 if !required_types.is_empty() {
890 write!(
891 w,
892 "{}",
893 write_section_heading(
894 "Required Associated Types",
895 "required-associated-types",
896 None,
897 "<div class=\"methods\">",
898 )
899 )?;
900 for t in required_types {
901 write!(w, "{}", trait_item(cx, t, it))?;
902 }
903 w.write_str("</div>")?;
904 }
905 if !provided_types.is_empty() {
906 write!(
907 w,
908 "{}",
909 write_section_heading(
910 "Provided Associated Types",
911 "provided-associated-types",
912 None,
913 "<div class=\"methods\">",
914 )
915 )?;
916 for t in provided_types {
917 write!(w, "{}", trait_item(cx, t, it))?;
918 }
919 w.write_str("</div>")?;
920 }
921
922 if !required_methods.is_empty() || must_implement_one_of_functions.is_some() {
924 write!(
925 w,
926 "{}",
927 write_section_heading(
928 "Required Methods",
929 "required-methods",
930 None,
931 "<div class=\"methods\">",
932 )
933 )?;
934
935 if let Some(list) = must_implement_one_of_functions.as_deref() {
936 write!(
937 w,
938 "<div class=\"stab must_implement\">At least one of the `{}` methods is required.</div>",
939 fmt::from_fn(|f| list.iter().joined("`, `", f)),
940 )?;
941 }
942
943 for m in required_methods {
944 write!(w, "{}", trait_item(cx, m, it))?;
945 }
946 w.write_str("</div>")?;
947 }
948 if !provided_methods.is_empty() {
949 write!(
950 w,
951 "{}",
952 write_section_heading(
953 "Provided Methods",
954 "provided-methods",
955 None,
956 "<div class=\"methods\">",
957 )
958 )?;
959 for m in provided_methods {
960 write!(w, "{}", trait_item(cx, m, it))?;
961 }
962 w.write_str("</div>")?;
963 }
964
965 write!(
967 w,
968 "{}",
969 render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
970 )?;
971
972 let mut extern_crates = FxIndexSet::default();
973
974 if !t.is_dyn_compatible(cx.tcx()) {
975 write!(
976 w,
977 "{}",
978 write_section_heading(
979 "Dyn Compatibility",
980 "dyn-compatibility",
981 None,
982 format!(
983 "<div class=\"dyn-compatibility-info\"><p>This trait is <b>not</b> \
984 <a href=\"{base}/reference/items/traits.html#dyn-compatibility\">dyn compatible</a>.</p>\
985 <p><i>In older versions of Rust, dyn compatibility was called \"object safety\", \
986 so this trait is not object safe.</i></p></div>",
987 base = crate::clean::utils::DOC_RUST_LANG_ORG_VERSION
988 ),
989 ),
990 )?;
991 }
992
993 if let Some(implementors) = cx.shared.cache.implementors.get(&it.item_id.expect_def_id()) {
994 let mut implementor_dups: FxHashMap<Symbol, (DefId, bool)> = FxHashMap::default();
997 for implementor in implementors {
998 if let Some(did) =
999 implementor.inner_impl().for_.without_borrowed_ref().def_id(&cx.shared.cache)
1000 && !did.is_local()
1001 {
1002 extern_crates.insert(did.krate);
1003 }
1004 match implementor.inner_impl().for_.without_borrowed_ref() {
1005 clean::Type::Path { path } if !path.is_assoc_ty() => {
1006 let did = path.def_id();
1007 let &mut (prev_did, ref mut has_duplicates) =
1008 implementor_dups.entry(path.last()).or_insert((did, false));
1009 if prev_did != did {
1010 *has_duplicates = true;
1011 }
1012 }
1013 _ => {}
1014 }
1015 }
1016
1017 let (local, mut foreign) =
1018 implementors.iter().partition::<Vec<_>, _>(|i| i.is_on_local_type(cx));
1019
1020 let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) =
1021 local.iter().partition(|i| i.inner_impl().kind.is_auto());
1022
1023 synthetic.sort_by_cached_key(|i| ImplString::new(i, cx));
1024 concrete.sort_by_cached_key(|i| ImplString::new(i, cx));
1025 foreign.sort_by_cached_key(|i| ImplString::new(i, cx));
1026
1027 if !foreign.is_empty() {
1028 write!(
1029 w,
1030 "{}",
1031 write_section_heading(
1032 "Implementations on Foreign Types",
1033 "foreign-impls",
1034 None,
1035 ""
1036 )
1037 )?;
1038
1039 for implementor in foreign {
1040 let provided_methods = implementor.inner_impl().provided_trait_methods(tcx);
1041 let assoc_link =
1042 AssocItemLink::GotoSource(implementor.impl_item.item_id, &provided_methods);
1043 write!(
1044 w,
1045 "{}",
1046 render_impl(
1047 cx,
1048 implementor,
1049 it,
1050 assoc_link,
1051 RenderMode::Normal,
1052 None,
1053 &[],
1054 ImplRenderingParameters {
1055 show_def_docs: false,
1056 show_default_items: false,
1057 show_non_assoc_items: true,
1058 toggle_open_by_default: false,
1059 },
1060 )
1061 )?;
1062 }
1063 }
1064
1065 write!(
1066 w,
1067 "{}",
1068 write_section_heading(
1069 "Implementors",
1070 "implementors",
1071 None,
1072 "<div id=\"implementors-list\">",
1073 )
1074 )?;
1075 for implementor in concrete {
1076 write!(w, "{}", render_implementor(cx, implementor, it, &implementor_dups, &[]))?;
1077 }
1078 w.write_str("</div>")?;
1079
1080 if t.is_auto(tcx) {
1081 write!(
1082 w,
1083 "{}",
1084 write_section_heading(
1085 "Auto implementors",
1086 "synthetic-implementors",
1087 None,
1088 "<div id=\"synthetic-implementors-list\">",
1089 )
1090 )?;
1091 for implementor in synthetic {
1092 write!(
1093 w,
1094 "{}",
1095 render_implementor(
1096 cx,
1097 implementor,
1098 it,
1099 &implementor_dups,
1100 &collect_paths_for_type(
1101 &implementor.inner_impl().for_,
1102 &cx.shared.cache,
1103 ),
1104 )
1105 )?;
1106 }
1107 w.write_str("</div>")?;
1108 }
1109 } else {
1110 write!(
1113 w,
1114 "{}",
1115 write_section_heading(
1116 "Implementors",
1117 "implementors",
1118 None,
1119 "<div id=\"implementors-list\"></div>",
1120 )
1121 )?;
1122
1123 if t.is_auto(tcx) {
1124 write!(
1125 w,
1126 "{}",
1127 write_section_heading(
1128 "Auto implementors",
1129 "synthetic-implementors",
1130 None,
1131 "<div id=\"synthetic-implementors-list\"></div>",
1132 )
1133 )?;
1134 }
1135 }
1136
1137 let mut js_src_path: UrlPartsBuilder =
1209 iter::repeat_n("..", cx.current.len()).chain(iter::once("trait.impl")).collect();
1210 if let Some(did) = it.item_id.as_def_id()
1211 && let get_extern = { || cx.shared.cache.external_paths.get(&did).map(|s| &s.0) }
1212 && let Some(fqp) = cx.shared.cache.exact_paths.get(&did).or_else(get_extern)
1213 {
1214 js_src_path.extend(fqp[..fqp.len() - 1].iter().copied());
1215 js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), fqp.last().unwrap()));
1216 } else {
1217 js_src_path.extend(cx.current.iter().copied());
1218 js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), it.name.unwrap()));
1219 }
1220 let extern_crates = fmt::from_fn(|f| {
1221 if !extern_crates.is_empty() {
1222 f.write_str(" data-ignore-extern-crates=\"")?;
1223 extern_crates.iter().map(|&cnum| tcx.crate_name(cnum)).joined(",", f)?;
1224 f.write_str("\"")?;
1225 }
1226 Ok(())
1227 });
1228 write!(
1229 w,
1230 "<script src=\"{src}\"{extern_crates} async></script>",
1231 src = js_src_path.finish()
1232 )
1233 })
1234}
1235
1236fn item_trait_alias(
1237 cx: &Context<'_>,
1238 it: &clean::Item,
1239 t: &clean::TraitAlias,
1240) -> impl fmt::Display {
1241 fmt::from_fn(|w| {
1242 wrap_item(w, |w| {
1243 write!(
1244 w,
1245 "{attrs}trait {name}{generics} = {bounds}{where_clause};",
1246 attrs = render_attributes_in_pre(it, "", cx),
1247 name = it.name.unwrap(),
1248 generics = t.generics.print(cx),
1249 bounds = print_bounds(&t.bounds, true, cx),
1250 where_clause =
1251 print_where_clause(&t.generics, cx, 0, Ending::NoNewline).maybe_display(),
1252 )
1253 })?;
1254
1255 write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1256 write!(
1261 w,
1262 "{}",
1263 render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
1264 )
1265 })
1266}
1267
1268fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> impl fmt::Display {
1269 fmt::from_fn(|w| {
1270 wrap_item(w, |w| {
1271 write!(
1272 w,
1273 "{attrs}{vis}type {name}{generics}{where_clause} = {type_};",
1274 attrs = render_attributes_in_pre(it, "", cx),
1275 vis = visibility_print_with_space(it, cx),
1276 name = it.name.unwrap(),
1277 generics = t.generics.print(cx),
1278 where_clause =
1279 print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display(),
1280 type_ = t.type_.print(cx),
1281 )
1282 })?;
1283
1284 write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1285
1286 if let Some(inner_type) = &t.inner_type {
1287 write!(w, "{}", write_section_heading("Aliased Type", "aliased-type", None, ""),)?;
1288
1289 match inner_type {
1290 clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
1291 let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
1292 let enum_def_id = ty.ty_adt_def().unwrap().did();
1293
1294 DisplayEnum {
1295 variants,
1296 generics: &t.generics,
1297 is_non_exhaustive: *is_non_exhaustive,
1298 def_id: enum_def_id,
1299 }
1300 .render_into(cx, it, true, w)?;
1301 }
1302 clean::TypeAliasInnerType::Union { fields } => {
1303 let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
1304 let union_def_id = ty.ty_adt_def().unwrap().did();
1305
1306 ItemUnion {
1307 cx,
1308 it,
1309 fields,
1310 generics: &t.generics,
1311 is_type_alias: true,
1312 def_id: union_def_id,
1313 }
1314 .render_into(w)?;
1315 }
1316 clean::TypeAliasInnerType::Struct { ctor_kind, fields } => {
1317 let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
1318 let struct_def_id = ty.ty_adt_def().unwrap().did();
1319
1320 DisplayStruct {
1321 ctor_kind: *ctor_kind,
1322 generics: &t.generics,
1323 fields,
1324 def_id: struct_def_id,
1325 }
1326 .render_into(cx, it, true, w)?;
1327 }
1328 }
1329 } else {
1330 let def_id = it.item_id.expect_def_id();
1331 write!(
1336 w,
1337 "{}{}",
1338 render_assoc_items(cx, it, def_id, AssocItemRender::All),
1339 document_type_layout(cx, def_id)
1340 )?;
1341 }
1342
1343 let cache = &cx.shared.cache;
1416 if let Some(target_did) = t.type_.def_id(cache)
1417 && let get_extern = { || cache.external_paths.get(&target_did) }
1418 && let Some(&(ref target_fqp, target_type)) =
1419 cache.paths.get(&target_did).or_else(get_extern)
1420 && target_type.is_adt() && let Some(self_did) = it.item_id.as_def_id()
1422 && let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) }
1423 && let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local)
1424 {
1425 let mut js_src_path: UrlPartsBuilder =
1426 iter::repeat_n("..", cx.current.len()).chain(iter::once("type.impl")).collect();
1427 js_src_path.extend(target_fqp[..target_fqp.len() - 1].iter().copied());
1428 js_src_path.push_fmt(format_args!("{target_type}.{}.js", target_fqp.last().unwrap()));
1429 let self_path = join_path_syms(self_fqp);
1430 write!(
1431 w,
1432 "<script src=\"{src}\" data-self-path=\"{self_path}\" async></script>",
1433 src = js_src_path.finish(),
1434 )?;
1435 }
1436 Ok(())
1437 })
1438}
1439
1440item_template!(
1441 #[template(path = "item_union.html")]
1442 struct ItemUnion<'a, 'cx> {
1443 cx: &'a Context<'cx>,
1444 it: &'a clean::Item,
1445 fields: &'a [clean::Item],
1446 generics: &'a clean::Generics,
1447 is_type_alias: bool,
1448 def_id: DefId,
1449 },
1450 methods = [document, document_type_layout, render_assoc_items]
1451);
1452
1453impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
1454 fn render_union(&self) -> impl Display {
1455 render_union(self.it, Some(self.generics), self.fields, self.cx)
1456 }
1457
1458 fn document_field(&self, field: &'a clean::Item) -> impl Display {
1459 document(self.cx, field, Some(self.it), HeadingOffset::H3)
1460 }
1461
1462 fn stability_field(&self, field: &clean::Item) -> Option<String> {
1463 field.stability_class(self.cx.tcx())
1464 }
1465
1466 fn print_ty(&self, ty: &'a clean::Type) -> impl Display {
1467 ty.print(self.cx)
1468 }
1469
1470 fn fields_iter(&self) -> impl Iterator<Item = (&'a clean::Item, &'a clean::Type)> {
1477 self.fields.iter().filter_map(|f| match f.kind {
1478 clean::StructFieldItem(ref ty) => Some((f, ty)),
1479 _ => None,
1480 })
1481 }
1482
1483 fn render_attributes_in_pre(&self) -> impl fmt::Display {
1484 fmt::from_fn(move |f| {
1485 if self.is_type_alias {
1486 if let Some(repr) = clean::repr_attributes(
1488 self.cx.tcx(),
1489 self.cx.cache(),
1490 self.def_id,
1491 ItemType::Union,
1492 ) {
1493 writeln!(f, "{repr}")?;
1494 };
1495 } else {
1496 for a in self.it.attributes(self.cx.tcx(), self.cx.cache()) {
1497 writeln!(f, "{a}")?;
1498 }
1499 }
1500 Ok(())
1501 })
1502 }
1503}
1504
1505fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display {
1506 fmt::from_fn(|w| {
1507 ItemUnion {
1508 cx,
1509 it,
1510 fields: &s.fields,
1511 generics: &s.generics,
1512 is_type_alias: false,
1513 def_id: it.def_id().unwrap(),
1514 }
1515 .render_into(w)?;
1516 Ok(())
1517 })
1518}
1519
1520fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Display {
1521 fmt::from_fn(|f| {
1522 if !s.is_empty()
1523 && s.iter().all(|field| {
1524 matches!(field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
1525 })
1526 {
1527 return f.write_str("<span class=\"comment\">/* private fields */</span>");
1528 }
1529
1530 s.iter()
1531 .map(|ty| {
1532 fmt::from_fn(|f| match ty.kind {
1533 clean::StrippedItem(box clean::StructFieldItem(_)) => f.write_str("_"),
1534 clean::StructFieldItem(ref ty) => write!(f, "{}", ty.print(cx)),
1535 _ => unreachable!(),
1536 })
1537 })
1538 .joined(", ", f)
1539 })
1540}
1541
1542struct DisplayEnum<'clean> {
1543 variants: &'clean IndexVec<VariantIdx, clean::Item>,
1544 generics: &'clean clean::Generics,
1545 is_non_exhaustive: bool,
1546 def_id: DefId,
1547}
1548
1549impl<'clean> DisplayEnum<'clean> {
1550 fn render_into<W: fmt::Write>(
1551 self,
1552 cx: &Context<'_>,
1553 it: &clean::Item,
1554 is_type_alias: bool,
1555 w: &mut W,
1556 ) -> fmt::Result {
1557 let non_stripped_variant_count = self.variants.iter().filter(|i| !i.is_stripped()).count();
1558 let variants_len = self.variants.len();
1559 let has_stripped_entries = variants_len != non_stripped_variant_count;
1560
1561 wrap_item(w, |w| {
1562 if is_type_alias {
1563 render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Enum);
1565 } else {
1566 render_attributes_in_code(w, it, cx);
1567 }
1568 write!(
1569 w,
1570 "{}enum {}{}{}",
1571 visibility_print_with_space(it, cx),
1572 it.name.unwrap(),
1573 self.generics.print(cx),
1574 render_enum_fields(
1575 cx,
1576 Some(self.generics),
1577 self.variants,
1578 non_stripped_variant_count,
1579 has_stripped_entries,
1580 self.is_non_exhaustive,
1581 self.def_id,
1582 ),
1583 )
1584 })?;
1585
1586 let def_id = it.item_id.expect_def_id();
1587 let layout_def_id = if is_type_alias {
1588 self.def_id
1589 } else {
1590 write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1591 def_id
1594 };
1595
1596 if non_stripped_variant_count != 0 {
1597 write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?;
1598 }
1599 write!(
1600 w,
1601 "{}{}",
1602 render_assoc_items(cx, it, def_id, AssocItemRender::All),
1603 document_type_layout(cx, layout_def_id)
1604 )
1605 }
1606}
1607
1608fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display {
1609 fmt::from_fn(|w| {
1610 DisplayEnum {
1611 variants: &e.variants,
1612 generics: &e.generics,
1613 is_non_exhaustive: it.is_non_exhaustive(),
1614 def_id: it.def_id().unwrap(),
1615 }
1616 .render_into(cx, it, false, w)
1617 })
1618}
1619
1620fn should_show_enum_discriminant(
1624 cx: &Context<'_>,
1625 enum_def_id: DefId,
1626 variants: &IndexVec<VariantIdx, clean::Item>,
1627) -> bool {
1628 let mut has_variants_with_value = false;
1629 for variant in variants {
1630 if let clean::VariantItem(ref var) = variant.kind
1631 && matches!(var.kind, clean::VariantKind::CLike)
1632 {
1633 has_variants_with_value |= var.discriminant.is_some();
1634 } else {
1635 return false;
1636 }
1637 }
1638 if has_variants_with_value {
1639 return true;
1640 }
1641 let repr = cx.tcx().adt_def(enum_def_id).repr();
1642 repr.c() || repr.int.is_some()
1643}
1644
1645fn display_c_like_variant(
1646 cx: &Context<'_>,
1647 item: &clean::Item,
1648 variant: &clean::Variant,
1649 index: VariantIdx,
1650 should_show_enum_discriminant: bool,
1651 enum_def_id: DefId,
1652) -> impl fmt::Display {
1653 fmt::from_fn(move |w| {
1654 let name = item.name.unwrap();
1655 if let Some(ref value) = variant.discriminant {
1656 write!(w, "{} = {}", name.as_str(), value.value(cx.tcx(), true))?;
1657 } else if should_show_enum_discriminant {
1658 let adt_def = cx.tcx().adt_def(enum_def_id);
1659 let discr = adt_def.discriminant_for_variant(cx.tcx(), index);
1660 write!(w, "{} = {}", name.as_str(), discr)?;
1663 } else {
1664 write!(w, "{name}")?;
1665 }
1666 Ok(())
1667 })
1668}
1669
1670fn render_enum_fields(
1671 cx: &Context<'_>,
1672 g: Option<&clean::Generics>,
1673 variants: &IndexVec<VariantIdx, clean::Item>,
1674 count_variants: usize,
1675 has_stripped_entries: bool,
1676 is_non_exhaustive: bool,
1677 enum_def_id: DefId,
1678) -> impl fmt::Display {
1679 fmt::from_fn(move |w| {
1680 let should_show_enum_discriminant =
1681 should_show_enum_discriminant(cx, enum_def_id, variants);
1682 if let Some(generics) = g
1683 && let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline)
1684 {
1685 write!(w, "{where_clause}")?;
1686 } else {
1687 w.write_char(' ')?;
1689 }
1690
1691 let variants_stripped = has_stripped_entries;
1692 if count_variants == 0 && !variants_stripped {
1693 w.write_str("{}")
1694 } else {
1695 w.write_str("{\n")?;
1696 let toggle = should_hide_fields(count_variants);
1697 if toggle {
1698 toggle_open(&mut *w, format_args!("{count_variants} variants"));
1699 }
1700 const TAB: &str = " ";
1701 for (index, v) in variants.iter_enumerated() {
1702 if v.is_stripped() {
1703 continue;
1704 }
1705 write!(w, "{}", render_attributes_in_pre(v, TAB, cx))?;
1706 w.write_str(TAB)?;
1707 match v.kind {
1708 clean::VariantItem(ref var) => match var.kind {
1709 clean::VariantKind::CLike => {
1710 write!(
1711 w,
1712 "{}",
1713 display_c_like_variant(
1714 cx,
1715 v,
1716 var,
1717 index,
1718 should_show_enum_discriminant,
1719 enum_def_id,
1720 )
1721 )?;
1722 }
1723 clean::VariantKind::Tuple(ref s) => {
1724 write!(w, "{}({})", v.name.unwrap(), print_tuple_struct_fields(cx, s))?;
1725 }
1726 clean::VariantKind::Struct(ref s) => {
1727 write!(
1728 w,
1729 "{}",
1730 render_struct(v, None, None, &s.fields, TAB, false, cx)
1731 )?;
1732 }
1733 },
1734 _ => unreachable!(),
1735 }
1736 w.write_str(",\n")?;
1737 }
1738
1739 if variants_stripped && !is_non_exhaustive {
1740 w.write_str(" <span class=\"comment\">// some variants omitted</span>\n")?;
1741 }
1742 if toggle {
1743 toggle_close(&mut *w);
1744 }
1745 w.write_str("}")
1746 }
1747 })
1748}
1749
1750fn item_variants(
1751 cx: &Context<'_>,
1752 it: &clean::Item,
1753 variants: &IndexVec<VariantIdx, clean::Item>,
1754 enum_def_id: DefId,
1755) -> impl fmt::Display {
1756 fmt::from_fn(move |w| {
1757 let tcx = cx.tcx();
1758 write!(
1759 w,
1760 "{}",
1761 write_section_heading(
1762 &format!("Variants{}", document_non_exhaustive_header(it)),
1763 "variants",
1764 Some("variants"),
1765 format!("{}<div class=\"variants\">", document_non_exhaustive(it)),
1766 ),
1767 )?;
1768
1769 let should_show_enum_discriminant =
1770 should_show_enum_discriminant(cx, enum_def_id, variants);
1771 for (index, variant) in variants.iter_enumerated() {
1772 if variant.is_stripped() {
1773 continue;
1774 }
1775 let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
1776 write!(
1777 w,
1778 "<section id=\"{id}\" class=\"variant\">\
1779 <a href=\"#{id}\" class=\"anchor\">§</a>\
1780 {}\
1781 <h3 class=\"code-header\">",
1782 render_stability_since_raw_with_extra(
1783 variant.stable_since(tcx),
1784 variant.const_stability(tcx),
1785 " rightside",
1786 )
1787 .maybe_display()
1788 )?;
1789 if let clean::VariantItem(ref var) = variant.kind
1790 && let clean::VariantKind::CLike = var.kind
1791 {
1792 write!(
1793 w,
1794 "{}",
1795 display_c_like_variant(
1796 cx,
1797 variant,
1798 var,
1799 index,
1800 should_show_enum_discriminant,
1801 enum_def_id,
1802 )
1803 )?;
1804 } else {
1805 w.write_str(variant.name.unwrap().as_str())?;
1806 }
1807
1808 let clean::VariantItem(variant_data) = &variant.kind else { unreachable!() };
1809
1810 if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
1811 write!(w, "({})", print_tuple_struct_fields(cx, s))?;
1812 }
1813 w.write_str("</h3></section>")?;
1814
1815 write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4))?;
1816
1817 let heading_and_fields = match &variant_data.kind {
1818 clean::VariantKind::Struct(s) => {
1819 if s.fields.iter().any(|f| !f.is_doc_hidden()) {
1821 Some(("Fields", &s.fields))
1822 } else {
1823 None
1824 }
1825 }
1826 clean::VariantKind::Tuple(fields) => {
1827 if fields.iter().any(|f| !f.doc_value().is_empty()) {
1830 Some(("Tuple Fields", fields))
1831 } else {
1832 None
1833 }
1834 }
1835 clean::VariantKind::CLike => None,
1836 };
1837
1838 if let Some((heading, fields)) = heading_and_fields {
1839 let variant_id =
1840 cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
1841 write!(
1842 w,
1843 "<div class=\"sub-variant\" id=\"{variant_id}\">\
1844 <h4>{heading}</h4>\
1845 {}",
1846 document_non_exhaustive(variant)
1847 )?;
1848 for field in fields {
1849 match field.kind {
1850 clean::StrippedItem(box clean::StructFieldItem(_)) => {}
1851 clean::StructFieldItem(ref ty) => {
1852 let id = cx.derive_id(format!(
1853 "variant.{}.field.{}",
1854 variant.name.unwrap(),
1855 field.name.unwrap()
1856 ));
1857 write!(
1858 w,
1859 "<div class=\"sub-variant-field\">\
1860 <span id=\"{id}\" class=\"section-header\">\
1861 <a href=\"#{id}\" class=\"anchor field\">§</a>\
1862 <code>{f}: {t}</code>\
1863 </span>\
1864 {doc}\
1865 </div>",
1866 f = field.name.unwrap(),
1867 t = ty.print(cx),
1868 doc = document(cx, field, Some(variant), HeadingOffset::H5),
1869 )?;
1870 }
1871 _ => unreachable!(),
1872 }
1873 }
1874 w.write_str("</div>")?;
1875 }
1876 }
1877 w.write_str("</div>")
1878 })
1879}
1880
1881fn item_macro(cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) -> impl fmt::Display {
1882 fmt::from_fn(|w| {
1883 wrap_item(w, |w| {
1884 if !t.macro_rules {
1886 write!(w, "{}", visibility_print_with_space(it, cx))?;
1887 }
1888 write!(w, "{}", Escape(&t.source))
1889 })?;
1890 write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
1891 })
1892}
1893
1894fn item_proc_macro(cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) -> impl fmt::Display {
1895 fmt::from_fn(|w| {
1896 wrap_item(w, |w| {
1897 let name = it.name.expect("proc-macros always have names");
1898 match m.kind {
1899 MacroKind::Bang => {
1900 write!(w, "{name}!() {{ <span class=\"comment\">/* proc-macro */</span> }}")?;
1901 }
1902 MacroKind::Attr => {
1903 write!(w, "#[{name}]")?;
1904 }
1905 MacroKind::Derive => {
1906 write!(w, "#[derive({name})]")?;
1907 if !m.helpers.is_empty() {
1908 w.write_str(
1909 "\n{\n \
1910 <span class=\"comment\">// Attributes available to this derive:</span>\n",
1911 )?;
1912 for attr in &m.helpers {
1913 writeln!(w, " #[{attr}]")?;
1914 }
1915 w.write_str("}\n")?;
1916 }
1917 }
1918 }
1919 fmt::Result::Ok(())
1920 })?;
1921 write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
1922 })
1923}
1924
1925fn item_primitive(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
1926 fmt::from_fn(|w| {
1927 let def_id = it.item_id.expect_def_id();
1928 write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1929 if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
1930 write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All))?;
1931 } else {
1932 let (concrete, synthetic, blanket_impl) =
1935 get_filtered_impls_for_reference(&cx.shared, it);
1936
1937 render_all_impls(w, cx, it, &concrete, &synthetic, &blanket_impl);
1938 }
1939 Ok(())
1940 })
1941}
1942
1943fn item_constant(
1944 cx: &Context<'_>,
1945 it: &clean::Item,
1946 generics: &clean::Generics,
1947 ty: &clean::Type,
1948 c: &clean::ConstantKind,
1949) -> impl fmt::Display {
1950 fmt::from_fn(|w| {
1951 wrap_item(w, |w| {
1952 let tcx = cx.tcx();
1953 render_attributes_in_code(w, it, cx);
1954
1955 write!(
1956 w,
1957 "{vis}const {name}{generics}: {typ}{where_clause}",
1958 vis = visibility_print_with_space(it, cx),
1959 name = it.name.unwrap(),
1960 generics = generics.print(cx),
1961 typ = ty.print(cx),
1962 where_clause =
1963 print_where_clause(generics, cx, 0, Ending::NoNewline).maybe_display(),
1964 )?;
1965
1966 let value = c.value(tcx);
1976 let is_literal = c.is_literal(tcx);
1977 let expr = c.expr(tcx);
1978 if value.is_some() || is_literal {
1979 write!(w, " = {expr};", expr = Escape(&expr))?;
1980 } else {
1981 w.write_str(";")?;
1982 }
1983
1984 if !is_literal && let Some(value) = &value {
1985 let value_lowercase = value.to_lowercase();
1986 let expr_lowercase = expr.to_lowercase();
1987
1988 if value_lowercase != expr_lowercase
1989 && value_lowercase.trim_end_matches("i32") != expr_lowercase
1990 {
1991 write!(w, " // {value}", value = Escape(value))?;
1992 }
1993 }
1994 Ok::<(), fmt::Error>(())
1995 })?;
1996
1997 write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
1998 })
1999}
2000
2001struct DisplayStruct<'a> {
2002 ctor_kind: Option<CtorKind>,
2003 generics: &'a clean::Generics,
2004 fields: &'a [clean::Item],
2005 def_id: DefId,
2006}
2007
2008impl<'a> DisplayStruct<'a> {
2009 fn render_into<W: fmt::Write>(
2010 self,
2011 cx: &Context<'_>,
2012 it: &clean::Item,
2013 is_type_alias: bool,
2014 w: &mut W,
2015 ) -> fmt::Result {
2016 wrap_item(w, |w| {
2017 if is_type_alias {
2018 render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Struct);
2020 } else {
2021 render_attributes_in_code(w, it, cx);
2022 }
2023 write!(
2024 w,
2025 "{}",
2026 render_struct(it, Some(self.generics), self.ctor_kind, self.fields, "", true, cx)
2027 )
2028 })?;
2029
2030 if !is_type_alias {
2031 write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
2032 }
2033
2034 let def_id = it.item_id.expect_def_id();
2035 write!(
2036 w,
2037 "{}{}{}",
2038 item_fields(cx, it, self.fields, self.ctor_kind),
2039 render_assoc_items(cx, it, def_id, AssocItemRender::All),
2040 document_type_layout(cx, def_id),
2041 )
2042 }
2043}
2044
2045fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display {
2046 fmt::from_fn(|w| {
2047 DisplayStruct {
2048 ctor_kind: s.ctor_kind,
2049 generics: &s.generics,
2050 fields: s.fields.as_slice(),
2051 def_id: it.def_id().unwrap(),
2052 }
2053 .render_into(cx, it, false, w)
2054 })
2055}
2056
2057fn item_fields(
2058 cx: &Context<'_>,
2059 it: &clean::Item,
2060 fields: &[clean::Item],
2061 ctor_kind: Option<CtorKind>,
2062) -> impl fmt::Display {
2063 fmt::from_fn(move |w| {
2064 let mut fields = fields
2065 .iter()
2066 .filter_map(|f| match f.kind {
2067 clean::StructFieldItem(ref ty) => Some((f, ty)),
2068 _ => None,
2069 })
2070 .peekable();
2071 if let None | Some(CtorKind::Fn) = ctor_kind
2072 && fields.peek().is_some()
2073 {
2074 let title = format!(
2075 "{}{}",
2076 if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
2077 document_non_exhaustive_header(it),
2078 );
2079 write!(
2080 w,
2081 "{}",
2082 write_section_heading(
2083 &title,
2084 "fields",
2085 Some("fields"),
2086 document_non_exhaustive(it)
2087 )
2088 )?;
2089 for (index, (field, ty)) in fields.enumerate() {
2090 let field_name =
2091 field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string());
2092 let id = cx.derive_id(format!("{typ}.{field_name}", typ = ItemType::StructField));
2093 write!(
2094 w,
2095 "<span id=\"{id}\" class=\"{item_type} section-header\">\
2096 <a href=\"#{id}\" class=\"anchor field\">§</a>\
2097 <code>{field_name}: {ty}</code>\
2098 </span>\
2099 {doc}",
2100 item_type = ItemType::StructField,
2101 ty = ty.print(cx),
2102 doc = document(cx, field, Some(it), HeadingOffset::H3),
2103 )?;
2104 }
2105 }
2106 Ok(())
2107 })
2108}
2109
2110fn item_static(
2111 cx: &Context<'_>,
2112 it: &clean::Item,
2113 s: &clean::Static,
2114 safety: Option<hir::Safety>,
2115) -> impl fmt::Display {
2116 fmt::from_fn(move |w| {
2117 wrap_item(w, |w| {
2118 render_attributes_in_code(w, it, cx);
2119 write!(
2120 w,
2121 "{vis}{safe}static {mutability}{name}: {typ}",
2122 vis = visibility_print_with_space(it, cx),
2123 safe = safety.map(|safe| safe.prefix_str()).unwrap_or(""),
2124 mutability = s.mutability.print_with_space(),
2125 name = it.name.unwrap(),
2126 typ = s.type_.print(cx)
2127 )
2128 })?;
2129
2130 write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
2131 })
2132}
2133
2134fn item_foreign_type(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
2135 fmt::from_fn(|w| {
2136 wrap_item(w, |w| {
2137 w.write_str("extern {\n")?;
2138 render_attributes_in_code(w, it, cx);
2139 write!(w, " {}type {};\n}}", visibility_print_with_space(it, cx), it.name.unwrap(),)
2140 })?;
2141
2142 write!(
2143 w,
2144 "{}{}",
2145 document(cx, it, None, HeadingOffset::H2),
2146 render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
2147 )
2148 })
2149}
2150
2151fn item_keyword(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
2152 document(cx, it, None, HeadingOffset::H2)
2153}
2154
2155pub(crate) fn compare_names(left: &str, right: &str) -> Ordering {
2161 let mut left = left.chars().peekable();
2162 let mut right = right.chars().peekable();
2163
2164 loop {
2165 let (l, r) = match (left.next(), right.next()) {
2167 (None, None) => return Ordering::Equal,
2169 (None, Some(_)) => return Ordering::Less,
2171 (Some(_), None) => return Ordering::Greater,
2172 (Some(l), Some(r)) => (l, r),
2173 };
2174 let next_ordering = match (l.to_digit(10), r.to_digit(10)) {
2175 (None, None) => Ord::cmp(&l, &r),
2177 (None, Some(_)) => Ordering::Greater,
2180 (Some(_), None) => Ordering::Less,
2181 (Some(l), Some(r)) => {
2183 if l == 0 || r == 0 {
2184 let ordering = Ord::cmp(&l, &r);
2186 if ordering != Ordering::Equal {
2187 return ordering;
2188 }
2189 loop {
2190 let (l, r) = match (left.peek(), right.peek()) {
2192 (None, None) => return Ordering::Equal,
2194 (None, Some(_)) => return Ordering::Less,
2196 (Some(_), None) => return Ordering::Greater,
2197 (Some(l), Some(r)) => (l, r),
2198 };
2199 match (l.to_digit(10), r.to_digit(10)) {
2201 (None, None) => break Ordering::Equal,
2203 (None, Some(_)) => return Ordering::Less,
2205 (Some(_), None) => return Ordering::Greater,
2206 (Some(l), Some(r)) => {
2208 left.next();
2209 right.next();
2210 let ordering = Ord::cmp(&l, &r);
2211 if ordering != Ordering::Equal {
2212 return ordering;
2213 }
2214 }
2215 }
2216 }
2217 } else {
2218 let mut same_length_ordering = Ord::cmp(&l, &r);
2220 loop {
2221 let (l, r) = match (left.peek(), right.peek()) {
2223 (None, None) => return same_length_ordering,
2225 (None, Some(_)) => return Ordering::Less,
2227 (Some(_), None) => return Ordering::Greater,
2228 (Some(l), Some(r)) => (l, r),
2229 };
2230 match (l.to_digit(10), r.to_digit(10)) {
2232 (None, None) => break same_length_ordering,
2234 (None, Some(_)) => return Ordering::Less,
2236 (Some(_), None) => return Ordering::Greater,
2237 (Some(l), Some(r)) => {
2239 left.next();
2240 right.next();
2241 same_length_ordering = same_length_ordering.then(Ord::cmp(&l, &r));
2242 }
2243 }
2244 }
2245 }
2246 }
2247 };
2248 if next_ordering != Ordering::Equal {
2249 return next_ordering;
2250 }
2251 }
2252}
2253
2254pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
2255 let mut s = join_path_syms(&cx.current);
2256 s.push_str("::");
2257 s.push_str(item.name.unwrap().as_str());
2258 s
2259}
2260
2261pub(super) fn print_item_path(ty: ItemType, name: &str) -> impl Display {
2262 fmt::from_fn(move |f| match ty {
2263 ItemType::Module => write!(f, "{}index.html", ensure_trailing_slash(name)),
2264 _ => write!(f, "{ty}.{name}.html"),
2265 })
2266}
2267
2268fn print_bounds(
2269 bounds: &[clean::GenericBound],
2270 trait_alias: bool,
2271 cx: &Context<'_>,
2272) -> impl Display {
2273 (!bounds.is_empty())
2274 .then_some(fmt::from_fn(move |f| {
2275 let has_lots_of_bounds = bounds.len() > 2;
2276 let inter_str = if has_lots_of_bounds { "\n + " } else { " + " };
2277 if !trait_alias {
2278 if has_lots_of_bounds {
2279 f.write_str(":\n ")?;
2280 } else {
2281 f.write_str(": ")?;
2282 }
2283 }
2284
2285 bounds.iter().map(|p| p.print(cx)).joined(inter_str, f)
2286 }))
2287 .maybe_display()
2288}
2289
2290fn wrap_item<W, F, T>(w: &mut W, f: F) -> T
2291where
2292 W: fmt::Write,
2293 F: FnOnce(&mut W) -> T,
2294{
2295 write!(w, r#"<pre class="rust item-decl"><code>"#).unwrap();
2296 let res = f(w);
2297 write!(w, "</code></pre>").unwrap();
2298 res
2299}
2300
2301#[derive(PartialEq, Eq)]
2302struct ImplString(String);
2303
2304impl ImplString {
2305 fn new(i: &Impl, cx: &Context<'_>) -> ImplString {
2306 ImplString(format!("{}", i.inner_impl().print(false, cx)))
2307 }
2308}
2309
2310impl PartialOrd for ImplString {
2311 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2312 Some(Ord::cmp(self, other))
2313 }
2314}
2315
2316impl Ord for ImplString {
2317 fn cmp(&self, other: &Self) -> Ordering {
2318 compare_names(&self.0, &other.0)
2319 }
2320}
2321
2322fn render_implementor(
2323 cx: &Context<'_>,
2324 implementor: &Impl,
2325 trait_: &clean::Item,
2326 implementor_dups: &FxHashMap<Symbol, (DefId, bool)>,
2327 aliases: &[String],
2328) -> impl fmt::Display {
2329 let use_absolute = match implementor.inner_impl().for_ {
2332 clean::Type::Path { ref path, .. }
2333 | clean::BorrowedRef { type_: box clean::Type::Path { ref path, .. }, .. }
2334 if !path.is_assoc_ty() =>
2335 {
2336 implementor_dups[&path.last()].1
2337 }
2338 _ => false,
2339 };
2340 render_impl(
2341 cx,
2342 implementor,
2343 trait_,
2344 AssocItemLink::Anchor(None),
2345 RenderMode::Normal,
2346 Some(use_absolute),
2347 aliases,
2348 ImplRenderingParameters {
2349 show_def_docs: false,
2350 show_default_items: false,
2351 show_non_assoc_items: false,
2352 toggle_open_by_default: false,
2353 },
2354 )
2355}
2356
2357fn render_union(
2358 it: &clean::Item,
2359 g: Option<&clean::Generics>,
2360 fields: &[clean::Item],
2361 cx: &Context<'_>,
2362) -> impl Display {
2363 fmt::from_fn(move |mut f| {
2364 write!(f, "{}union {}", visibility_print_with_space(it, cx), it.name.unwrap(),)?;
2365
2366 let where_displayed = if let Some(generics) = g {
2367 write!(f, "{}", generics.print(cx))?;
2368 if let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline) {
2369 write!(f, "{where_clause}")?;
2370 true
2371 } else {
2372 false
2373 }
2374 } else {
2375 false
2376 };
2377
2378 if !where_displayed {
2380 f.write_str(" ")?;
2381 }
2382
2383 writeln!(f, "{{")?;
2384 let count_fields =
2385 fields.iter().filter(|field| matches!(field.kind, clean::StructFieldItem(..))).count();
2386 let toggle = should_hide_fields(count_fields);
2387 if toggle {
2388 toggle_open(&mut f, format_args!("{count_fields} fields"));
2389 }
2390
2391 for field in fields {
2392 if let clean::StructFieldItem(ref ty) = field.kind {
2393 writeln!(
2394 f,
2395 " {}{}: {},",
2396 visibility_print_with_space(field, cx),
2397 field.name.unwrap(),
2398 ty.print(cx)
2399 )?;
2400 }
2401 }
2402
2403 if it.has_stripped_entries().unwrap() {
2404 writeln!(f, " <span class=\"comment\">/* private fields */</span>")?;
2405 }
2406 if toggle {
2407 toggle_close(&mut f);
2408 }
2409 f.write_str("}").unwrap();
2410 Ok(())
2411 })
2412}
2413
2414fn render_struct(
2415 it: &clean::Item,
2416 g: Option<&clean::Generics>,
2417 ty: Option<CtorKind>,
2418 fields: &[clean::Item],
2419 tab: &str,
2420 structhead: bool,
2421 cx: &Context<'_>,
2422) -> impl fmt::Display {
2423 fmt::from_fn(move |w| {
2424 write!(
2425 w,
2426 "{}{}{}",
2427 visibility_print_with_space(it, cx),
2428 if structhead { "struct " } else { "" },
2429 it.name.unwrap()
2430 )?;
2431 if let Some(g) = g {
2432 write!(w, "{}", g.print(cx))?;
2433 }
2434 write!(
2435 w,
2436 "{}",
2437 render_struct_fields(
2438 g,
2439 ty,
2440 fields,
2441 tab,
2442 structhead,
2443 it.has_stripped_entries().unwrap_or(false),
2444 cx,
2445 )
2446 )
2447 })
2448}
2449
2450fn render_struct_fields(
2451 g: Option<&clean::Generics>,
2452 ty: Option<CtorKind>,
2453 fields: &[clean::Item],
2454 tab: &str,
2455 structhead: bool,
2456 has_stripped_entries: bool,
2457 cx: &Context<'_>,
2458) -> impl fmt::Display {
2459 fmt::from_fn(move |w| {
2460 match ty {
2461 None => {
2462 let where_displayed = if let Some(generics) = g
2463 && let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline)
2464 {
2465 write!(w, "{where_clause}")?;
2466 true
2467 } else {
2468 false
2469 };
2470
2471 if !where_displayed {
2473 w.write_str(" {")?;
2474 } else {
2475 w.write_str("{")?;
2476 }
2477 let count_fields =
2478 fields.iter().filter(|f| matches!(f.kind, clean::StructFieldItem(..))).count();
2479 let has_visible_fields = count_fields > 0;
2480 let toggle = should_hide_fields(count_fields);
2481 if toggle {
2482 toggle_open(&mut *w, format_args!("{count_fields} fields"));
2483 }
2484 for field in fields {
2485 if let clean::StructFieldItem(ref ty) = field.kind {
2486 write!(
2487 w,
2488 "\n{tab} {vis}{name}: {ty},",
2489 vis = visibility_print_with_space(field, cx),
2490 name = field.name.unwrap(),
2491 ty = ty.print(cx)
2492 )?;
2493 }
2494 }
2495
2496 if has_visible_fields {
2497 if has_stripped_entries {
2498 write!(
2499 w,
2500 "\n{tab} <span class=\"comment\">/* private fields */</span>"
2501 )?;
2502 }
2503 write!(w, "\n{tab}")?;
2504 } else if has_stripped_entries {
2505 write!(w, " <span class=\"comment\">/* private fields */</span> ")?;
2506 }
2507 if toggle {
2508 toggle_close(&mut *w);
2509 }
2510 w.write_str("}")?;
2511 }
2512 Some(CtorKind::Fn) => {
2513 w.write_str("(")?;
2514 if !fields.is_empty()
2515 && fields.iter().all(|field| {
2516 matches!(field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
2517 })
2518 {
2519 write!(w, "<span class=\"comment\">/* private fields */</span>")?;
2520 } else {
2521 for (i, field) in fields.iter().enumerate() {
2522 if i > 0 {
2523 w.write_str(", ")?;
2524 }
2525 match field.kind {
2526 clean::StrippedItem(box clean::StructFieldItem(..)) => {
2527 write!(w, "_")?;
2528 }
2529 clean::StructFieldItem(ref ty) => {
2530 write!(
2531 w,
2532 "{}{}",
2533 visibility_print_with_space(field, cx),
2534 ty.print(cx)
2535 )?;
2536 }
2537 _ => unreachable!(),
2538 }
2539 }
2540 }
2541 w.write_str(")")?;
2542 if let Some(g) = g {
2543 write!(
2544 w,
2545 "{}",
2546 print_where_clause(g, cx, 0, Ending::NoNewline).maybe_display()
2547 )?;
2548 }
2549 if structhead {
2551 w.write_str(";")?;
2552 }
2553 }
2554 Some(CtorKind::Const) => {
2555 if let Some(g) = g {
2557 write!(
2558 w,
2559 "{}",
2560 print_where_clause(g, cx, 0, Ending::NoNewline).maybe_display()
2561 )?;
2562 }
2563 w.write_str(";")?;
2564 }
2565 }
2566 Ok(())
2567 })
2568}
2569
2570fn document_non_exhaustive_header(item: &clean::Item) -> &str {
2571 if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
2572}
2573
2574fn document_non_exhaustive(item: &clean::Item) -> impl Display {
2575 fmt::from_fn(|f| {
2576 if item.is_non_exhaustive() {
2577 write!(
2578 f,
2579 "<details class=\"toggle non-exhaustive\">\
2580 <summary class=\"hideme\"><span>{}</span></summary>\
2581 <div class=\"docblock\">",
2582 {
2583 if item.is_struct() {
2584 "This struct is marked as non-exhaustive"
2585 } else if item.is_enum() {
2586 "This enum is marked as non-exhaustive"
2587 } else if item.is_variant() {
2588 "This variant is marked as non-exhaustive"
2589 } else {
2590 "This type is marked as non-exhaustive"
2591 }
2592 }
2593 )?;
2594
2595 if item.is_struct() {
2596 f.write_str(
2597 "Non-exhaustive structs could have additional fields added in future. \
2598 Therefore, non-exhaustive structs cannot be constructed in external crates \
2599 using the traditional <code>Struct { .. }</code> syntax; cannot be \
2600 matched against without a wildcard <code>..</code>; and \
2601 struct update syntax will not work.",
2602 )?;
2603 } else if item.is_enum() {
2604 f.write_str(
2605 "Non-exhaustive enums could have additional variants added in future. \
2606 Therefore, when matching against variants of non-exhaustive enums, an \
2607 extra wildcard arm must be added to account for any future variants.",
2608 )?;
2609 } else if item.is_variant() {
2610 f.write_str(
2611 "Non-exhaustive enum variants could have additional fields added in future. \
2612 Therefore, non-exhaustive enum variants cannot be constructed in external \
2613 crates and cannot be matched against.",
2614 )?;
2615 } else {
2616 f.write_str(
2617 "This type will require a wildcard arm in any match statements or constructors.",
2618 )?;
2619 }
2620
2621 f.write_str("</div></details>")?;
2622 }
2623 Ok(())
2624 })
2625}
2626
2627fn pluralize(count: usize) -> &'static str {
2628 if count > 1 { "s" } else { "" }
2629}