]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/render/print_item.rs
Remove unneeded `to_string` call
[rust.git] / src / librustdoc / html / render / print_item.rs
1 use clean::AttributesExt;
2
3 use std::cmp::Ordering;
4 use std::fmt;
5
6 use rustc_data_structures::fx::FxHashMap;
7 use rustc_hir as hir;
8 use rustc_hir::def::CtorKind;
9 use rustc_hir::def_id::DefId;
10 use rustc_middle::middle::stability;
11 use rustc_middle::span_bug;
12 use rustc_middle::ty::layout::LayoutError;
13 use rustc_middle::ty::{Adt, TyCtxt};
14 use rustc_span::hygiene::MacroKind;
15 use rustc_span::symbol::{kw, sym, Symbol};
16 use rustc_target::abi::{Layout, Primitive, TagEncoding, Variants};
17
18 use super::{
19     collect_paths_for_type, document, ensure_trailing_slash, item_ty_to_section,
20     notable_traits_decl, render_assoc_item, render_assoc_items, render_attributes_in_code,
21     render_attributes_in_pre, render_impl, render_stability_since_raw, write_srclink,
22     AssocItemLink, Context, ImplRenderingParameters,
23 };
24 use crate::clean;
25 use crate::formats::item_type::ItemType;
26 use crate::formats::{AssocItemRender, Impl, RenderMode};
27 use crate::html::escape::Escape;
28 use crate::html::format::{
29     join_with_double_colon, print_abi_with_space, print_constness_with_space, print_where_clause,
30     Buffer, PrintWithSpace,
31 };
32 use crate::html::highlight;
33 use crate::html::layout::Page;
34 use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine};
35 use crate::html::url_parts_builder::UrlPartsBuilder;
36
37 use askama::Template;
38
39 const ITEM_TABLE_OPEN: &str = "<div class=\"item-table\">";
40 const ITEM_TABLE_CLOSE: &str = "</div>";
41 const ITEM_TABLE_ROW_OPEN: &str = "<div class=\"item-row\">";
42 const ITEM_TABLE_ROW_CLOSE: &str = "</div>";
43
44 // A component in a `use` path, like `string` in std::string::ToString
45 struct PathComponent {
46     path: String,
47     name: Symbol,
48 }
49
50 #[derive(Template)]
51 #[template(path = "print_item.html")]
52 struct ItemVars<'a> {
53     page: &'a Page<'a>,
54     static_root_path: &'a str,
55     typ: &'a str,
56     name: &'a str,
57     item_type: &'a str,
58     path_components: Vec<PathComponent>,
59     stability_since_raw: &'a str,
60     src_href: Option<&'a str>,
61 }
62
63 pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer, page: &Page<'_>) {
64     debug_assert!(!item.is_stripped());
65     let typ = match *item.kind {
66         clean::ModuleItem(_) => {
67             if item.is_crate() {
68                 "Crate "
69             } else {
70                 "Module "
71             }
72         }
73         clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ",
74         clean::TraitItem(..) => "Trait ",
75         clean::StructItem(..) => "Struct ",
76         clean::UnionItem(..) => "Union ",
77         clean::EnumItem(..) => "Enum ",
78         clean::TypedefItem(..) => "Type Definition ",
79         clean::MacroItem(..) => "Macro ",
80         clean::ProcMacroItem(ref mac) => match mac.kind {
81             MacroKind::Bang => "Macro ",
82             MacroKind::Attr => "Attribute Macro ",
83             MacroKind::Derive => "Derive Macro ",
84         },
85         clean::PrimitiveItem(..) => "Primitive Type ",
86         clean::StaticItem(..) | clean::ForeignStaticItem(..) => "Static ",
87         clean::ConstantItem(..) => "Constant ",
88         clean::ForeignTypeItem => "Foreign Type ",
89         clean::KeywordItem(..) => "Keyword ",
90         clean::OpaqueTyItem(..) => "Opaque Type ",
91         clean::TraitAliasItem(..) => "Trait Alias ",
92         _ => {
93             // We don't generate pages for any other type.
94             unreachable!();
95         }
96     };
97     let mut stability_since_raw = Buffer::new();
98     render_stability_since_raw(
99         &mut stability_since_raw,
100         item.stable_since(cx.tcx()),
101         item.const_stability(cx.tcx()),
102         None,
103         None,
104     );
105     let stability_since_raw: String = stability_since_raw.into_inner();
106
107     // Write source tag
108     //
109     // When this item is part of a `crate use` in a downstream crate, the
110     // source link in the downstream documentation will actually come back to
111     // this page, and this link will be auto-clicked. The `id` attribute is
112     // used to find the link to auto-click.
113     let src_href =
114         if cx.include_sources && !item.is_primitive() { cx.src_href(item) } else { None };
115
116     let path_components = if item.is_primitive() || item.is_keyword() {
117         vec![]
118     } else {
119         let cur = &cx.current;
120         let amt = if item.is_mod() { cur.len() - 1 } else { cur.len() };
121         cur.iter()
122             .enumerate()
123             .take(amt)
124             .map(|(i, component)| PathComponent {
125                 path: "../".repeat(cur.len() - i - 1),
126                 name: *component,
127             })
128             .collect()
129     };
130
131     let item_vars = ItemVars {
132         page,
133         static_root_path: page.get_static_root_path(),
134         typ,
135         name: item.name.as_ref().unwrap().as_str(),
136         item_type: &item.type_().to_string(),
137         path_components,
138         stability_since_raw: &stability_since_raw,
139         src_href: src_href.as_deref(),
140     };
141
142     item_vars.render_into(buf).unwrap();
143
144     match *item.kind {
145         clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items),
146         clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => {
147             item_function(buf, cx, item, f)
148         }
149         clean::TraitItem(ref t) => item_trait(buf, cx, item, t),
150         clean::StructItem(ref s) => item_struct(buf, cx, item, s),
151         clean::UnionItem(ref s) => item_union(buf, cx, item, s),
152         clean::EnumItem(ref e) => item_enum(buf, cx, item, e),
153         clean::TypedefItem(ref t, is_associated) => item_typedef(buf, cx, item, t, is_associated),
154         clean::MacroItem(ref m) => item_macro(buf, cx, item, m),
155         clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m),
156         clean::PrimitiveItem(_) => item_primitive(buf, cx, item),
157         clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => item_static(buf, cx, item, i),
158         clean::ConstantItem(ref c) => item_constant(buf, cx, item, c),
159         clean::ForeignTypeItem => item_foreign_type(buf, cx, item),
160         clean::KeywordItem(_) => item_keyword(buf, cx, item),
161         clean::OpaqueTyItem(ref e) => item_opaque_ty(buf, cx, item, e),
162         clean::TraitAliasItem(ref ta) => item_trait_alias(buf, cx, item, ta),
163         _ => {
164             // We don't generate pages for any other type.
165             unreachable!();
166         }
167     }
168 }
169
170 /// For large structs, enums, unions, etc, determine whether to hide their fields
171 fn should_hide_fields(n_fields: usize) -> bool {
172     n_fields > 12
173 }
174
175 fn toggle_open(w: &mut Buffer, text: impl fmt::Display) {
176     write!(
177         w,
178         "<details class=\"rustdoc-toggle type-contents-toggle\">\
179             <summary class=\"hideme\">\
180                 <span>Show {}</span>\
181             </summary>",
182         text
183     );
184 }
185
186 fn toggle_close(w: &mut Buffer) {
187     w.write_str("</details>");
188 }
189
190 fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) {
191     document(w, cx, item, None, HeadingOffset::H2);
192
193     let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::<Vec<usize>>();
194
195     // the order of item types in the listing
196     fn reorder(ty: ItemType) -> u8 {
197         match ty {
198             ItemType::ExternCrate => 0,
199             ItemType::Import => 1,
200             ItemType::Primitive => 2,
201             ItemType::Module => 3,
202             ItemType::Macro => 4,
203             ItemType::Struct => 5,
204             ItemType::Enum => 6,
205             ItemType::Constant => 7,
206             ItemType::Static => 8,
207             ItemType::Trait => 9,
208             ItemType::Function => 10,
209             ItemType::Typedef => 12,
210             ItemType::Union => 13,
211             _ => 14 + ty as u8,
212         }
213     }
214
215     fn cmp(
216         i1: &clean::Item,
217         i2: &clean::Item,
218         idx1: usize,
219         idx2: usize,
220         tcx: TyCtxt<'_>,
221     ) -> Ordering {
222         let ty1 = i1.type_();
223         let ty2 = i2.type_();
224         if item_ty_to_section(ty1) != item_ty_to_section(ty2)
225             || (ty1 != ty2 && (ty1 == ItemType::ExternCrate || ty2 == ItemType::ExternCrate))
226         {
227             return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2));
228         }
229         let s1 = i1.stability(tcx).as_ref().map(|s| s.level);
230         let s2 = i2.stability(tcx).as_ref().map(|s| s.level);
231         if let (Some(a), Some(b)) = (s1, s2) {
232             match (a.is_stable(), b.is_stable()) {
233                 (true, true) | (false, false) => {}
234                 (false, true) => return Ordering::Less,
235                 (true, false) => return Ordering::Greater,
236             }
237         }
238         let lhs = i1.name.unwrap_or(kw::Empty);
239         let rhs = i2.name.unwrap_or(kw::Empty);
240         compare_names(lhs.as_str(), rhs.as_str())
241     }
242
243     if cx.shared.sort_modules_alphabetically {
244         indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2, cx.tcx()));
245     }
246     // This call is to remove re-export duplicates in cases such as:
247     //
248     // ```
249     // crate mod foo {
250     //     crate mod bar {
251     //         crate trait Double { fn foo(); }
252     //     }
253     // }
254     //
255     // crate use foo::bar::*;
256     // crate use foo::*;
257     // ```
258     //
259     // `Double` will appear twice in the generated docs.
260     //
261     // FIXME: This code is quite ugly and could be improved. Small issue: DefId
262     // can be identical even if the elements are different (mostly in imports).
263     // So in case this is an import, we keep everything by adding a "unique id"
264     // (which is the position in the vector).
265     indices.dedup_by_key(|i| {
266         (
267             items[*i].def_id,
268             if items[*i].name.is_some() { Some(full_path(cx, &items[*i])) } else { None },
269             items[*i].type_(),
270             if items[*i].is_import() { *i } else { 0 },
271         )
272     });
273
274     debug!("{:?}", indices);
275     let mut last_section = None;
276
277     for &idx in &indices {
278         let myitem = &items[idx];
279         if myitem.is_stripped() {
280             continue;
281         }
282
283         let my_section = item_ty_to_section(myitem.type_());
284         if Some(my_section) != last_section {
285             if last_section.is_some() {
286                 w.write_str(ITEM_TABLE_CLOSE);
287             }
288             last_section = Some(my_section);
289             write!(
290                 w,
291                 "<h2 id=\"{id}\" class=\"small-section-header\">\
292                     <a href=\"#{id}\">{name}</a>\
293                  </h2>\n{}",
294                 ITEM_TABLE_OPEN,
295                 id = cx.derive_id(my_section.id().to_owned()),
296                 name = my_section.name(),
297             );
298         }
299
300         match *myitem.kind {
301             clean::ExternCrateItem { ref src } => {
302                 use crate::html::format::anchor;
303
304                 w.write_str(ITEM_TABLE_ROW_OPEN);
305                 match *src {
306                     Some(src) => write!(
307                         w,
308                         "<div class=\"item-left\"><code>{}extern crate {} as {};",
309                         myitem.visibility.print_with_space(myitem.def_id, cx),
310                         anchor(myitem.def_id.expect_def_id(), src, cx),
311                         myitem.name.unwrap(),
312                     ),
313                     None => write!(
314                         w,
315                         "<div class=\"item-left\"><code>{}extern crate {};",
316                         myitem.visibility.print_with_space(myitem.def_id, cx),
317                         anchor(myitem.def_id.expect_def_id(), myitem.name.unwrap(), cx),
318                     ),
319                 }
320                 w.write_str("</code></div>");
321                 w.write_str(ITEM_TABLE_ROW_CLOSE);
322             }
323
324             clean::ImportItem(ref import) => {
325                 let (stab, stab_tags) = if let Some(import_def_id) = import.source.did {
326                     let ast_attrs = cx.tcx().get_attrs(import_def_id);
327                     let import_attrs = Box::new(clean::Attributes::from_ast(ast_attrs, None));
328
329                     // Just need an item with the correct def_id and attrs
330                     let import_item = clean::Item {
331                         def_id: import_def_id.into(),
332                         attrs: import_attrs,
333                         cfg: ast_attrs.cfg(cx.tcx(), &cx.cache().hidden_cfg),
334                         ..myitem.clone()
335                     };
336
337                     let stab = import_item.stability_class(cx.tcx());
338                     let stab_tags = Some(extra_info_tags(&import_item, item, cx.tcx()));
339                     (stab, stab_tags)
340                 } else {
341                     (None, None)
342                 };
343
344                 let add = if stab.is_some() { " " } else { "" };
345
346                 w.write_str(ITEM_TABLE_ROW_OPEN);
347                 write!(
348                     w,
349                     "<div class=\"item-left {stab}{add}import-item\">\
350                          <code>{vis}{imp}</code>\
351                      </div>\
352                      <div class=\"item-right docblock-short\">{stab_tags}</div>",
353                     stab = stab.unwrap_or_default(),
354                     add = add,
355                     vis = myitem.visibility.print_with_space(myitem.def_id, cx),
356                     imp = import.print(cx),
357                     stab_tags = stab_tags.unwrap_or_default(),
358                 );
359                 w.write_str(ITEM_TABLE_ROW_CLOSE);
360             }
361
362             _ => {
363                 if myitem.name.is_none() {
364                     continue;
365                 }
366
367                 let unsafety_flag = match *myitem.kind {
368                     clean::FunctionItem(_) | clean::ForeignFunctionItem(_)
369                         if myitem.fn_header(cx.tcx()).unwrap().unsafety
370                             == hir::Unsafety::Unsafe =>
371                     {
372                         "<a title=\"unsafe function\" href=\"#\"><sup>⚠</sup></a>"
373                     }
374                     _ => "",
375                 };
376
377                 let stab = myitem.stability_class(cx.tcx());
378                 let add = if stab.is_some() { " " } else { "" };
379
380                 let visibility_emoji = match myitem.visibility {
381                     clean::Visibility::Restricted(_) => {
382                         "<span title=\"Restricted Visibility\">&nbsp;🔒</span> "
383                     }
384                     _ => "",
385                 };
386
387                 let doc_value = myitem.doc_value().unwrap_or_default();
388                 w.write_str(ITEM_TABLE_ROW_OPEN);
389                 write!(
390                     w,
391                     "<div class=\"item-left {stab}{add}module-item\">\
392                             <a class=\"{class}\" href=\"{href}\" title=\"{title}\">{name}</a>\
393                             {visibility_emoji}\
394                             {unsafety_flag}\
395                             {stab_tags}\
396                      </div>\
397                      <div class=\"item-right docblock-short\">{docs}</div>",
398                     name = myitem.name.unwrap(),
399                     visibility_emoji = visibility_emoji,
400                     stab_tags = extra_info_tags(myitem, item, cx.tcx()),
401                     docs = MarkdownSummaryLine(&doc_value, &myitem.links(cx)).into_string(),
402                     class = myitem.type_(),
403                     add = add,
404                     stab = stab.unwrap_or_default(),
405                     unsafety_flag = unsafety_flag,
406                     href = item_path(myitem.type_(), myitem.name.unwrap().as_str()),
407                     title = [full_path(cx, myitem), myitem.type_().to_string()]
408                         .iter()
409                         .filter_map(|s| if !s.is_empty() { Some(s.as_str()) } else { None })
410                         .collect::<Vec<_>>()
411                         .join(" "),
412                 );
413                 w.write_str(ITEM_TABLE_ROW_CLOSE);
414             }
415         }
416     }
417
418     if last_section.is_some() {
419         w.write_str(ITEM_TABLE_CLOSE);
420     }
421 }
422
423 /// Render the stability, deprecation and portability tags that are displayed in the item's summary
424 /// at the module level.
425 fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> String {
426     let mut tags = String::new();
427
428     fn tag_html(class: &str, title: &str, contents: &str) -> String {
429         format!(r#"<span class="stab {}" title="{}">{}</span>"#, class, Escape(title), contents)
430     }
431
432     // The trailing space after each tag is to space it properly against the rest of the docs.
433     if let Some(depr) = &item.deprecation(tcx) {
434         let mut message = "Deprecated";
435         if !stability::deprecation_in_effect(depr) {
436             message = "Deprecation planned";
437         }
438         tags += &tag_html("deprecated", "", message);
439     }
440
441     // The "rustc_private" crates are permanently unstable so it makes no sense
442     // to render "unstable" everywhere.
443     if item
444         .stability(tcx)
445         .as_ref()
446         .map(|s| s.level.is_unstable() && s.feature != sym::rustc_private)
447         == Some(true)
448     {
449         tags += &tag_html("unstable", "", "Experimental");
450     }
451
452     let cfg = match (&item.cfg, parent.cfg.as_ref()) {
453         (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
454         (cfg, _) => cfg.as_deref().cloned(),
455     };
456
457     debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.cfg, cfg);
458     if let Some(ref cfg) = cfg {
459         tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html());
460     }
461
462     tags
463 }
464
465 fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::Function) {
466     let header = it.fn_header(cx.tcx()).expect("printing a function which isn't a function");
467     let constness = print_constness_with_space(&header.constness, it.const_stability(cx.tcx()));
468     let unsafety = header.unsafety.print_with_space();
469     let abi = print_abi_with_space(header.abi).to_string();
470     let asyncness = header.asyncness.print_with_space();
471     let visibility = it.visibility.print_with_space(it.def_id, cx).to_string();
472     let name = it.name.unwrap();
473
474     let generics_len = format!("{:#}", f.generics.print(cx)).len();
475     let header_len = "fn ".len()
476         + visibility.len()
477         + constness.len()
478         + asyncness.len()
479         + unsafety.len()
480         + abi.len()
481         + name.as_str().len()
482         + generics_len;
483
484     wrap_into_docblock(w, |w| {
485         wrap_item(w, "fn", |w| {
486             render_attributes_in_pre(w, it, "");
487             w.reserve(header_len);
488             write!(
489                 w,
490                 "{vis}{constness}{asyncness}{unsafety}{abi}fn \
491                  {name}{generics}{decl}{notable_traits}{where_clause}",
492                 vis = visibility,
493                 constness = constness,
494                 asyncness = asyncness,
495                 unsafety = unsafety,
496                 abi = abi,
497                 name = name,
498                 generics = f.generics.print(cx),
499                 where_clause = print_where_clause(&f.generics, cx, 0, true),
500                 decl = f.decl.full_print(header_len, 0, header.asyncness, cx),
501                 notable_traits = notable_traits_decl(&f.decl, cx),
502             );
503         });
504     });
505     document(w, cx, it, None, HeadingOffset::H2)
506 }
507
508 fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) {
509     let bounds = bounds(&t.bounds, false, cx);
510     let types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>();
511     let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>();
512     let required = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>();
513     let provided = t.items.iter().filter(|m| m.is_method()).collect::<Vec<_>>();
514     let count_types = types.len();
515     let count_consts = consts.len();
516     let count_methods = required.len() + provided.len();
517
518     // Output the trait definition
519     wrap_into_docblock(w, |w| {
520         wrap_item(w, "trait", |w| {
521             render_attributes_in_pre(w, it, "");
522             write!(
523                 w,
524                 "{}{}{}trait {}{}{}",
525                 it.visibility.print_with_space(it.def_id, cx),
526                 t.unsafety.print_with_space(),
527                 if t.is_auto { "auto " } else { "" },
528                 it.name.unwrap(),
529                 t.generics.print(cx),
530                 bounds
531             );
532
533             if !t.generics.where_predicates.is_empty() {
534                 write!(w, "{}", print_where_clause(&t.generics, cx, 0, true));
535             } else {
536                 w.write_str(" ");
537             }
538
539             if t.items.is_empty() {
540                 w.write_str("{ }");
541             } else {
542                 // FIXME: we should be using a derived_id for the Anchors here
543                 w.write_str("{\n");
544                 let mut toggle = false;
545
546                 // If there are too many associated types, hide _everything_
547                 if should_hide_fields(count_types) {
548                     toggle = true;
549                     toggle_open(
550                         w,
551                         format_args!(
552                             "{} associated items",
553                             count_types + count_consts + count_methods
554                         ),
555                     );
556                 }
557                 for t in &types {
558                     render_assoc_item(
559                         w,
560                         t,
561                         AssocItemLink::Anchor(None),
562                         ItemType::Trait,
563                         cx,
564                         RenderMode::Normal,
565                     );
566                     w.write_str(";\n");
567                 }
568                 // If there are too many associated constants, hide everything after them
569                 // We also do this if the types + consts is large because otherwise we could
570                 // render a bunch of types and _then_ a bunch of consts just because both were
571                 // _just_ under the limit
572                 if !toggle && should_hide_fields(count_types + count_consts) {
573                     toggle = true;
574                     toggle_open(
575                         w,
576                         format_args!(
577                             "{} associated constant{} and {} method{}",
578                             count_consts,
579                             pluralize(count_consts),
580                             count_methods,
581                             pluralize(count_methods),
582                         ),
583                     );
584                 }
585                 if !types.is_empty() && !consts.is_empty() {
586                     w.write_str("\n");
587                 }
588                 for t in &consts {
589                     render_assoc_item(
590                         w,
591                         t,
592                         AssocItemLink::Anchor(None),
593                         ItemType::Trait,
594                         cx,
595                         RenderMode::Normal,
596                     );
597                     w.write_str(";\n");
598                 }
599                 if !toggle && should_hide_fields(count_methods) {
600                     toggle = true;
601                     toggle_open(w, format_args!("{} methods", count_methods));
602                 }
603                 if !consts.is_empty() && !required.is_empty() {
604                     w.write_str("\n");
605                 }
606                 for (pos, m) in required.iter().enumerate() {
607                     render_assoc_item(
608                         w,
609                         m,
610                         AssocItemLink::Anchor(None),
611                         ItemType::Trait,
612                         cx,
613                         RenderMode::Normal,
614                     );
615                     w.write_str(";\n");
616
617                     if pos < required.len() - 1 {
618                         w.write_str("<div class=\"item-spacer\"></div>");
619                     }
620                 }
621                 if !required.is_empty() && !provided.is_empty() {
622                     w.write_str("\n");
623                 }
624                 for (pos, m) in provided.iter().enumerate() {
625                     render_assoc_item(
626                         w,
627                         m,
628                         AssocItemLink::Anchor(None),
629                         ItemType::Trait,
630                         cx,
631                         RenderMode::Normal,
632                     );
633                     match *m.kind {
634                         clean::MethodItem(ref inner, _)
635                             if !inner.generics.where_predicates.is_empty() =>
636                         {
637                             w.write_str(",\n    { ... }\n");
638                         }
639                         _ => {
640                             w.write_str(" { ... }\n");
641                         }
642                     }
643                     if pos < provided.len() - 1 {
644                         w.write_str("<div class=\"item-spacer\"></div>");
645                     }
646                 }
647                 if toggle {
648                     toggle_close(w);
649                 }
650                 w.write_str("}");
651             }
652         });
653     });
654
655     // Trait documentation
656     document(w, cx, it, None, HeadingOffset::H2);
657
658     fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) {
659         write!(
660             w,
661             "<h2 id=\"{0}\" class=\"small-section-header\">\
662                 {1}<a href=\"#{0}\" class=\"anchor\"></a>\
663              </h2>{2}",
664             id, title, extra_content
665         )
666     }
667
668     fn trait_item(w: &mut Buffer, cx: &Context<'_>, m: &clean::Item, t: &clean::Item) {
669         let name = m.name.unwrap();
670         info!("Documenting {} on {:?}", name, t.name);
671         let item_type = m.type_();
672         let id = cx.derive_id(format!("{}.{}", item_type, name));
673         let mut content = Buffer::empty_from(w);
674         document(&mut content, cx, m, Some(t), HeadingOffset::H5);
675         let toggled = !content.is_empty();
676         if toggled {
677             write!(w, "<details class=\"rustdoc-toggle\" open><summary>");
678         }
679         write!(w, "<div id=\"{}\" class=\"method has-srclink\">", id);
680         write!(w, "<div class=\"rightside\">");
681
682         let has_stability = render_stability_since(w, m, t, cx.tcx());
683         if has_stability {
684             w.write_str(" · ");
685         }
686         write_srclink(cx, m, w);
687         write!(w, "</div>");
688         write!(w, "<h4 class=\"code-header\">");
689         render_assoc_item(
690             w,
691             m,
692             AssocItemLink::Anchor(Some(&id)),
693             ItemType::Impl,
694             cx,
695             RenderMode::Normal,
696         );
697         w.write_str("</h4>");
698         w.write_str("</div>");
699         if toggled {
700             write!(w, "</summary>");
701             w.push_buffer(content);
702             write!(w, "</details>");
703         }
704     }
705
706     if !types.is_empty() {
707         write_small_section_header(
708             w,
709             "associated-types",
710             "Associated Types",
711             "<div class=\"methods\">",
712         );
713         for t in types {
714             trait_item(w, cx, t, it);
715         }
716         w.write_str("</div>");
717     }
718
719     if !consts.is_empty() {
720         write_small_section_header(
721             w,
722             "associated-const",
723             "Associated Constants",
724             "<div class=\"methods\">",
725         );
726         for t in consts {
727             trait_item(w, cx, t, it);
728         }
729         w.write_str("</div>");
730     }
731
732     // Output the documentation for each function individually
733     if !required.is_empty() {
734         write_small_section_header(
735             w,
736             "required-methods",
737             "Required methods",
738             "<div class=\"methods\">",
739         );
740         for m in required {
741             trait_item(w, cx, m, it);
742         }
743         w.write_str("</div>");
744     }
745     if !provided.is_empty() {
746         write_small_section_header(
747             w,
748             "provided-methods",
749             "Provided methods",
750             "<div class=\"methods\">",
751         );
752         for m in provided {
753             trait_item(w, cx, m, it);
754         }
755         w.write_str("</div>");
756     }
757
758     // If there are methods directly on this trait object, render them here.
759     render_assoc_items(w, cx, it, it.def_id.expect_def_id(), AssocItemRender::All);
760
761     let cache = cx.cache();
762     if let Some(implementors) = cache.implementors.get(&it.def_id.expect_def_id()) {
763         // The DefId is for the first Type found with that name. The bool is
764         // if any Types with the same name but different DefId have been found.
765         let mut implementor_dups: FxHashMap<Symbol, (DefId, bool)> = FxHashMap::default();
766         for implementor in implementors {
767             match implementor.inner_impl().for_ {
768                 clean::Type::Path { ref path }
769                 | clean::BorrowedRef { type_: box clean::Type::Path { ref path }, .. }
770                     if !path.is_assoc_ty() =>
771                 {
772                     let did = path.def_id();
773                     let &mut (prev_did, ref mut has_duplicates) =
774                         implementor_dups.entry(path.last()).or_insert((did, false));
775                     if prev_did != did {
776                         *has_duplicates = true;
777                     }
778                 }
779                 _ => {}
780             }
781         }
782
783         let (local, foreign) = implementors.iter().partition::<Vec<_>, _>(|i| {
784             i.inner_impl().for_.def_id(cache).map_or(true, |d| cache.paths.contains_key(&d))
785         });
786
787         let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) =
788             local.iter().partition(|i| i.inner_impl().kind.is_auto());
789
790         synthetic.sort_by(|a, b| compare_impl(a, b, cx));
791         concrete.sort_by(|a, b| compare_impl(a, b, cx));
792
793         if !foreign.is_empty() {
794             write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", "");
795
796             for implementor in foreign {
797                 let provided_methods = implementor.inner_impl().provided_trait_methods(cx.tcx());
798                 let assoc_link =
799                     AssocItemLink::GotoSource(implementor.impl_item.def_id, &provided_methods);
800                 render_impl(
801                     w,
802                     cx,
803                     implementor,
804                     it,
805                     assoc_link,
806                     RenderMode::Normal,
807                     None,
808                     &[],
809                     ImplRenderingParameters {
810                         show_def_docs: false,
811                         is_on_foreign_type: true,
812                         show_default_items: false,
813                         show_non_assoc_items: true,
814                         toggle_open_by_default: false,
815                     },
816                 );
817             }
818         }
819
820         write_small_section_header(
821             w,
822             "implementors",
823             "Implementors",
824             "<div class=\"item-list\" id=\"implementors-list\">",
825         );
826         for implementor in concrete {
827             render_implementor(cx, implementor, it, w, &implementor_dups, &[]);
828         }
829         w.write_str("</div>");
830
831         if t.is_auto {
832             write_small_section_header(
833                 w,
834                 "synthetic-implementors",
835                 "Auto implementors",
836                 "<div class=\"item-list\" id=\"synthetic-implementors-list\">",
837             );
838             for implementor in synthetic {
839                 render_implementor(
840                     cx,
841                     implementor,
842                     it,
843                     w,
844                     &implementor_dups,
845                     &collect_paths_for_type(implementor.inner_impl().for_.clone(), cache),
846                 );
847             }
848             w.write_str("</div>");
849         }
850     } else {
851         // even without any implementations to write in, we still want the heading and list, so the
852         // implementors javascript file pulled in below has somewhere to write the impls into
853         write_small_section_header(
854             w,
855             "implementors",
856             "Implementors",
857             "<div class=\"item-list\" id=\"implementors-list\"></div>",
858         );
859
860         if t.is_auto {
861             write_small_section_header(
862                 w,
863                 "synthetic-implementors",
864                 "Auto implementors",
865                 "<div class=\"item-list\" id=\"synthetic-implementors-list\"></div>",
866             );
867         }
868     }
869
870     let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..")
871         .take(cx.current.len())
872         .chain(std::iter::once("implementors"))
873         .collect();
874     if it.def_id.is_local() {
875         js_src_path.extend(cx.current.iter().copied());
876     } else {
877         let (ref path, _) = cache.external_paths[&it.def_id.expect_def_id()];
878         js_src_path.extend(path[..path.len() - 1].iter().copied());
879     }
880     js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), it.name.unwrap()));
881     write!(
882         w,
883         "<script type=\"text/javascript\" src=\"{src}\" async></script>",
884         src = js_src_path.finish(),
885     );
886 }
887
888 fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TraitAlias) {
889     wrap_into_docblock(w, |w| {
890         wrap_item(w, "trait-alias", |w| {
891             render_attributes_in_pre(w, it, "");
892             write!(
893                 w,
894                 "trait {}{}{} = {};",
895                 it.name.unwrap(),
896                 t.generics.print(cx),
897                 print_where_clause(&t.generics, cx, 0, true),
898                 bounds(&t.bounds, true, cx)
899             );
900         });
901     });
902
903     document(w, cx, it, None, HeadingOffset::H2);
904
905     // Render any items associated directly to this alias, as otherwise they
906     // won't be visible anywhere in the docs. It would be nice to also show
907     // associated items from the aliased type (see discussion in #32077), but
908     // we need #14072 to make sense of the generics.
909     render_assoc_items(w, cx, it, it.def_id.expect_def_id(), AssocItemRender::All)
910 }
911
912 fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) {
913     wrap_into_docblock(w, |w| {
914         wrap_item(w, "opaque", |w| {
915             render_attributes_in_pre(w, it, "");
916             write!(
917                 w,
918                 "type {}{}{where_clause} = impl {bounds};",
919                 it.name.unwrap(),
920                 t.generics.print(cx),
921                 where_clause = print_where_clause(&t.generics, cx, 0, true),
922                 bounds = bounds(&t.bounds, false, cx),
923             );
924         });
925     });
926
927     document(w, cx, it, None, HeadingOffset::H2);
928
929     // Render any items associated directly to this alias, as otherwise they
930     // won't be visible anywhere in the docs. It would be nice to also show
931     // associated items from the aliased type (see discussion in #32077), but
932     // we need #14072 to make sense of the generics.
933     render_assoc_items(w, cx, it, it.def_id.expect_def_id(), AssocItemRender::All)
934 }
935
936 fn item_typedef(
937     w: &mut Buffer,
938     cx: &Context<'_>,
939     it: &clean::Item,
940     t: &clean::Typedef,
941     is_associated: bool,
942 ) {
943     fn write_content(
944         w: &mut Buffer,
945         cx: &Context<'_>,
946         it: &clean::Item,
947         t: &clean::Typedef,
948         is_associated: bool,
949     ) {
950         wrap_item(w, "typedef", |w| {
951             render_attributes_in_pre(w, it, "");
952             if !is_associated {
953                 write!(w, "{}", it.visibility.print_with_space(it.def_id, cx));
954             }
955             write!(
956                 w,
957                 "type {}{}{where_clause} = {type_};",
958                 it.name.unwrap(),
959                 t.generics.print(cx),
960                 where_clause = print_where_clause(&t.generics, cx, 0, true),
961                 type_ = t.type_.print(cx),
962             );
963         });
964     }
965
966     // If this is an associated typedef, we don't want to wrap it into a docblock.
967     if is_associated {
968         write_content(w, cx, it, t, is_associated);
969     } else {
970         wrap_into_docblock(w, |w| {
971             write_content(w, cx, it, t, is_associated);
972         });
973     }
974
975     document(w, cx, it, None, HeadingOffset::H2);
976
977     let def_id = it.def_id.expect_def_id();
978     // Render any items associated directly to this alias, as otherwise they
979     // won't be visible anywhere in the docs. It would be nice to also show
980     // associated items from the aliased type (see discussion in #32077), but
981     // we need #14072 to make sense of the generics.
982     render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
983     document_type_layout(w, cx, def_id);
984 }
985
986 fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) {
987     wrap_into_docblock(w, |w| {
988         wrap_item(w, "union", |w| {
989             render_attributes_in_pre(w, it, "");
990             render_union(w, it, Some(&s.generics), &s.fields, "", cx);
991         });
992     });
993
994     document(w, cx, it, None, HeadingOffset::H2);
995
996     let mut fields = s
997         .fields
998         .iter()
999         .filter_map(|f| match *f.kind {
1000             clean::StructFieldItem(ref ty) => Some((f, ty)),
1001             _ => None,
1002         })
1003         .peekable();
1004     if fields.peek().is_some() {
1005         write!(
1006             w,
1007             "<h2 id=\"fields\" class=\"fields small-section-header\">\
1008                    Fields<a href=\"#fields\" class=\"anchor\"></a></h2>"
1009         );
1010         for (field, ty) in fields {
1011             let name = field.name.expect("union field name");
1012             let id = format!("{}.{}", ItemType::StructField, name);
1013             write!(
1014                 w,
1015                 "<span id=\"{id}\" class=\"{shortty} small-section-header\">\
1016                      <a href=\"#{id}\" class=\"anchor field\"></a>\
1017                      <code>{name}: {ty}</code>\
1018                  </span>",
1019                 id = id,
1020                 name = name,
1021                 shortty = ItemType::StructField,
1022                 ty = ty.print(cx),
1023             );
1024             if let Some(stability_class) = field.stability_class(cx.tcx()) {
1025                 write!(w, "<span class=\"stab {stab}\"></span>", stab = stability_class);
1026             }
1027             document(w, cx, field, Some(it), HeadingOffset::H3);
1028         }
1029     }
1030     let def_id = it.def_id.expect_def_id();
1031     render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
1032     document_type_layout(w, cx, def_id);
1033 }
1034
1035 fn print_tuple_struct_fields(w: &mut Buffer, cx: &Context<'_>, s: &[clean::Item]) {
1036     for (i, ty) in s.iter().enumerate() {
1037         if i > 0 {
1038             w.write_str(",&nbsp;");
1039         }
1040         match *ty.kind {
1041             clean::StrippedItem(box clean::StructFieldItem(_)) => w.write_str("_"),
1042             clean::StructFieldItem(ref ty) => write!(w, "{}", ty.print(cx)),
1043             _ => unreachable!(),
1044         }
1045     }
1046 }
1047
1048 fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) {
1049     wrap_into_docblock(w, |w| {
1050         wrap_item(w, "enum", |w| {
1051             render_attributes_in_pre(w, it, "");
1052             write!(
1053                 w,
1054                 "{}enum {}{}{}",
1055                 it.visibility.print_with_space(it.def_id, cx),
1056                 it.name.unwrap(),
1057                 e.generics.print(cx),
1058                 print_where_clause(&e.generics, cx, 0, true),
1059             );
1060             if e.variants.is_empty() && !e.variants_stripped {
1061                 w.write_str(" {}");
1062             } else {
1063                 w.write_str(" {\n");
1064                 let count_variants = e.variants.len();
1065                 let toggle = should_hide_fields(count_variants);
1066                 if toggle {
1067                     toggle_open(w, format_args!("{} variants", count_variants));
1068                 }
1069                 for v in &e.variants {
1070                     w.write_str("    ");
1071                     let name = v.name.unwrap();
1072                     match *v.kind {
1073                         clean::VariantItem(ref var) => match var {
1074                             clean::Variant::CLike => write!(w, "{}", name),
1075                             clean::Variant::Tuple(ref s) => {
1076                                 write!(w, "{}(", name);
1077                                 print_tuple_struct_fields(w, cx, s);
1078                                 w.write_str(")");
1079                             }
1080                             clean::Variant::Struct(ref s) => {
1081                                 render_struct(
1082                                     w,
1083                                     v,
1084                                     None,
1085                                     s.struct_type,
1086                                     &s.fields,
1087                                     "    ",
1088                                     false,
1089                                     cx,
1090                                 );
1091                             }
1092                         },
1093                         _ => unreachable!(),
1094                     }
1095                     w.write_str(",\n");
1096                 }
1097
1098                 if e.variants_stripped {
1099                     w.write_str("    // some variants omitted\n");
1100                 }
1101                 if toggle {
1102                     toggle_close(w);
1103                 }
1104                 w.write_str("}");
1105             }
1106         });
1107     });
1108
1109     document(w, cx, it, None, HeadingOffset::H2);
1110
1111     if !e.variants.is_empty() {
1112         write!(
1113             w,
1114             "<h2 id=\"variants\" class=\"variants small-section-header\">\
1115                    Variants{}<a href=\"#variants\" class=\"anchor\"></a></h2>",
1116             document_non_exhaustive_header(it)
1117         );
1118         document_non_exhaustive(w, it);
1119         for variant in &e.variants {
1120             let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
1121             write!(
1122                 w,
1123                 "<h3 id=\"{id}\" class=\"variant small-section-header\">\
1124                     <a href=\"#{id}\" class=\"anchor field\"></a>\
1125                     <code>{name}",
1126                 id = id,
1127                 name = variant.name.unwrap()
1128             );
1129             if let clean::VariantItem(clean::Variant::Tuple(ref s)) = *variant.kind {
1130                 w.write_str("(");
1131                 print_tuple_struct_fields(w, cx, s);
1132                 w.write_str(")");
1133             }
1134             w.write_str("</code>");
1135             render_stability_since(w, variant, it, cx.tcx());
1136             w.write_str("</h3>");
1137
1138             use crate::clean::Variant;
1139
1140             let heading_and_fields = match &*variant.kind {
1141                 clean::VariantItem(Variant::Struct(s)) => Some(("Fields", &s.fields)),
1142                 // Documentation on tuple variant fields is rare, so to reduce noise we only emit
1143                 // the section if at least one field is documented.
1144                 clean::VariantItem(Variant::Tuple(fields))
1145                     if fields.iter().any(|f| f.doc_value().is_some()) =>
1146                 {
1147                     Some(("Tuple Fields", fields))
1148                 }
1149                 _ => None,
1150             };
1151
1152             if let Some((heading, fields)) = heading_and_fields {
1153                 let variant_id =
1154                     cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
1155                 write!(w, "<div class=\"sub-variant\" id=\"{id}\">", id = variant_id);
1156                 write!(w, "<h4>{heading}</h4>", heading = heading);
1157                 document_non_exhaustive(w, variant);
1158                 for field in fields {
1159                     match *field.kind {
1160                         clean::StrippedItem(box clean::StructFieldItem(_)) => {}
1161                         clean::StructFieldItem(ref ty) => {
1162                             let id = cx.derive_id(format!(
1163                                 "variant.{}.field.{}",
1164                                 variant.name.unwrap(),
1165                                 field.name.unwrap()
1166                             ));
1167                             write!(
1168                                 w,
1169                                 "<div class=\"sub-variant-field\">\
1170                                  <span id=\"{id}\" class=\"variant small-section-header\">\
1171                                     <a href=\"#{id}\" class=\"anchor field\"></a>\
1172                                     <code>{f}:&nbsp;{t}</code>\
1173                                 </span>",
1174                                 id = id,
1175                                 f = field.name.unwrap(),
1176                                 t = ty.print(cx)
1177                             );
1178                             document(w, cx, field, Some(variant), HeadingOffset::H5);
1179                             write!(w, "</div>");
1180                         }
1181                         _ => unreachable!(),
1182                     }
1183                 }
1184                 w.write_str("</div>");
1185             }
1186
1187             document(w, cx, variant, Some(it), HeadingOffset::H4);
1188         }
1189     }
1190     let def_id = it.def_id.expect_def_id();
1191     render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
1192     document_type_layout(w, cx, def_id);
1193 }
1194
1195 fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) {
1196     wrap_into_docblock(w, |w| {
1197         highlight::render_with_highlighting(
1198             &t.source,
1199             w,
1200             Some("macro"),
1201             None,
1202             None,
1203             it.span(cx.tcx()).inner().edition(),
1204             None,
1205             None,
1206             None,
1207         );
1208     });
1209     document(w, cx, it, None, HeadingOffset::H2)
1210 }
1211
1212 fn item_proc_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) {
1213     wrap_into_docblock(w, |w| {
1214         let name = it.name.expect("proc-macros always have names");
1215         match m.kind {
1216             MacroKind::Bang => {
1217                 wrap_item(w, "macro", |w| {
1218                     write!(w, "{}!() {{ /* proc-macro */ }}", name);
1219                 });
1220             }
1221             MacroKind::Attr => {
1222                 wrap_item(w, "attr", |w| {
1223                     write!(w, "#[{}]", name);
1224                 });
1225             }
1226             MacroKind::Derive => {
1227                 wrap_item(w, "derive", |w| {
1228                     write!(w, "#[derive({})]", name);
1229                     if !m.helpers.is_empty() {
1230                         w.push_str("\n{\n");
1231                         w.push_str("    // Attributes available to this derive:\n");
1232                         for attr in &m.helpers {
1233                             writeln!(w, "    #[{}]", attr);
1234                         }
1235                         w.push_str("}\n");
1236                     }
1237                 });
1238             }
1239         }
1240     });
1241     document(w, cx, it, None, HeadingOffset::H2)
1242 }
1243
1244 fn item_primitive(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
1245     document(w, cx, it, None, HeadingOffset::H2);
1246     render_assoc_items(w, cx, it, it.def_id.expect_def_id(), AssocItemRender::All)
1247 }
1248
1249 fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::Constant) {
1250     wrap_into_docblock(w, |w| {
1251         wrap_item(w, "const", |w| {
1252             render_attributes_in_code(w, it);
1253
1254             write!(
1255                 w,
1256                 "{vis}const {name}: {typ}",
1257                 vis = it.visibility.print_with_space(it.def_id, cx),
1258                 name = it.name.unwrap(),
1259                 typ = c.type_.print(cx),
1260             );
1261
1262             let value = c.value(cx.tcx());
1263             let is_literal = c.is_literal(cx.tcx());
1264             let expr = c.expr(cx.tcx());
1265             if value.is_some() || is_literal {
1266                 write!(w, " = {expr};", expr = Escape(&expr));
1267             } else {
1268                 w.write_str(";");
1269             }
1270
1271             if !is_literal {
1272                 if let Some(value) = &value {
1273                     let value_lowercase = value.to_lowercase();
1274                     let expr_lowercase = expr.to_lowercase();
1275
1276                     if value_lowercase != expr_lowercase
1277                         && value_lowercase.trim_end_matches("i32") != expr_lowercase
1278                     {
1279                         write!(w, " // {value}", value = Escape(value));
1280                     }
1281                 }
1282             }
1283         });
1284     });
1285
1286     document(w, cx, it, None, HeadingOffset::H2)
1287 }
1288
1289 fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) {
1290     wrap_into_docblock(w, |w| {
1291         wrap_item(w, "struct", |w| {
1292             render_attributes_in_code(w, it);
1293             render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx);
1294         });
1295     });
1296
1297     document(w, cx, it, None, HeadingOffset::H2);
1298
1299     let mut fields = s
1300         .fields
1301         .iter()
1302         .filter_map(|f| match *f.kind {
1303             clean::StructFieldItem(ref ty) => Some((f, ty)),
1304             _ => None,
1305         })
1306         .peekable();
1307     if let CtorKind::Fictive | CtorKind::Fn = s.struct_type {
1308         if fields.peek().is_some() {
1309             write!(
1310                 w,
1311                 "<h2 id=\"fields\" class=\"fields small-section-header\">\
1312                      {}{}<a href=\"#fields\" class=\"anchor\"></a>\
1313                  </h2>",
1314                 if let CtorKind::Fictive = s.struct_type { "Fields" } else { "Tuple Fields" },
1315                 document_non_exhaustive_header(it)
1316             );
1317             document_non_exhaustive(w, it);
1318             for (index, (field, ty)) in fields.enumerate() {
1319                 let field_name =
1320                     field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string());
1321                 let id = cx.derive_id(format!("{}.{}", ItemType::StructField, field_name));
1322                 write!(
1323                     w,
1324                     "<span id=\"{id}\" class=\"{item_type} small-section-header\">\
1325                          <a href=\"#{id}\" class=\"anchor field\"></a>\
1326                          <code>{name}: {ty}</code>\
1327                      </span>",
1328                     item_type = ItemType::StructField,
1329                     id = id,
1330                     name = field_name,
1331                     ty = ty.print(cx)
1332                 );
1333                 document(w, cx, field, Some(it), HeadingOffset::H3);
1334             }
1335         }
1336     }
1337     let def_id = it.def_id.expect_def_id();
1338     render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
1339     document_type_layout(w, cx, def_id);
1340 }
1341
1342 fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) {
1343     wrap_into_docblock(w, |w| {
1344         wrap_item(w, "static", |w| {
1345             render_attributes_in_code(w, it);
1346             write!(
1347                 w,
1348                 "{vis}static {mutability}{name}: {typ}",
1349                 vis = it.visibility.print_with_space(it.def_id, cx),
1350                 mutability = s.mutability.print_with_space(),
1351                 name = it.name.unwrap(),
1352                 typ = s.type_.print(cx)
1353             );
1354         });
1355     });
1356     document(w, cx, it, None, HeadingOffset::H2)
1357 }
1358
1359 fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
1360     wrap_into_docblock(w, |w| {
1361         wrap_item(w, "foreigntype", |w| {
1362             w.write_str("extern {\n");
1363             render_attributes_in_code(w, it);
1364             write!(
1365                 w,
1366                 "    {}type {};\n}}",
1367                 it.visibility.print_with_space(it.def_id, cx),
1368                 it.name.unwrap(),
1369             );
1370         });
1371     });
1372
1373     document(w, cx, it, None, HeadingOffset::H2);
1374
1375     render_assoc_items(w, cx, it, it.def_id.expect_def_id(), AssocItemRender::All)
1376 }
1377
1378 fn item_keyword(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
1379     document(w, cx, it, None, HeadingOffset::H2)
1380 }
1381
1382 /// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order).
1383 crate fn compare_names(mut lhs: &str, mut rhs: &str) -> Ordering {
1384     /// Takes a non-numeric and a numeric part from the given &str.
1385     fn take_parts<'a>(s: &mut &'a str) -> (&'a str, &'a str) {
1386         let i = s.find(|c: char| c.is_ascii_digit());
1387         let (a, b) = s.split_at(i.unwrap_or(s.len()));
1388         let i = b.find(|c: char| !c.is_ascii_digit());
1389         let (b, c) = b.split_at(i.unwrap_or(b.len()));
1390         *s = c;
1391         (a, b)
1392     }
1393
1394     while !lhs.is_empty() || !rhs.is_empty() {
1395         let (la, lb) = take_parts(&mut lhs);
1396         let (ra, rb) = take_parts(&mut rhs);
1397         // First process the non-numeric part.
1398         match la.cmp(ra) {
1399             Ordering::Equal => (),
1400             x => return x,
1401         }
1402         // Then process the numeric part, if both sides have one (and they fit in a u64).
1403         if let (Ok(ln), Ok(rn)) = (lb.parse::<u64>(), rb.parse::<u64>()) {
1404             match ln.cmp(&rn) {
1405                 Ordering::Equal => (),
1406                 x => return x,
1407             }
1408         }
1409         // Then process the numeric part again, but this time as strings.
1410         match lb.cmp(rb) {
1411             Ordering::Equal => (),
1412             x => return x,
1413         }
1414     }
1415
1416     Ordering::Equal
1417 }
1418
1419 pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
1420     let mut s = join_with_double_colon(&cx.current);
1421     s.push_str("::");
1422     s.push_str(item.name.unwrap().as_str());
1423     s
1424 }
1425
1426 pub(super) fn item_path(ty: ItemType, name: &str) -> String {
1427     match ty {
1428         ItemType::Module => format!("{}index.html", ensure_trailing_slash(name)),
1429         _ => format!("{}.{}.html", ty, name),
1430     }
1431 }
1432
1433 fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) -> String {
1434     let mut bounds = String::new();
1435     if !t_bounds.is_empty() {
1436         if !trait_alias {
1437             bounds.push_str(": ");
1438         }
1439         for (i, p) in t_bounds.iter().enumerate() {
1440             if i > 0 {
1441                 bounds.push_str(" + ");
1442             }
1443             bounds.push_str(&p.print(cx).to_string());
1444         }
1445     }
1446     bounds
1447 }
1448
1449 fn wrap_into_docblock<F>(w: &mut Buffer, f: F)
1450 where
1451     F: FnOnce(&mut Buffer),
1452 {
1453     w.write_str("<div class=\"docblock item-decl\">");
1454     f(w);
1455     w.write_str("</div>")
1456 }
1457
1458 fn wrap_item<F>(w: &mut Buffer, item_name: &str, f: F)
1459 where
1460     F: FnOnce(&mut Buffer),
1461 {
1462     w.write_fmt(format_args!("<pre class=\"rust {}\"><code>", item_name));
1463     f(w);
1464     w.write_str("</code></pre>");
1465 }
1466
1467 fn render_stability_since(
1468     w: &mut Buffer,
1469     item: &clean::Item,
1470     containing_item: &clean::Item,
1471     tcx: TyCtxt<'_>,
1472 ) -> bool {
1473     render_stability_since_raw(
1474         w,
1475         item.stable_since(tcx),
1476         item.const_stability(tcx),
1477         containing_item.stable_since(tcx),
1478         containing_item.const_stable_since(tcx),
1479     )
1480 }
1481
1482 fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cx: &Context<'_>) -> Ordering {
1483     let lhss = format!("{}", lhs.inner_impl().print(false, cx));
1484     let rhss = format!("{}", rhs.inner_impl().print(false, cx));
1485
1486     // lhs and rhs are formatted as HTML, which may be unnecessary
1487     compare_names(&lhss, &rhss)
1488 }
1489
1490 fn render_implementor(
1491     cx: &Context<'_>,
1492     implementor: &Impl,
1493     trait_: &clean::Item,
1494     w: &mut Buffer,
1495     implementor_dups: &FxHashMap<Symbol, (DefId, bool)>,
1496     aliases: &[String],
1497 ) {
1498     // If there's already another implementor that has the same abridged name, use the
1499     // full path, for example in `std::iter::ExactSizeIterator`
1500     let use_absolute = match implementor.inner_impl().for_ {
1501         clean::Type::Path { ref path, .. }
1502         | clean::BorrowedRef { type_: box clean::Type::Path { ref path, .. }, .. }
1503             if !path.is_assoc_ty() =>
1504         {
1505             implementor_dups[&path.last()].1
1506         }
1507         _ => false,
1508     };
1509     render_impl(
1510         w,
1511         cx,
1512         implementor,
1513         trait_,
1514         AssocItemLink::Anchor(None),
1515         RenderMode::Normal,
1516         Some(use_absolute),
1517         aliases,
1518         ImplRenderingParameters {
1519             show_def_docs: false,
1520             is_on_foreign_type: false,
1521             show_default_items: false,
1522             show_non_assoc_items: false,
1523             toggle_open_by_default: false,
1524         },
1525     );
1526 }
1527
1528 fn render_union(
1529     w: &mut Buffer,
1530     it: &clean::Item,
1531     g: Option<&clean::Generics>,
1532     fields: &[clean::Item],
1533     tab: &str,
1534     cx: &Context<'_>,
1535 ) {
1536     write!(w, "{}union {}", it.visibility.print_with_space(it.def_id, cx), it.name.unwrap());
1537     if let Some(g) = g {
1538         write!(w, "{}", g.print(cx));
1539         write!(w, "{}", print_where_clause(g, cx, 0, true));
1540     }
1541
1542     write!(w, " {{\n{}", tab);
1543     let count_fields =
1544         fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count();
1545     let toggle = should_hide_fields(count_fields);
1546     if toggle {
1547         toggle_open(w, format_args!("{} fields", count_fields));
1548     }
1549
1550     for field in fields {
1551         if let clean::StructFieldItem(ref ty) = *field.kind {
1552             write!(
1553                 w,
1554                 "    {}{}: {},\n{}",
1555                 field.visibility.print_with_space(field.def_id, cx),
1556                 field.name.unwrap(),
1557                 ty.print(cx),
1558                 tab
1559             );
1560         }
1561     }
1562
1563     if it.has_stripped_fields().unwrap() {
1564         write!(w, "    /* private fields */\n{}", tab);
1565     }
1566     if toggle {
1567         toggle_close(w);
1568     }
1569     w.write_str("}");
1570 }
1571
1572 fn render_struct(
1573     w: &mut Buffer,
1574     it: &clean::Item,
1575     g: Option<&clean::Generics>,
1576     ty: CtorKind,
1577     fields: &[clean::Item],
1578     tab: &str,
1579     structhead: bool,
1580     cx: &Context<'_>,
1581 ) {
1582     write!(
1583         w,
1584         "{}{}{}",
1585         it.visibility.print_with_space(it.def_id, cx),
1586         if structhead { "struct " } else { "" },
1587         it.name.unwrap()
1588     );
1589     if let Some(g) = g {
1590         write!(w, "{}", g.print(cx))
1591     }
1592     match ty {
1593         CtorKind::Fictive => {
1594             if let Some(g) = g {
1595                 write!(w, "{}", print_where_clause(g, cx, 0, true),)
1596             }
1597             w.write_str(" {");
1598             let count_fields =
1599                 fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count();
1600             let has_visible_fields = count_fields > 0;
1601             let toggle = should_hide_fields(count_fields);
1602             if toggle {
1603                 toggle_open(w, format_args!("{} fields", count_fields));
1604             }
1605             for field in fields {
1606                 if let clean::StructFieldItem(ref ty) = *field.kind {
1607                     write!(
1608                         w,
1609                         "\n{}    {}{}: {},",
1610                         tab,
1611                         field.visibility.print_with_space(field.def_id, cx),
1612                         field.name.unwrap(),
1613                         ty.print(cx),
1614                     );
1615                 }
1616             }
1617
1618             if has_visible_fields {
1619                 if it.has_stripped_fields().unwrap() {
1620                     write!(w, "\n{}    /* private fields */", tab);
1621                 }
1622                 write!(w, "\n{}", tab);
1623             } else if it.has_stripped_fields().unwrap() {
1624                 write!(w, " /* private fields */ ");
1625             }
1626             if toggle {
1627                 toggle_close(w);
1628             }
1629             w.write_str("}");
1630         }
1631         CtorKind::Fn => {
1632             w.write_str("(");
1633             for (i, field) in fields.iter().enumerate() {
1634                 if i > 0 {
1635                     w.write_str(", ");
1636                 }
1637                 match *field.kind {
1638                     clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
1639                     clean::StructFieldItem(ref ty) => {
1640                         write!(
1641                             w,
1642                             "{}{}",
1643                             field.visibility.print_with_space(field.def_id, cx),
1644                             ty.print(cx),
1645                         )
1646                     }
1647                     _ => unreachable!(),
1648                 }
1649             }
1650             w.write_str(")");
1651             if let Some(g) = g {
1652                 write!(w, "{}", print_where_clause(g, cx, 0, false),)
1653             }
1654             // We only want a ";" when we are displaying a tuple struct, not a variant tuple struct.
1655             if structhead {
1656                 w.write_str(";");
1657             }
1658         }
1659         CtorKind::Const => {
1660             // Needed for PhantomData.
1661             if let Some(g) = g {
1662                 write!(w, "{}", print_where_clause(g, cx, 0, false),)
1663             }
1664             w.write_str(";");
1665         }
1666     }
1667 }
1668
1669 fn document_non_exhaustive_header(item: &clean::Item) -> &str {
1670     if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
1671 }
1672
1673 fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) {
1674     if item.is_non_exhaustive() {
1675         write!(
1676             w,
1677             "<details class=\"rustdoc-toggle non-exhaustive\">\
1678                  <summary class=\"hideme\"><span>{}</span></summary>\
1679                  <div class=\"docblock\">",
1680             {
1681                 if item.is_struct() {
1682                     "This struct is marked as non-exhaustive"
1683                 } else if item.is_enum() {
1684                     "This enum is marked as non-exhaustive"
1685                 } else if item.is_variant() {
1686                     "This variant is marked as non-exhaustive"
1687                 } else {
1688                     "This type is marked as non-exhaustive"
1689                 }
1690             }
1691         );
1692
1693         if item.is_struct() {
1694             w.write_str(
1695                 "Non-exhaustive structs could have additional fields added in future. \
1696                  Therefore, non-exhaustive structs cannot be constructed in external crates \
1697                  using the traditional <code>Struct { .. }</code> syntax; cannot be \
1698                  matched against without a wildcard <code>..</code>; and \
1699                  struct update syntax will not work.",
1700             );
1701         } else if item.is_enum() {
1702             w.write_str(
1703                 "Non-exhaustive enums could have additional variants added in future. \
1704                  Therefore, when matching against variants of non-exhaustive enums, an \
1705                  extra wildcard arm must be added to account for any future variants.",
1706             );
1707         } else if item.is_variant() {
1708             w.write_str(
1709                 "Non-exhaustive enum variants could have additional fields added in future. \
1710                  Therefore, non-exhaustive enum variants cannot be constructed in external \
1711                  crates and cannot be matched against.",
1712             );
1713         } else {
1714             w.write_str(
1715                 "This type will require a wildcard arm in any match statements or constructors.",
1716             );
1717         }
1718
1719         w.write_str("</div></details>");
1720     }
1721 }
1722
1723 fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
1724     fn write_size_of_layout(w: &mut Buffer, layout: Layout<'_>, tag_size: u64) {
1725         if layout.abi().is_unsized() {
1726             write!(w, "(unsized)");
1727         } else {
1728             let bytes = layout.size().bytes() - tag_size;
1729             write!(w, "{size} byte{pl}", size = bytes, pl = if bytes == 1 { "" } else { "s" },);
1730         }
1731     }
1732
1733     if !cx.shared.show_type_layout {
1734         return;
1735     }
1736
1737     writeln!(w, "<h2 class=\"small-section-header\">Layout</h2>");
1738     writeln!(w, "<div class=\"docblock\">");
1739
1740     let tcx = cx.tcx();
1741     let param_env = tcx.param_env(ty_def_id);
1742     let ty = tcx.type_of(ty_def_id);
1743     match tcx.layout_of(param_env.and(ty)) {
1744         Ok(ty_layout) => {
1745             writeln!(
1746                 w,
1747                 "<div class=\"warning\"><p><strong>Note:</strong> Most layout information is \
1748                  <strong>completely unstable</strong> and may even differ between compilations. \
1749                  The only exception is types with certain <code>repr(...)</code> attributes. \
1750                  Please see the Rust Reference’s \
1751                  <a href=\"https://doc.rust-lang.org/reference/type-layout.html\">“Type Layout”</a> \
1752                  chapter for details on type layout guarantees.</p></div>"
1753             );
1754             w.write_str("<p><strong>Size:</strong> ");
1755             write_size_of_layout(w, ty_layout.layout, 0);
1756             writeln!(w, "</p>");
1757             if let Variants::Multiple { variants, tag, tag_encoding, .. } =
1758                 &ty_layout.layout.variants()
1759             {
1760                 if !variants.is_empty() {
1761                     w.write_str(
1762                         "<p><strong>Size for each variant:</strong></p>\
1763                             <ul>",
1764                     );
1765
1766                     let Adt(adt, _) = ty_layout.ty.kind() else {
1767                         span_bug!(tcx.def_span(ty_def_id), "not an adt")
1768                     };
1769
1770                     let tag_size = if let TagEncoding::Niche { .. } = tag_encoding {
1771                         0
1772                     } else if let Primitive::Int(i, _) = tag.value {
1773                         i.size().bytes()
1774                     } else {
1775                         span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int")
1776                     };
1777
1778                     for (index, layout) in variants.iter_enumerated() {
1779                         let name = adt.variant(index).name;
1780                         write!(w, "<li><code>{name}</code>: ", name = name);
1781                         write_size_of_layout(w, *layout, tag_size);
1782                         writeln!(w, "</li>");
1783                     }
1784                     w.write_str("</ul>");
1785                 }
1786             }
1787         }
1788         // This kind of layout error can occur with valid code, e.g. if you try to
1789         // get the layout of a generic type such as `Vec<T>`.
1790         Err(LayoutError::Unknown(_)) => {
1791             writeln!(
1792                 w,
1793                 "<p><strong>Note:</strong> Unable to compute type layout, \
1794                  possibly due to this type having generic parameters. \
1795                  Layout can only be computed for concrete, fully-instantiated types.</p>"
1796             );
1797         }
1798         // This kind of error probably can't happen with valid code, but we don't
1799         // want to panic and prevent the docs from building, so we just let the
1800         // user know that we couldn't compute the layout.
1801         Err(LayoutError::SizeOverflow(_)) => {
1802             writeln!(
1803                 w,
1804                 "<p><strong>Note:</strong> Encountered an error during type layout; \
1805                  the type was too big.</p>"
1806             );
1807         }
1808         Err(LayoutError::NormalizationFailure(_, _)) => {
1809             writeln!(
1810                 w,
1811                 "<p><strong>Note:</strong> Encountered an error during type layout; \
1812                 the type failed to be normalized.</p>"
1813             )
1814         }
1815     }
1816
1817     writeln!(w, "</div>");
1818 }
1819
1820 fn pluralize(count: usize) -> &'static str {
1821     if count > 1 { "s" } else { "" }
1822 }