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
43macro_rules! item_template {
62 (
63 $(#[$meta:meta])*
64 struct $name:ident<'a, 'cx> {
65 cx: &'a Context<'cx>,
66 it: &'a clean::Item,
67 $($field_name:ident: $field_ty:ty),*,
68 },
69 methods = [$($methods:tt),* $(,)?]
70 ) => {
71 #[derive(Template)]
72 $(#[$meta])*
73 struct $name<'a, 'cx> {
74 cx: &'a Context<'cx>,
75 it: &'a clean::Item,
76 $($field_name: $field_ty),*
77 }
78
79 impl<'a, 'cx: 'a> ItemTemplate<'a, 'cx> for $name<'a, 'cx> {
80 fn item_and_cx(&self) -> (&'a clean::Item, &'a Context<'cx>) {
81 (&self.it, &self.cx)
82 }
83 }
84
85 impl<'a, 'cx: 'a> $name<'a, 'cx> {
86 item_template_methods!($($methods)*);
87 }
88 };
89}
90
91macro_rules! item_template_methods {
95 () => {};
96 (document $($rest:tt)*) => {
97 fn document(&self) -> impl fmt::Display {
98 let (item, cx) = self.item_and_cx();
99 document(cx, item, None, HeadingOffset::H2)
100 }
101 item_template_methods!($($rest)*);
102 };
103 (document_type_layout $($rest:tt)*) => {
104 fn document_type_layout(&self) -> impl fmt::Display {
105 let (item, cx) = self.item_and_cx();
106 let def_id = item.item_id.expect_def_id();
107 document_type_layout(cx, def_id)
108 }
109 item_template_methods!($($rest)*);
110 };
111 (render_assoc_items $($rest:tt)*) => {
112 fn render_assoc_items(&self) -> impl fmt::Display {
113 let (item, cx) = self.item_and_cx();
114 let def_id = item.item_id.expect_def_id();
115 render_assoc_items(cx, item, def_id, AssocItemRender::All)
116 }
117 item_template_methods!($($rest)*);
118 };
119 ($method:ident $($rest:tt)*) => {
120 compile_error!(concat!("unknown method: ", stringify!($method)));
121 };
122 ($token:tt $($rest:tt)*) => {
123 compile_error!(concat!("unexpected token: ", stringify!($token)));
124 };
125}
126
127const ITEM_TABLE_OPEN: &str = "<dl class=\"item-table\">";
128const REEXPORTS_TABLE_OPEN: &str = "<dl class=\"item-table reexports\">";
129const ITEM_TABLE_CLOSE: &str = "</dl>";
130
131struct PathComponent {
133 path: String,
134 name: Symbol,
135}
136
137#[derive(Template)]
138#[template(path = "print_item.html")]
139struct ItemVars<'a> {
140 typ: &'a str,
141 name: &'a str,
142 item_type: &'a str,
143 path_components: Vec<PathComponent>,
144 stability_since_raw: &'a str,
145 src_href: Option<&'a str>,
146}
147
148pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item) -> impl fmt::Display {
149 debug_assert!(!item.is_stripped());
150
151 fmt::from_fn(|buf| {
152 let typ = match item.kind {
153 clean::ModuleItem(_) => {
154 if item.is_crate() {
155 "Crate "
156 } else {
157 "Module "
158 }
159 }
160 clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ",
161 clean::TraitItem(..) => "Trait ",
162 clean::StructItem(..) => "Struct ",
163 clean::UnionItem(..) => "Union ",
164 clean::EnumItem(..) => "Enum ",
165 clean::TypeAliasItem(..) => "Type Alias ",
166 clean::MacroItem(..) => "Macro ",
167 clean::ProcMacroItem(ref mac) => match mac.kind {
168 MacroKind::Bang => "Macro ",
169 MacroKind::Attr => "Attribute Macro ",
170 MacroKind::Derive => "Derive Macro ",
171 },
172 clean::PrimitiveItem(..) => "Primitive Type ",
173 clean::StaticItem(..) | clean::ForeignStaticItem(..) => "Static ",
174 clean::ConstantItem(..) => "Constant ",
175 clean::ForeignTypeItem => "Foreign Type ",
176 clean::KeywordItem => "Keyword ",
177 clean::AttributeItem => "Attribute ",
178 clean::TraitAliasItem(..) => "Trait Alias ",
179 _ => {
180 unreachable!();
182 }
183 };
184 let stability_since_raw =
185 render_stability_since_raw(item.stable_since(cx.tcx()), item.const_stability(cx.tcx()))
186 .maybe_display()
187 .to_string();
188
189 let src_href =
196 if cx.info.include_sources && !item.is_primitive() { cx.src_href(item) } else { None };
197
198 let path_components = if item.is_fake_item() {
199 vec![]
200 } else {
201 let cur = &cx.current;
202 let amt = if item.is_mod() { cur.len() - 1 } else { cur.len() };
203 cur.iter()
204 .enumerate()
205 .take(amt)
206 .map(|(i, component)| PathComponent {
207 path: "../".repeat(cur.len() - i - 1),
208 name: *component,
209 })
210 .collect()
211 };
212
213 let item_vars = ItemVars {
214 typ,
215 name: item.name.as_ref().unwrap().as_str(),
216 item_type: &item.type_().to_string(),
217 path_components,
218 stability_since_raw: &stability_since_raw,
219 src_href: src_href.as_deref(),
220 };
221
222 item_vars.render_into(buf).unwrap();
223
224 match &item.kind {
225 clean::ModuleItem(m) => {
226 write!(buf, "{}", item_module(cx, item, &m.items))
227 }
228 clean::FunctionItem(f) | clean::ForeignFunctionItem(f, _) => {
229 write!(buf, "{}", item_function(cx, item, f))
230 }
231 clean::TraitItem(t) => write!(buf, "{}", item_trait(cx, item, t)),
232 clean::StructItem(s) => {
233 write!(buf, "{}", item_struct(cx, item, s))
234 }
235 clean::UnionItem(s) => write!(buf, "{}", item_union(cx, item, s)),
236 clean::EnumItem(e) => write!(buf, "{}", item_enum(cx, item, e)),
237 clean::TypeAliasItem(t) => {
238 write!(buf, "{}", item_type_alias(cx, item, t))
239 }
240 clean::MacroItem(m) => write!(buf, "{}", item_macro(cx, item, m)),
241 clean::ProcMacroItem(m) => {
242 write!(buf, "{}", item_proc_macro(cx, item, m))
243 }
244 clean::PrimitiveItem(_) => write!(buf, "{}", item_primitive(cx, item)),
245 clean::StaticItem(i) => {
246 write!(buf, "{}", item_static(cx, item, i, None))
247 }
248 clean::ForeignStaticItem(i, safety) => {
249 write!(buf, "{}", item_static(cx, item, i, Some(*safety)))
250 }
251 clean::ConstantItem(ci) => {
252 write!(buf, "{}", item_constant(cx, item, &ci.generics, &ci.type_, &ci.kind))
253 }
254 clean::ForeignTypeItem => {
255 write!(buf, "{}", item_foreign_type(cx, item))
256 }
257 clean::KeywordItem | clean::AttributeItem => {
258 write!(buf, "{}", item_keyword_or_attribute(cx, item))
259 }
260 clean::TraitAliasItem(ta) => {
261 write!(buf, "{}", item_trait_alias(cx, item, ta))
262 }
263 _ => {
264 unreachable!();
266 }
267 }?;
268
269 let mut types_with_notable_traits = cx.types_with_notable_traits.borrow_mut();
271 if !types_with_notable_traits.is_empty() {
272 write!(
273 buf,
274 r#"<script type="text/json" id="notable-traits-data">{}</script>"#,
275 notable_traits_json(types_with_notable_traits.iter(), cx),
276 )?;
277 types_with_notable_traits.clear();
278 }
279 Ok(())
280 })
281}
282
283fn should_hide_fields(n_fields: usize) -> bool {
285 n_fields > 12
286}
287
288fn toggle_open(mut w: impl fmt::Write, text: impl Display) {
289 write!(
290 w,
291 "<details class=\"toggle type-contents-toggle\">\
292 <summary class=\"hideme\">\
293 <span>Show {text}</span>\
294 </summary>",
295 )
296 .unwrap();
297}
298
299fn toggle_close(mut w: impl fmt::Write) {
300 w.write_str("</details>").unwrap();
301}
302
303trait ItemTemplate<'a, 'cx: 'a>: askama::Template + Display {
304 fn item_and_cx(&self) -> (&'a clean::Item, &'a Context<'cx>);
305}
306
307fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> impl fmt::Display {
308 fmt::from_fn(|w| {
309 write!(w, "{}", document(cx, item, None, HeadingOffset::H2))?;
310
311 let mut not_stripped_items: FxIndexMap<ItemType, Vec<(usize, &clean::Item)>> =
312 FxIndexMap::default();
313
314 for (index, item) in items.iter().filter(|i| !i.is_stripped()).enumerate() {
315 not_stripped_items.entry(item.type_()).or_default().push((index, item));
316 }
317
318 fn reorder(ty: ItemType) -> u8 {
320 match ty {
321 ItemType::ExternCrate => 0,
322 ItemType::Import => 1,
323 ItemType::Primitive => 2,
324 ItemType::Module => 3,
325 ItemType::Macro => 4,
326 ItemType::Struct => 5,
327 ItemType::Enum => 6,
328 ItemType::Constant => 7,
329 ItemType::Static => 8,
330 ItemType::Trait => 9,
331 ItemType::Function => 10,
332 ItemType::TypeAlias => 12,
333 ItemType::Union => 13,
334 _ => 14 + ty as u8,
335 }
336 }
337
338 fn cmp(i1: &clean::Item, i2: &clean::Item, tcx: TyCtxt<'_>) -> Ordering {
339 let is_stable1 =
340 i1.stability(tcx).as_ref().map(|s| s.level.is_stable()).unwrap_or(true);
341 let is_stable2 =
342 i2.stability(tcx).as_ref().map(|s| s.level.is_stable()).unwrap_or(true);
343 if is_stable1 != is_stable2 {
344 return is_stable2.cmp(&is_stable1);
347 }
348 match (i1.name, i2.name) {
349 (Some(name1), Some(name2)) => compare_names(name1.as_str(), name2.as_str()),
350 (Some(_), None) => Ordering::Greater,
351 (None, Some(_)) => Ordering::Less,
352 (None, None) => Ordering::Equal,
353 }
354 }
355
356 let tcx = cx.tcx();
357
358 match cx.shared.module_sorting {
359 ModuleSorting::Alphabetical => {
360 for items in not_stripped_items.values_mut() {
361 items.sort_by(|(_, i1), (_, i2)| cmp(i1, i2, tcx));
362 }
363 }
364 ModuleSorting::DeclarationOrder => {}
365 }
366 for items in not_stripped_items.values_mut() {
386 items.dedup_by_key(|(idx, i)| {
387 (
388 i.item_id,
389 if i.name.is_some() { Some(full_path(cx, i)) } else { None },
390 i.type_(),
391 if i.is_import() { *idx } else { 0 },
392 )
393 });
394 }
395
396 debug!("{not_stripped_items:?}");
397
398 let mut types = not_stripped_items.keys().copied().collect::<Vec<_>>();
399 types.sort_unstable_by(|a, b| reorder(*a).cmp(&reorder(*b)));
400
401 for type_ in types {
402 let my_section = item_ty_to_section(type_);
403 let tag = if my_section == super::ItemSection::Reexports {
404 REEXPORTS_TABLE_OPEN
405 } else {
406 ITEM_TABLE_OPEN
407 };
408 write!(
409 w,
410 "{}",
411 write_section_heading(my_section.name(), &cx.derive_id(my_section.id()), None, tag)
412 )?;
413
414 for (_, myitem) in ¬_stripped_items[&type_] {
415 match myitem.kind {
416 clean::ExternCrateItem { ref src } => {
417 use crate::html::format::print_anchor;
418
419 match *src {
420 Some(src) => {
421 write!(
422 w,
423 "<dt><code>{}extern crate {} as {};",
424 visibility_print_with_space(myitem, cx),
425 print_anchor(myitem.item_id.expect_def_id(), src, cx),
426 EscapeBodyTextWithWbr(myitem.name.unwrap().as_str())
427 )?;
428 }
429 None => {
430 write!(
431 w,
432 "<dt><code>{}extern crate {};",
433 visibility_print_with_space(myitem, cx),
434 print_anchor(
435 myitem.item_id.expect_def_id(),
436 myitem.name.unwrap(),
437 cx
438 )
439 )?;
440 }
441 }
442 }
443 clean::ImportItem(ref import) => {
444 let stab_tags =
445 import.source.did.map_or_else(String::new, |import_def_id| {
446 print_extra_info_tags(tcx, myitem, item, Some(import_def_id))
447 .to_string()
448 });
449 let id = match import.kind {
450 clean::ImportKind::Simple(s) => {
451 format!(" id=\"{}\"", cx.derive_id(format!("reexport.{s}")))
452 }
453 clean::ImportKind::Glob => String::new(),
454 };
455 write!(
456 w,
457 "<dt{id}>\
458 <code>"
459 )?;
460 render_attributes_in_code(w, myitem, "", cx);
461 write!(
462 w,
463 "{vis}{imp}</code>{stab_tags}\
464 </dt>",
465 vis = visibility_print_with_space(myitem, cx),
466 imp = print_import(import, cx),
467 )?;
468 }
469 _ => {
470 if myitem.name.is_none() {
471 continue;
472 }
473
474 let unsafety_flag = match myitem.kind {
475 clean::FunctionItem(_) | clean::ForeignFunctionItem(..)
476 if myitem.fn_header(tcx).unwrap().safety
477 == hir::HeaderSafety::Normal(hir::Safety::Unsafe) =>
478 {
479 "<sup title=\"unsafe function\">âš </sup>"
480 }
481 clean::ForeignStaticItem(_, hir::Safety::Unsafe) => {
482 "<sup title=\"unsafe static\">âš </sup>"
483 }
484 _ => "",
485 };
486 let visibility_and_hidden = match myitem.visibility(tcx) {
487 Some(ty::Visibility::Restricted(_)) => {
488 if myitem.is_doc_hidden() {
489 "<span title=\"Restricted Visibility\"> 🔒</span><span title=\"Hidden item\">👻</span> "
491 } else {
492 "<span title=\"Restricted Visibility\"> 🔒</span> "
493 }
494 }
495 _ if myitem.is_doc_hidden() => {
496 "<span title=\"Hidden item\"> 👻</span> "
497 }
498 _ => "",
499 };
500
501 let docs = MarkdownSummaryLine(&myitem.doc_value(), &myitem.links(cx))
502 .into_string();
503 let (docs_before, docs_after) =
504 if docs.is_empty() { ("", "") } else { ("<dd>", "</dd>") };
505 write!(
506 w,
507 "<dt>\
508 <a class=\"{class}\" href=\"{href}\" title=\"{title1} {title2}\">\
509 {name}\
510 </a>\
511 {visibility_and_hidden}\
512 {unsafety_flag}\
513 {stab_tags}\
514 </dt>\
515 {docs_before}{docs}{docs_after}",
516 name = EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()),
517 visibility_and_hidden = visibility_and_hidden,
518 stab_tags = print_extra_info_tags(tcx, myitem, item, None),
519 class = type_,
520 unsafety_flag = unsafety_flag,
521 href = print_item_path(type_, myitem.name.unwrap().as_str()),
522 title1 = myitem.type_(),
523 title2 = full_path(cx, myitem),
524 )?;
525 }
526 }
527 }
528 w.write_str(ITEM_TABLE_CLOSE)?;
529 }
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!("{:#}", print_generics(&f.generics, 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 render_attributes_in_code(w, it, "", cx);
629 write!(
630 w,
631 "{vis}{constness}{asyncness}{safety}{abi}fn \
632 {name}{generics}{decl}{notable_traits}{where_clause}",
633 vis = visibility,
634 constness = constness,
635 asyncness = asyncness,
636 safety = safety,
637 abi = abi,
638 name = name,
639 generics = print_generics(&f.generics, cx),
640 where_clause =
641 print_where_clause(&f.generics, cx, 0, Ending::Newline).maybe_display(),
642 decl = full_print_fn_decl(&f.decl, 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 render_attributes_in_code(&mut w, it, "", cx);
670 write!(
671 w,
672 "{vis}{safety}{is_auto}trait {name}{generics}{bounds}",
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 = print_generics(&t.generics, 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 render_attributes_in_code(w, it, "", cx);
1244 write!(
1245 w,
1246 "trait {name}{generics} = {bounds}{where_clause};",
1247 name = it.name.unwrap(),
1248 generics = print_generics(&t.generics, 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 render_attributes_in_code(w, it, "", cx);
1272 write!(
1273 w,
1274 "{vis}type {name}{generics}{where_clause} = {type_};",
1275 vis = visibility_print_with_space(it, cx),
1276 name = it.name.unwrap(),
1277 generics = print_generics(&t.generics, cx),
1278 where_clause =
1279 print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display(),
1280 type_ = print_type(&t.type_, 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(
1456 self.it,
1457 Some(self.generics),
1458 self.fields,
1459 self.def_id,
1460 self.is_type_alias,
1461 self.cx,
1462 )
1463 }
1464
1465 fn print_field_attrs(&self, field: &'a clean::Item) -> impl Display {
1466 fmt::from_fn(move |w| {
1467 render_attributes_in_code(w, field, "", self.cx);
1468 Ok(())
1469 })
1470 }
1471
1472 fn document_field(&self, field: &'a clean::Item) -> impl Display {
1473 document(self.cx, field, Some(self.it), HeadingOffset::H3)
1474 }
1475
1476 fn stability_field(&self, field: &clean::Item) -> Option<String> {
1477 field.stability_class(self.cx.tcx())
1478 }
1479
1480 fn print_ty(&self, ty: &'a clean::Type) -> impl Display {
1481 print_type(ty, self.cx)
1482 }
1483
1484 fn fields_iter(&self) -> impl Iterator<Item = (&'a clean::Item, &'a clean::Type)> {
1491 self.fields.iter().filter_map(|f| match f.kind {
1492 clean::StructFieldItem(ref ty) => Some((f, ty)),
1493 _ => None,
1494 })
1495 }
1496}
1497
1498fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display {
1499 fmt::from_fn(|w| {
1500 ItemUnion {
1501 cx,
1502 it,
1503 fields: &s.fields,
1504 generics: &s.generics,
1505 is_type_alias: false,
1506 def_id: it.def_id().unwrap(),
1507 }
1508 .render_into(w)?;
1509 Ok(())
1510 })
1511}
1512
1513fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Display {
1514 fmt::from_fn(|f| {
1515 if !s.is_empty()
1516 && s.iter().all(|field| {
1517 matches!(field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
1518 })
1519 {
1520 return f.write_str("<span class=\"comment\">/* private fields */</span>");
1521 }
1522
1523 s.iter()
1524 .map(|ty| {
1525 fmt::from_fn(|f| match ty.kind {
1526 clean::StrippedItem(box clean::StructFieldItem(_)) => f.write_str("_"),
1527 clean::StructFieldItem(ref ty) => write!(f, "{}", print_type(ty, cx)),
1528 _ => unreachable!(),
1529 })
1530 })
1531 .joined(", ", f)
1532 })
1533}
1534
1535struct DisplayEnum<'clean> {
1536 variants: &'clean IndexVec<VariantIdx, clean::Item>,
1537 generics: &'clean clean::Generics,
1538 is_non_exhaustive: bool,
1539 def_id: DefId,
1540}
1541
1542impl<'clean> DisplayEnum<'clean> {
1543 fn render_into<W: fmt::Write>(
1544 self,
1545 cx: &Context<'_>,
1546 it: &clean::Item,
1547 is_type_alias: bool,
1548 w: &mut W,
1549 ) -> fmt::Result {
1550 let non_stripped_variant_count = self.variants.iter().filter(|i| !i.is_stripped()).count();
1551 let variants_len = self.variants.len();
1552 let has_stripped_entries = variants_len != non_stripped_variant_count;
1553
1554 wrap_item(w, |w| {
1555 if is_type_alias {
1556 render_repr_attribute_in_code(w, cx, self.def_id);
1558 } else {
1559 render_attributes_in_code(w, it, "", cx);
1560 }
1561 write!(
1562 w,
1563 "{}enum {}{}{}",
1564 visibility_print_with_space(it, cx),
1565 it.name.unwrap(),
1566 print_generics(&self.generics, cx),
1567 render_enum_fields(
1568 cx,
1569 Some(self.generics),
1570 self.variants,
1571 non_stripped_variant_count,
1572 has_stripped_entries,
1573 self.is_non_exhaustive,
1574 self.def_id,
1575 ),
1576 )
1577 })?;
1578
1579 let def_id = it.item_id.expect_def_id();
1580 let layout_def_id = if is_type_alias {
1581 self.def_id
1582 } else {
1583 write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1584 def_id
1587 };
1588
1589 if non_stripped_variant_count != 0 {
1590 write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?;
1591 }
1592 write!(
1593 w,
1594 "{}{}",
1595 render_assoc_items(cx, it, def_id, AssocItemRender::All),
1596 document_type_layout(cx, layout_def_id)
1597 )
1598 }
1599}
1600
1601fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display {
1602 fmt::from_fn(|w| {
1603 DisplayEnum {
1604 variants: &e.variants,
1605 generics: &e.generics,
1606 is_non_exhaustive: it.is_non_exhaustive(),
1607 def_id: it.def_id().unwrap(),
1608 }
1609 .render_into(cx, it, false, w)
1610 })
1611}
1612
1613fn should_show_enum_discriminant(
1617 cx: &Context<'_>,
1618 enum_def_id: DefId,
1619 variants: &IndexVec<VariantIdx, clean::Item>,
1620) -> bool {
1621 let mut has_variants_with_value = false;
1622 for variant in variants {
1623 if let clean::VariantItem(ref var) = variant.kind
1624 && matches!(var.kind, clean::VariantKind::CLike)
1625 {
1626 has_variants_with_value |= var.discriminant.is_some();
1627 } else {
1628 return false;
1629 }
1630 }
1631 if has_variants_with_value {
1632 return true;
1633 }
1634 let repr = cx.tcx().adt_def(enum_def_id).repr();
1635 repr.c() || repr.int.is_some()
1636}
1637
1638fn display_c_like_variant(
1639 cx: &Context<'_>,
1640 item: &clean::Item,
1641 variant: &clean::Variant,
1642 index: VariantIdx,
1643 should_show_enum_discriminant: bool,
1644 enum_def_id: DefId,
1645) -> impl fmt::Display {
1646 fmt::from_fn(move |w| {
1647 let name = item.name.unwrap();
1648 if let Some(ref value) = variant.discriminant {
1649 write!(w, "{} = {}", name.as_str(), value.value(cx.tcx(), true))?;
1650 } else if should_show_enum_discriminant {
1651 let adt_def = cx.tcx().adt_def(enum_def_id);
1652 let discr = adt_def.discriminant_for_variant(cx.tcx(), index);
1653 write!(w, "{} = {}", name.as_str(), discr)?;
1656 } else {
1657 write!(w, "{name}")?;
1658 }
1659 Ok(())
1660 })
1661}
1662
1663fn render_enum_fields(
1664 cx: &Context<'_>,
1665 g: Option<&clean::Generics>,
1666 variants: &IndexVec<VariantIdx, clean::Item>,
1667 count_variants: usize,
1668 has_stripped_entries: bool,
1669 is_non_exhaustive: bool,
1670 enum_def_id: DefId,
1671) -> impl fmt::Display {
1672 fmt::from_fn(move |w| {
1673 let should_show_enum_discriminant =
1674 should_show_enum_discriminant(cx, enum_def_id, variants);
1675 if let Some(generics) = g
1676 && let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline)
1677 {
1678 write!(w, "{where_clause}")?;
1679 } else {
1680 w.write_char(' ')?;
1682 }
1683
1684 let variants_stripped = has_stripped_entries;
1685 if count_variants == 0 && !variants_stripped {
1686 w.write_str("{}")
1687 } else {
1688 w.write_str("{\n")?;
1689 let toggle = should_hide_fields(count_variants);
1690 if toggle {
1691 toggle_open(&mut *w, format_args!("{count_variants} variants"));
1692 }
1693 const TAB: &str = " ";
1694 for (index, v) in variants.iter_enumerated() {
1695 if v.is_stripped() {
1696 continue;
1697 }
1698 render_attributes_in_code(w, v, TAB, cx);
1699 w.write_str(TAB)?;
1700 match v.kind {
1701 clean::VariantItem(ref var) => match var.kind {
1702 clean::VariantKind::CLike => {
1703 write!(
1704 w,
1705 "{}",
1706 display_c_like_variant(
1707 cx,
1708 v,
1709 var,
1710 index,
1711 should_show_enum_discriminant,
1712 enum_def_id,
1713 )
1714 )?;
1715 }
1716 clean::VariantKind::Tuple(ref s) => {
1717 write!(w, "{}({})", v.name.unwrap(), print_tuple_struct_fields(cx, s))?;
1718 }
1719 clean::VariantKind::Struct(ref s) => {
1720 write!(
1721 w,
1722 "{}",
1723 render_struct(v, None, None, &s.fields, TAB, false, cx)
1724 )?;
1725 }
1726 },
1727 _ => unreachable!(),
1728 }
1729 w.write_str(",\n")?;
1730 }
1731
1732 if variants_stripped && !is_non_exhaustive {
1733 w.write_str(" <span class=\"comment\">// some variants omitted</span>\n")?;
1734 }
1735 if toggle {
1736 toggle_close(&mut *w);
1737 }
1738 w.write_str("}")
1739 }
1740 })
1741}
1742
1743fn item_variants(
1744 cx: &Context<'_>,
1745 it: &clean::Item,
1746 variants: &IndexVec<VariantIdx, clean::Item>,
1747 enum_def_id: DefId,
1748) -> impl fmt::Display {
1749 fmt::from_fn(move |w| {
1750 let tcx = cx.tcx();
1751 write!(
1752 w,
1753 "{}",
1754 write_section_heading(
1755 &format!("Variants{}", document_non_exhaustive_header(it)),
1756 "variants",
1757 Some("variants"),
1758 format!("{}<div class=\"variants\">", document_non_exhaustive(it)),
1759 ),
1760 )?;
1761
1762 let should_show_enum_discriminant =
1763 should_show_enum_discriminant(cx, enum_def_id, variants);
1764 for (index, variant) in variants.iter_enumerated() {
1765 if variant.is_stripped() {
1766 continue;
1767 }
1768 let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
1769 write!(
1770 w,
1771 "<section id=\"{id}\" class=\"variant\">\
1772 <a href=\"#{id}\" class=\"anchor\">§</a>\
1773 {}\
1774 <h3 class=\"code-header\">",
1775 render_stability_since_raw_with_extra(
1776 variant.stable_since(tcx),
1777 variant.const_stability(tcx),
1778 " rightside",
1779 )
1780 .maybe_display()
1781 )?;
1782 render_attributes_in_code(w, variant, "", cx);
1783 if let clean::VariantItem(ref var) = variant.kind
1784 && let clean::VariantKind::CLike = var.kind
1785 {
1786 write!(
1787 w,
1788 "{}",
1789 display_c_like_variant(
1790 cx,
1791 variant,
1792 var,
1793 index,
1794 should_show_enum_discriminant,
1795 enum_def_id,
1796 )
1797 )?;
1798 } else {
1799 w.write_str(variant.name.unwrap().as_str())?;
1800 }
1801
1802 let clean::VariantItem(variant_data) = &variant.kind else { unreachable!() };
1803
1804 if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
1805 write!(w, "({})", print_tuple_struct_fields(cx, s))?;
1806 }
1807 w.write_str("</h3></section>")?;
1808
1809 write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4))?;
1810
1811 let heading_and_fields = match &variant_data.kind {
1812 clean::VariantKind::Struct(s) => {
1813 if s.fields.iter().any(|f| !f.is_doc_hidden()) {
1815 Some(("Fields", &s.fields))
1816 } else {
1817 None
1818 }
1819 }
1820 clean::VariantKind::Tuple(fields) => {
1821 if fields.iter().any(|f| !f.doc_value().is_empty()) {
1824 Some(("Tuple Fields", fields))
1825 } else {
1826 None
1827 }
1828 }
1829 clean::VariantKind::CLike => None,
1830 };
1831
1832 if let Some((heading, fields)) = heading_and_fields {
1833 let variant_id =
1834 cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
1835 write!(
1836 w,
1837 "<div class=\"sub-variant\" id=\"{variant_id}\">\
1838 <h4>{heading}</h4>\
1839 {}",
1840 document_non_exhaustive(variant)
1841 )?;
1842 for field in fields {
1843 match field.kind {
1844 clean::StrippedItem(box clean::StructFieldItem(_)) => {}
1845 clean::StructFieldItem(ref ty) => {
1846 let id = cx.derive_id(format!(
1847 "variant.{}.field.{}",
1848 variant.name.unwrap(),
1849 field.name.unwrap()
1850 ));
1851 write!(
1852 w,
1853 "<div class=\"sub-variant-field\">\
1854 <span id=\"{id}\" class=\"section-header\">\
1855 <a href=\"#{id}\" class=\"anchor field\">§</a>\
1856 <code>"
1857 )?;
1858 render_attributes_in_code(w, field, "", cx);
1859 write!(
1860 w,
1861 "{f}: {t}</code>\
1862 </span>\
1863 {doc}\
1864 </div>",
1865 f = field.name.unwrap(),
1866 t = print_type(ty, cx),
1867 doc = document(cx, field, Some(variant), HeadingOffset::H5),
1868 )?;
1869 }
1870 _ => unreachable!(),
1871 }
1872 }
1873 w.write_str("</div>")?;
1874 }
1875 }
1876 w.write_str("</div>")
1877 })
1878}
1879
1880fn item_macro(cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) -> impl fmt::Display {
1881 fmt::from_fn(|w| {
1882 wrap_item(w, |w| {
1883 render_attributes_in_code(w, it, "", cx);
1885 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 = print_generics(generics, cx),
1961 typ = print_type(ty, 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_attribute_in_code(w, cx, self.def_id);
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>",
2098 item_type = ItemType::StructField,
2099 )?;
2100 render_attributes_in_code(w, field, "", cx);
2101 write!(
2102 w,
2103 "{field_name}: {ty}</code>\
2104 </span>\
2105 {doc}",
2106 ty = print_type(ty, cx),
2107 doc = document(cx, field, Some(it), HeadingOffset::H3),
2108 )?;
2109 }
2110 }
2111 Ok(())
2112 })
2113}
2114
2115fn item_static(
2116 cx: &Context<'_>,
2117 it: &clean::Item,
2118 s: &clean::Static,
2119 safety: Option<hir::Safety>,
2120) -> impl fmt::Display {
2121 fmt::from_fn(move |w| {
2122 wrap_item(w, |w| {
2123 render_attributes_in_code(w, it, "", cx);
2124 write!(
2125 w,
2126 "{vis}{safe}static {mutability}{name}: {typ}",
2127 vis = visibility_print_with_space(it, cx),
2128 safe = safety.map(|safe| safe.prefix_str()).unwrap_or(""),
2129 mutability = s.mutability.print_with_space(),
2130 name = it.name.unwrap(),
2131 typ = print_type(&s.type_, cx)
2132 )
2133 })?;
2134
2135 write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
2136 })
2137}
2138
2139fn item_foreign_type(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
2140 fmt::from_fn(|w| {
2141 wrap_item(w, |w| {
2142 w.write_str("extern {\n")?;
2143 render_attributes_in_code(w, it, "", cx);
2144 write!(w, " {}type {};\n}}", visibility_print_with_space(it, cx), it.name.unwrap(),)
2145 })?;
2146
2147 write!(
2148 w,
2149 "{}{}",
2150 document(cx, it, None, HeadingOffset::H2),
2151 render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
2152 )
2153 })
2154}
2155
2156fn item_keyword_or_attribute(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
2157 document(cx, it, None, HeadingOffset::H2)
2158}
2159
2160pub(crate) fn compare_names(left: &str, right: &str) -> Ordering {
2166 let mut left = left.chars().peekable();
2167 let mut right = right.chars().peekable();
2168
2169 loop {
2170 let (l, r) = match (left.next(), right.next()) {
2172 (None, None) => return Ordering::Equal,
2174 (None, Some(_)) => return Ordering::Less,
2176 (Some(_), None) => return Ordering::Greater,
2177 (Some(l), Some(r)) => (l, r),
2178 };
2179 let next_ordering = match (l.to_digit(10), r.to_digit(10)) {
2180 (None, None) => Ord::cmp(&l, &r),
2182 (None, Some(_)) => Ordering::Greater,
2185 (Some(_), None) => Ordering::Less,
2186 (Some(l), Some(r)) => {
2188 if l == 0 || r == 0 {
2189 let ordering = Ord::cmp(&l, &r);
2191 if ordering != Ordering::Equal {
2192 return ordering;
2193 }
2194 loop {
2195 let (l, r) = match (left.peek(), right.peek()) {
2197 (None, None) => return Ordering::Equal,
2199 (None, Some(_)) => return Ordering::Less,
2201 (Some(_), None) => return Ordering::Greater,
2202 (Some(l), Some(r)) => (l, r),
2203 };
2204 match (l.to_digit(10), r.to_digit(10)) {
2206 (None, None) => break Ordering::Equal,
2208 (None, Some(_)) => return Ordering::Less,
2210 (Some(_), None) => return Ordering::Greater,
2211 (Some(l), Some(r)) => {
2213 left.next();
2214 right.next();
2215 let ordering = Ord::cmp(&l, &r);
2216 if ordering != Ordering::Equal {
2217 return ordering;
2218 }
2219 }
2220 }
2221 }
2222 } else {
2223 let mut same_length_ordering = Ord::cmp(&l, &r);
2225 loop {
2226 let (l, r) = match (left.peek(), right.peek()) {
2228 (None, None) => return same_length_ordering,
2230 (None, Some(_)) => return Ordering::Less,
2232 (Some(_), None) => return Ordering::Greater,
2233 (Some(l), Some(r)) => (l, r),
2234 };
2235 match (l.to_digit(10), r.to_digit(10)) {
2237 (None, None) => break same_length_ordering,
2239 (None, Some(_)) => return Ordering::Less,
2241 (Some(_), None) => return Ordering::Greater,
2242 (Some(l), Some(r)) => {
2244 left.next();
2245 right.next();
2246 same_length_ordering = same_length_ordering.then(Ord::cmp(&l, &r));
2247 }
2248 }
2249 }
2250 }
2251 }
2252 };
2253 if next_ordering != Ordering::Equal {
2254 return next_ordering;
2255 }
2256 }
2257}
2258
2259pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
2260 let mut s = join_path_syms(&cx.current);
2261 s.push_str("::");
2262 s.push_str(item.name.unwrap().as_str());
2263 s
2264}
2265
2266pub(super) fn print_item_path(ty: ItemType, name: &str) -> impl Display {
2267 fmt::from_fn(move |f| match ty {
2268 ItemType::Module => write!(f, "{}index.html", ensure_trailing_slash(name)),
2269 _ => write!(f, "{ty}.{name}.html"),
2270 })
2271}
2272
2273fn print_bounds(
2274 bounds: &[clean::GenericBound],
2275 trait_alias: bool,
2276 cx: &Context<'_>,
2277) -> impl Display {
2278 (!bounds.is_empty())
2279 .then_some(fmt::from_fn(move |f| {
2280 let has_lots_of_bounds = bounds.len() > 2;
2281 let inter_str = if has_lots_of_bounds { "\n + " } else { " + " };
2282 if !trait_alias {
2283 if has_lots_of_bounds {
2284 f.write_str(":\n ")?;
2285 } else {
2286 f.write_str(": ")?;
2287 }
2288 }
2289
2290 bounds.iter().map(|p| print_generic_bound(p, cx)).joined(inter_str, f)
2291 }))
2292 .maybe_display()
2293}
2294
2295fn wrap_item<W, F, T>(w: &mut W, f: F) -> T
2296where
2297 W: fmt::Write,
2298 F: FnOnce(&mut W) -> T,
2299{
2300 write!(w, r#"<pre class="rust item-decl"><code>"#).unwrap();
2301 let res = f(w);
2302 write!(w, "</code></pre>").unwrap();
2303 res
2304}
2305
2306#[derive(PartialEq, Eq)]
2307struct ImplString(String);
2308
2309impl ImplString {
2310 fn new(i: &Impl, cx: &Context<'_>) -> ImplString {
2311 ImplString(format!("{}", print_impl(i.inner_impl(), false, cx)))
2312 }
2313}
2314
2315impl PartialOrd for ImplString {
2316 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2317 Some(Ord::cmp(self, other))
2318 }
2319}
2320
2321impl Ord for ImplString {
2322 fn cmp(&self, other: &Self) -> Ordering {
2323 compare_names(&self.0, &other.0)
2324 }
2325}
2326
2327fn render_implementor(
2328 cx: &Context<'_>,
2329 implementor: &Impl,
2330 trait_: &clean::Item,
2331 implementor_dups: &FxHashMap<Symbol, (DefId, bool)>,
2332 aliases: &[String],
2333) -> impl fmt::Display {
2334 let use_absolute = match implementor.inner_impl().for_ {
2337 clean::Type::Path { ref path, .. }
2338 | clean::BorrowedRef { type_: box clean::Type::Path { ref path, .. }, .. }
2339 if !path.is_assoc_ty() =>
2340 {
2341 implementor_dups[&path.last()].1
2342 }
2343 _ => false,
2344 };
2345 render_impl(
2346 cx,
2347 implementor,
2348 trait_,
2349 AssocItemLink::Anchor(None),
2350 RenderMode::Normal,
2351 Some(use_absolute),
2352 aliases,
2353 ImplRenderingParameters {
2354 show_def_docs: false,
2355 show_default_items: false,
2356 show_non_assoc_items: false,
2357 toggle_open_by_default: false,
2358 },
2359 )
2360}
2361
2362fn render_union(
2363 it: &clean::Item,
2364 g: Option<&clean::Generics>,
2365 fields: &[clean::Item],
2366 def_id: DefId,
2367 is_type_alias: bool,
2368 cx: &Context<'_>,
2369) -> impl Display {
2370 fmt::from_fn(move |mut f| {
2371 if is_type_alias {
2372 render_repr_attribute_in_code(f, cx, def_id);
2374 } else {
2375 render_attributes_in_code(f, it, "", cx);
2376 }
2377 write!(f, "{}union {}", visibility_print_with_space(it, cx), it.name.unwrap(),)?;
2378
2379 let where_displayed = if let Some(generics) = g {
2380 write!(f, "{}", print_generics(generics, cx))?;
2381 if let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline) {
2382 write!(f, "{where_clause}")?;
2383 true
2384 } else {
2385 false
2386 }
2387 } else {
2388 false
2389 };
2390
2391 if !where_displayed {
2393 f.write_str(" ")?;
2394 }
2395
2396 writeln!(f, "{{")?;
2397 let count_fields =
2398 fields.iter().filter(|field| matches!(field.kind, clean::StructFieldItem(..))).count();
2399 let toggle = should_hide_fields(count_fields);
2400 if toggle {
2401 toggle_open(&mut f, format_args!("{count_fields} fields"));
2402 }
2403
2404 for field in fields {
2405 if let clean::StructFieldItem(ref ty) = field.kind {
2406 render_attributes_in_code(&mut f, field, " ", cx);
2407 writeln!(
2408 f,
2409 " {}{}: {},",
2410 visibility_print_with_space(field, cx),
2411 field.name.unwrap(),
2412 print_type(ty, cx)
2413 )?;
2414 }
2415 }
2416
2417 if it.has_stripped_entries().unwrap() {
2418 writeln!(f, " <span class=\"comment\">/* private fields */</span>")?;
2419 }
2420 if toggle {
2421 toggle_close(&mut f);
2422 }
2423 f.write_str("}").unwrap();
2424 Ok(())
2425 })
2426}
2427
2428fn render_struct(
2429 it: &clean::Item,
2430 g: Option<&clean::Generics>,
2431 ty: Option<CtorKind>,
2432 fields: &[clean::Item],
2433 tab: &str,
2434 structhead: bool,
2435 cx: &Context<'_>,
2436) -> impl fmt::Display {
2437 fmt::from_fn(move |w| {
2438 write!(
2439 w,
2440 "{}{}{}",
2441 visibility_print_with_space(it, cx),
2442 if structhead { "struct " } else { "" },
2443 it.name.unwrap()
2444 )?;
2445 if let Some(g) = g {
2446 write!(w, "{}", print_generics(g, cx))?;
2447 }
2448 write!(
2449 w,
2450 "{}",
2451 render_struct_fields(
2452 g,
2453 ty,
2454 fields,
2455 tab,
2456 structhead,
2457 it.has_stripped_entries().unwrap_or(false),
2458 cx,
2459 )
2460 )
2461 })
2462}
2463
2464fn render_struct_fields(
2465 g: Option<&clean::Generics>,
2466 ty: Option<CtorKind>,
2467 fields: &[clean::Item],
2468 tab: &str,
2469 structhead: bool,
2470 has_stripped_entries: bool,
2471 cx: &Context<'_>,
2472) -> impl fmt::Display {
2473 fmt::from_fn(move |w| {
2474 match ty {
2475 None => {
2476 let where_displayed = if let Some(generics) = g
2477 && let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline)
2478 {
2479 write!(w, "{where_clause}")?;
2480 true
2481 } else {
2482 false
2483 };
2484
2485 if !where_displayed {
2487 w.write_str(" {")?;
2488 } else {
2489 w.write_str("{")?;
2490 }
2491 let count_fields =
2492 fields.iter().filter(|f| matches!(f.kind, clean::StructFieldItem(..))).count();
2493 let has_visible_fields = count_fields > 0;
2494 let toggle = should_hide_fields(count_fields);
2495 if toggle {
2496 toggle_open(&mut *w, format_args!("{count_fields} fields"));
2497 }
2498 if has_visible_fields {
2499 writeln!(w)?;
2500 }
2501 for field in fields {
2502 if let clean::StructFieldItem(ref ty) = field.kind {
2503 render_attributes_in_code(w, field, &format!("{tab} "), cx);
2504 writeln!(
2505 w,
2506 "{tab} {vis}{name}: {ty},",
2507 vis = visibility_print_with_space(field, cx),
2508 name = field.name.unwrap(),
2509 ty = print_type(ty, cx)
2510 )?;
2511 }
2512 }
2513
2514 if has_visible_fields {
2515 if has_stripped_entries {
2516 writeln!(
2517 w,
2518 "{tab} <span class=\"comment\">/* private fields */</span>"
2519 )?;
2520 }
2521 write!(w, "{tab}")?;
2522 } else if has_stripped_entries {
2523 write!(w, " <span class=\"comment\">/* private fields */</span> ")?;
2524 }
2525 if toggle {
2526 toggle_close(&mut *w);
2527 }
2528 w.write_str("}")?;
2529 }
2530 Some(CtorKind::Fn) => {
2531 w.write_str("(")?;
2532 if !fields.is_empty()
2533 && fields.iter().all(|field| {
2534 matches!(field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
2535 })
2536 {
2537 write!(w, "<span class=\"comment\">/* private fields */</span>")?;
2538 } else {
2539 for (i, field) in fields.iter().enumerate() {
2540 if i > 0 {
2541 w.write_str(", ")?;
2542 }
2543 match field.kind {
2544 clean::StrippedItem(box clean::StructFieldItem(..)) => {
2545 write!(w, "_")?;
2546 }
2547 clean::StructFieldItem(ref ty) => {
2548 write!(
2549 w,
2550 "{}{}",
2551 visibility_print_with_space(field, cx),
2552 print_type(ty, cx),
2553 )?;
2554 }
2555 _ => unreachable!(),
2556 }
2557 }
2558 }
2559 w.write_str(")")?;
2560 if let Some(g) = g {
2561 write!(
2562 w,
2563 "{}",
2564 print_where_clause(g, cx, 0, Ending::NoNewline).maybe_display()
2565 )?;
2566 }
2567 if structhead {
2569 w.write_str(";")?;
2570 }
2571 }
2572 Some(CtorKind::Const) => {
2573 if let Some(g) = g {
2575 write!(
2576 w,
2577 "{}",
2578 print_where_clause(g, cx, 0, Ending::NoNewline).maybe_display()
2579 )?;
2580 }
2581 w.write_str(";")?;
2582 }
2583 }
2584 Ok(())
2585 })
2586}
2587
2588fn document_non_exhaustive_header(item: &clean::Item) -> &str {
2589 if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
2590}
2591
2592fn document_non_exhaustive(item: &clean::Item) -> impl Display {
2593 fmt::from_fn(|f| {
2594 if item.is_non_exhaustive() {
2595 write!(
2596 f,
2597 "<details class=\"toggle non-exhaustive\">\
2598 <summary class=\"hideme\"><span>{}</span></summary>\
2599 <div class=\"docblock\">",
2600 {
2601 if item.is_struct() {
2602 "This struct is marked as non-exhaustive"
2603 } else if item.is_enum() {
2604 "This enum is marked as non-exhaustive"
2605 } else if item.is_variant() {
2606 "This variant is marked as non-exhaustive"
2607 } else {
2608 "This type is marked as non-exhaustive"
2609 }
2610 }
2611 )?;
2612
2613 if item.is_struct() {
2614 f.write_str(
2615 "Non-exhaustive structs could have additional fields added in future. \
2616 Therefore, non-exhaustive structs cannot be constructed in external crates \
2617 using the traditional <code>Struct { .. }</code> syntax; cannot be \
2618 matched against without a wildcard <code>..</code>; and \
2619 struct update syntax will not work.",
2620 )?;
2621 } else if item.is_enum() {
2622 f.write_str(
2623 "Non-exhaustive enums could have additional variants added in future. \
2624 Therefore, when matching against variants of non-exhaustive enums, an \
2625 extra wildcard arm must be added to account for any future variants.",
2626 )?;
2627 } else if item.is_variant() {
2628 f.write_str(
2629 "Non-exhaustive enum variants could have additional fields added in future. \
2630 Therefore, non-exhaustive enum variants cannot be constructed in external \
2631 crates and cannot be matched against.",
2632 )?;
2633 } else {
2634 f.write_str(
2635 "This type will require a wildcard arm in any match statements or constructors.",
2636 )?;
2637 }
2638
2639 f.write_str("</div></details>")?;
2640 }
2641 Ok(())
2642 })
2643}
2644
2645fn pluralize(count: usize) -> &'static str {
2646 if count > 1 { "s" } else { "" }
2647}