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