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