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