]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/render/print_item.rs
Rollup merge of #88639 - Emilgardis:fix-issue-88600, r=GuillaumeGomez
[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.iter().enumerate() {
948         if i > 0 {
949             w.write_str(",&nbsp;");
950         }
951         match *ty.kind {
952             clean::StrippedItem(box clean::StructFieldItem(_)) => w.write_str("_"),
953             clean::StructFieldItem(ref ty) => write!(w, "{}", ty.print(cx)),
954             _ => unreachable!(),
955         }
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                     match *field.kind {
1072                         clean::StrippedItem(box clean::StructFieldItem(_)) => {}
1073                         clean::StructFieldItem(ref ty) => {
1074                             let id = cx.derive_id(format!(
1075                                 "variant.{}.field.{}",
1076                                 variant.name.as_ref().unwrap(),
1077                                 field.name.as_ref().unwrap()
1078                             ));
1079                             write!(
1080                                 w,
1081                                 "<span id=\"{id}\" class=\"variant small-section-header\">\
1082                                     <a href=\"#{id}\" class=\"anchor field\"></a>\
1083                                     <code>{f}:&nbsp;{t}</code>\
1084                                 </span>",
1085                                 id = id,
1086                                 f = field.name.as_ref().unwrap(),
1087                                 t = ty.print(cx)
1088                             );
1089                             document(w, cx, field, Some(variant));
1090                         }
1091                         _ => unreachable!(),
1092                     }
1093                 }
1094                 w.write_str("</div></div>");
1095             }
1096         }
1097     }
1098     let def_id = it.def_id.expect_def_id();
1099     render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
1100     document_type_layout(w, cx, def_id);
1101 }
1102
1103 fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) {
1104     wrap_into_docblock(w, |w| {
1105         highlight::render_with_highlighting(
1106             &t.source,
1107             w,
1108             Some("macro"),
1109             None,
1110             None,
1111             it.span(cx.tcx()).inner().edition(),
1112             None,
1113             None,
1114         );
1115     });
1116     document(w, cx, it, None)
1117 }
1118
1119 fn item_proc_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) {
1120     let name = it.name.as_ref().expect("proc-macros always have names");
1121     match m.kind {
1122         MacroKind::Bang => {
1123             wrap_item(w, "macro", |w| {
1124                 write!(w, "{}!() {{ /* proc-macro */ }}", name);
1125             });
1126         }
1127         MacroKind::Attr => {
1128             wrap_item(w, "attr", |w| {
1129                 write!(w, "#[{}]", name);
1130             });
1131         }
1132         MacroKind::Derive => {
1133             wrap_item(w, "derive", |w| {
1134                 write!(w, "#[derive({})]", name);
1135                 if !m.helpers.is_empty() {
1136                     w.push_str("\n{\n");
1137                     w.push_str("    // Attributes available to this derive:\n");
1138                     for attr in &m.helpers {
1139                         writeln!(w, "    #[{}]", attr);
1140                     }
1141                     w.push_str("}\n");
1142                 }
1143             });
1144         }
1145     }
1146     document(w, cx, it, None)
1147 }
1148
1149 fn item_primitive(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
1150     document(w, cx, it, None);
1151     render_assoc_items(w, cx, it, it.def_id.expect_def_id(), AssocItemRender::All)
1152 }
1153
1154 fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::Constant) {
1155     wrap_item(w, "const", |w| {
1156         render_attributes_in_code(w, it);
1157
1158         write!(
1159             w,
1160             "{vis}const {name}: {typ}",
1161             vis = it.visibility.print_with_space(it.def_id, cx),
1162             name = it.name.as_ref().unwrap(),
1163             typ = c.type_.print(cx),
1164         );
1165
1166         let value = c.value(cx.tcx());
1167         let is_literal = c.is_literal(cx.tcx());
1168         let expr = c.expr(cx.tcx());
1169         if value.is_some() || is_literal {
1170             write!(w, " = {expr};", expr = Escape(&expr));
1171         } else {
1172             w.write_str(";");
1173         }
1174
1175         if !is_literal {
1176             if let Some(value) = &value {
1177                 let value_lowercase = value.to_lowercase();
1178                 let expr_lowercase = expr.to_lowercase();
1179
1180                 if value_lowercase != expr_lowercase
1181                     && value_lowercase.trim_end_matches("i32") != expr_lowercase
1182                 {
1183                     write!(w, " // {value}", value = Escape(value));
1184                 }
1185             }
1186         }
1187     });
1188
1189     document(w, cx, it, None)
1190 }
1191
1192 fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) {
1193     wrap_into_docblock(w, |w| {
1194         wrap_item(w, "struct", |w| {
1195             render_attributes_in_code(w, it);
1196             render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx);
1197         });
1198     });
1199
1200     document(w, cx, it, None);
1201
1202     let mut fields = s
1203         .fields
1204         .iter()
1205         .filter_map(|f| match *f.kind {
1206             clean::StructFieldItem(ref ty) => Some((f, ty)),
1207             _ => None,
1208         })
1209         .peekable();
1210     if let CtorKind::Fictive | CtorKind::Fn = s.struct_type {
1211         if fields.peek().is_some() {
1212             write!(
1213                 w,
1214                 "<h2 id=\"fields\" class=\"fields small-section-header\">\
1215                      {}{}<a href=\"#fields\" class=\"anchor\"></a>\
1216                  </h2>",
1217                 if let CtorKind::Fictive = s.struct_type { "Fields" } else { "Tuple Fields" },
1218                 document_non_exhaustive_header(it)
1219             );
1220             document_non_exhaustive(w, it);
1221             for (index, (field, ty)) in fields.enumerate() {
1222                 let field_name =
1223                     field.name.map_or_else(|| index.to_string(), |sym| (*sym.as_str()).to_string());
1224                 let id = cx.derive_id(format!("{}.{}", ItemType::StructField, field_name));
1225                 write!(
1226                     w,
1227                     "<span id=\"{id}\" class=\"{item_type} small-section-header\">\
1228                          <a href=\"#{id}\" class=\"anchor field\"></a>\
1229                          <code>{name}: {ty}</code>\
1230                      </span>",
1231                     item_type = ItemType::StructField,
1232                     id = id,
1233                     name = field_name,
1234                     ty = ty.print(cx)
1235                 );
1236                 document(w, cx, field, Some(it));
1237             }
1238         }
1239     }
1240     let def_id = it.def_id.expect_def_id();
1241     render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
1242     document_type_layout(w, cx, def_id);
1243 }
1244
1245 fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) {
1246     wrap_item(w, "static", |w| {
1247         render_attributes_in_code(w, it);
1248         write!(
1249             w,
1250             "{vis}static {mutability}{name}: {typ}",
1251             vis = it.visibility.print_with_space(it.def_id, cx),
1252             mutability = s.mutability.print_with_space(),
1253             name = it.name.as_ref().unwrap(),
1254             typ = s.type_.print(cx)
1255         );
1256     });
1257     document(w, cx, it, None)
1258 }
1259
1260 fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
1261     wrap_item(w, "foreigntype", |w| {
1262         w.write_str("extern {\n");
1263         render_attributes_in_code(w, it);
1264         write!(
1265             w,
1266             "    {}type {};\n}}",
1267             it.visibility.print_with_space(it.def_id, cx),
1268             it.name.as_ref().unwrap(),
1269         );
1270     });
1271
1272     document(w, cx, it, None);
1273
1274     render_assoc_items(w, cx, it, it.def_id.expect_def_id(), AssocItemRender::All)
1275 }
1276
1277 fn item_keyword(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
1278     document(w, cx, it, None)
1279 }
1280
1281 /// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order).
1282 crate fn compare_names(mut lhs: &str, mut rhs: &str) -> Ordering {
1283     /// Takes a non-numeric and a numeric part from the given &str.
1284     fn take_parts<'a>(s: &mut &'a str) -> (&'a str, &'a str) {
1285         let i = s.find(|c: char| c.is_ascii_digit());
1286         let (a, b) = s.split_at(i.unwrap_or(s.len()));
1287         let i = b.find(|c: char| !c.is_ascii_digit());
1288         let (b, c) = b.split_at(i.unwrap_or(b.len()));
1289         *s = c;
1290         (a, b)
1291     }
1292
1293     while !lhs.is_empty() || !rhs.is_empty() {
1294         let (la, lb) = take_parts(&mut lhs);
1295         let (ra, rb) = take_parts(&mut rhs);
1296         // First process the non-numeric part.
1297         match la.cmp(ra) {
1298             Ordering::Equal => (),
1299             x => return x,
1300         }
1301         // Then process the numeric part, if both sides have one (and they fit in a u64).
1302         if let (Ok(ln), Ok(rn)) = (lb.parse::<u64>(), rb.parse::<u64>()) {
1303             match ln.cmp(&rn) {
1304                 Ordering::Equal => (),
1305                 x => return x,
1306             }
1307         }
1308         // Then process the numeric part again, but this time as strings.
1309         match lb.cmp(rb) {
1310             Ordering::Equal => (),
1311             x => return x,
1312         }
1313     }
1314
1315     Ordering::Equal
1316 }
1317
1318 pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
1319     let mut s = cx.current.join("::");
1320     s.push_str("::");
1321     s.push_str(&item.name.unwrap().as_str());
1322     s
1323 }
1324
1325 pub(super) fn item_path(ty: ItemType, name: &str) -> String {
1326     match ty {
1327         ItemType::Module => format!("{}index.html", ensure_trailing_slash(name)),
1328         _ => format!("{}.{}.html", ty, name),
1329     }
1330 }
1331
1332 fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) -> String {
1333     let mut bounds = String::new();
1334     if !t_bounds.is_empty() {
1335         if !trait_alias {
1336             bounds.push_str(": ");
1337         }
1338         for (i, p) in t_bounds.iter().enumerate() {
1339             if i > 0 {
1340                 bounds.push_str(" + ");
1341             }
1342             bounds.push_str(&p.print(cx).to_string());
1343         }
1344     }
1345     bounds
1346 }
1347
1348 fn wrap_into_docblock<F>(w: &mut Buffer, f: F)
1349 where
1350     F: FnOnce(&mut Buffer),
1351 {
1352     w.write_str("<div class=\"docblock type-decl\">");
1353     f(w);
1354     w.write_str("</div>")
1355 }
1356
1357 fn wrap_item<F>(w: &mut Buffer, item_name: &str, f: F)
1358 where
1359     F: FnOnce(&mut Buffer),
1360 {
1361     w.write_fmt(format_args!("<pre class=\"rust {}\"><code>", item_name));
1362     f(w);
1363     w.write_str("</code></pre>");
1364 }
1365
1366 fn render_stability_since(
1367     w: &mut Buffer,
1368     item: &clean::Item,
1369     containing_item: &clean::Item,
1370     tcx: TyCtxt<'_>,
1371 ) {
1372     render_stability_since_raw(
1373         w,
1374         item.stable_since(tcx).as_deref(),
1375         item.const_stability(tcx),
1376         containing_item.stable_since(tcx).as_deref(),
1377         containing_item.const_stable_since(tcx).as_deref(),
1378     )
1379 }
1380
1381 fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cx: &Context<'_>) -> Ordering {
1382     let lhss = format!("{}", lhs.inner_impl().print(false, cx));
1383     let rhss = format!("{}", rhs.inner_impl().print(false, cx));
1384
1385     // lhs and rhs are formatted as HTML, which may be unnecessary
1386     compare_names(&lhss, &rhss)
1387 }
1388
1389 fn render_implementor(
1390     cx: &Context<'_>,
1391     implementor: &Impl,
1392     trait_: &clean::Item,
1393     w: &mut Buffer,
1394     implementor_dups: &FxHashMap<Symbol, (DefId, bool)>,
1395     aliases: &[String],
1396 ) {
1397     // If there's already another implementor that has the same abridged name, use the
1398     // full path, for example in `std::iter::ExactSizeIterator`
1399     let use_absolute = match implementor.inner_impl().for_ {
1400         clean::ResolvedPath { ref path, is_generic: false, .. }
1401         | clean::BorrowedRef {
1402             type_: box clean::ResolvedPath { ref path, is_generic: false, .. },
1403             ..
1404         } => implementor_dups[&path.last()].1,
1405         _ => false,
1406     };
1407     render_impl(
1408         w,
1409         cx,
1410         implementor,
1411         trait_,
1412         AssocItemLink::Anchor(None),
1413         RenderMode::Normal,
1414         Some(use_absolute),
1415         aliases,
1416         ImplRenderingParameters {
1417             show_def_docs: false,
1418             is_on_foreign_type: false,
1419             show_default_items: false,
1420             show_non_assoc_items: false,
1421             toggle_open_by_default: false,
1422         },
1423     );
1424 }
1425
1426 fn render_union(
1427     w: &mut Buffer,
1428     it: &clean::Item,
1429     g: Option<&clean::Generics>,
1430     fields: &[clean::Item],
1431     tab: &str,
1432     cx: &Context<'_>,
1433 ) {
1434     write!(
1435         w,
1436         "{}union {}",
1437         it.visibility.print_with_space(it.def_id, cx),
1438         it.name.as_ref().unwrap()
1439     );
1440     if let Some(g) = g {
1441         write!(w, "{}", g.print(cx));
1442         write!(w, "{}", print_where_clause(&g, cx, 0, true));
1443     }
1444
1445     write!(w, " {{\n{}", tab);
1446     let count_fields =
1447         fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count();
1448     let toggle = should_hide_fields(count_fields);
1449     if toggle {
1450         toggle_open(w, format_args!("{} fields", count_fields));
1451     }
1452
1453     for field in fields {
1454         if let clean::StructFieldItem(ref ty) = *field.kind {
1455             write!(
1456                 w,
1457                 "    {}{}: {},\n{}",
1458                 field.visibility.print_with_space(field.def_id, cx),
1459                 field.name.as_ref().unwrap(),
1460                 ty.print(cx),
1461                 tab
1462             );
1463         }
1464     }
1465
1466     if it.has_stripped_fields().unwrap() {
1467         write!(w, "    // some fields omitted\n{}", tab);
1468     }
1469     if toggle {
1470         toggle_close(w);
1471     }
1472     w.write_str("}");
1473 }
1474
1475 fn render_struct(
1476     w: &mut Buffer,
1477     it: &clean::Item,
1478     g: Option<&clean::Generics>,
1479     ty: CtorKind,
1480     fields: &[clean::Item],
1481     tab: &str,
1482     structhead: bool,
1483     cx: &Context<'_>,
1484 ) {
1485     write!(
1486         w,
1487         "{}{}{}",
1488         it.visibility.print_with_space(it.def_id, cx),
1489         if structhead { "struct " } else { "" },
1490         it.name.as_ref().unwrap()
1491     );
1492     if let Some(g) = g {
1493         write!(w, "{}", g.print(cx))
1494     }
1495     match ty {
1496         CtorKind::Fictive => {
1497             if let Some(g) = g {
1498                 write!(w, "{}", print_where_clause(g, cx, 0, true),)
1499             }
1500             w.write_str(" {");
1501             let count_fields =
1502                 fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count();
1503             let has_visible_fields = count_fields > 0;
1504             let toggle = should_hide_fields(count_fields);
1505             if toggle {
1506                 toggle_open(w, format_args!("{} fields", count_fields));
1507             }
1508             for field in fields {
1509                 if let clean::StructFieldItem(ref ty) = *field.kind {
1510                     write!(
1511                         w,
1512                         "\n{}    {}{}: {},",
1513                         tab,
1514                         field.visibility.print_with_space(field.def_id, cx),
1515                         field.name.as_ref().unwrap(),
1516                         ty.print(cx),
1517                     );
1518                 }
1519             }
1520
1521             if has_visible_fields {
1522                 if it.has_stripped_fields().unwrap() {
1523                     write!(w, "\n{}    // some fields omitted", tab);
1524                 }
1525                 write!(w, "\n{}", tab);
1526             } else if it.has_stripped_fields().unwrap() {
1527                 // If there are no visible fields we can just display
1528                 // `{ /* fields omitted */ }` to save space.
1529                 write!(w, " /* fields omitted */ ");
1530             }
1531             if toggle {
1532                 toggle_close(w);
1533             }
1534             w.write_str("}");
1535         }
1536         CtorKind::Fn => {
1537             w.write_str("(");
1538             for (i, field) in fields.iter().enumerate() {
1539                 if i > 0 {
1540                     w.write_str(", ");
1541                 }
1542                 match *field.kind {
1543                     clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
1544                     clean::StructFieldItem(ref ty) => {
1545                         write!(
1546                             w,
1547                             "{}{}",
1548                             field.visibility.print_with_space(field.def_id, cx),
1549                             ty.print(cx),
1550                         )
1551                     }
1552                     _ => unreachable!(),
1553                 }
1554             }
1555             w.write_str(")");
1556             if let Some(g) = g {
1557                 write!(w, "{}", print_where_clause(g, cx, 0, false),)
1558             }
1559             // We only want a ";" when we are displaying a tuple struct, not a variant tuple struct.
1560             if structhead {
1561                 w.write_str(";");
1562             }
1563         }
1564         CtorKind::Const => {
1565             // Needed for PhantomData.
1566             if let Some(g) = g {
1567                 write!(w, "{}", print_where_clause(g, cx, 0, false),)
1568             }
1569             w.write_str(";");
1570         }
1571     }
1572 }
1573
1574 fn document_non_exhaustive_header(item: &clean::Item) -> &str {
1575     if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
1576 }
1577
1578 fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) {
1579     if item.is_non_exhaustive() {
1580         write!(
1581             w,
1582             "<details class=\"rustdoc-toggle non-exhaustive\">\
1583                  <summary class=\"hideme\"><span>{}</span></summary>\
1584                  <div class=\"docblock\">",
1585             {
1586                 if item.is_struct() {
1587                     "This struct is marked as non-exhaustive"
1588                 } else if item.is_enum() {
1589                     "This enum is marked as non-exhaustive"
1590                 } else if item.is_variant() {
1591                     "This variant is marked as non-exhaustive"
1592                 } else {
1593                     "This type is marked as non-exhaustive"
1594                 }
1595             }
1596         );
1597
1598         if item.is_struct() {
1599             w.write_str(
1600                 "Non-exhaustive structs could have additional fields added in future. \
1601                  Therefore, non-exhaustive structs cannot be constructed in external crates \
1602                  using the traditional <code>Struct { .. }</code> syntax; cannot be \
1603                  matched against without a wildcard <code>..</code>; and \
1604                  struct update syntax will not work.",
1605             );
1606         } else if item.is_enum() {
1607             w.write_str(
1608                 "Non-exhaustive enums could have additional variants added in future. \
1609                  Therefore, when matching against variants of non-exhaustive enums, an \
1610                  extra wildcard arm must be added to account for any future variants.",
1611             );
1612         } else if item.is_variant() {
1613             w.write_str(
1614                 "Non-exhaustive enum variants could have additional fields added in future. \
1615                  Therefore, non-exhaustive enum variants cannot be constructed in external \
1616                  crates and cannot be matched against.",
1617             );
1618         } else {
1619             w.write_str(
1620                 "This type will require a wildcard arm in any match statements or constructors.",
1621             );
1622         }
1623
1624         w.write_str("</div></details>");
1625     }
1626 }
1627
1628 fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
1629     fn write_size_of_layout(w: &mut Buffer, layout: &Layout, tag_size: u64) {
1630         if layout.abi.is_unsized() {
1631             write!(w, "(unsized)");
1632         } else {
1633             let bytes = layout.size.bytes() - tag_size;
1634             write!(w, "{size} byte{pl}", size = bytes, pl = if bytes == 1 { "" } else { "s" },);
1635         }
1636     }
1637
1638     if !cx.shared.show_type_layout {
1639         return;
1640     }
1641
1642     writeln!(w, "<h2 class=\"small-section-header\">Layout</h2>");
1643     writeln!(w, "<div class=\"docblock\">");
1644
1645     let tcx = cx.tcx();
1646     let param_env = tcx.param_env(ty_def_id);
1647     let ty = tcx.type_of(ty_def_id);
1648     match tcx.layout_of(param_env.and(ty)) {
1649         Ok(ty_layout) => {
1650             writeln!(
1651                 w,
1652                 "<div class=\"warning\"><p><strong>Note:</strong> Most layout information is \
1653                  completely unstable and may be different between compiler versions and platforms. \
1654                  The only exception is types with certain <code>repr(...)</code> attributes. \
1655                  Please see the Rust Reference’s \
1656                  <a href=\"https://doc.rust-lang.org/reference/type-layout.html\">“Type Layout”</a> \
1657                  chapter for details on type layout guarantees.</p></div>"
1658             );
1659             w.write_str("<p><strong>Size:</strong> ");
1660             write_size_of_layout(w, ty_layout.layout, 0);
1661             writeln!(w, "</p>");
1662             if let Variants::Multiple { variants, tag, tag_encoding, .. } =
1663                 &ty_layout.layout.variants
1664             {
1665                 if !variants.is_empty() {
1666                     w.write_str(
1667                         "<p><strong>Size for each variant:</strong></p>\
1668                             <ul>",
1669                     );
1670
1671                     let adt = if let Adt(adt, _) = ty_layout.ty.kind() {
1672                         adt
1673                     } else {
1674                         span_bug!(tcx.def_span(ty_def_id), "not an adt")
1675                     };
1676
1677                     let tag_size = if let TagEncoding::Niche { .. } = tag_encoding {
1678                         0
1679                     } else if let Primitive::Int(i, _) = tag.value {
1680                         i.size().bytes()
1681                     } else {
1682                         span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int")
1683                     };
1684
1685                     for (index, layout) in variants.iter_enumerated() {
1686                         let ident = adt.variants[index].ident;
1687                         write!(w, "<li><code>{name}</code>: ", name = ident);
1688                         write_size_of_layout(w, layout, tag_size);
1689                         writeln!(w, "</li>");
1690                     }
1691                     w.write_str("</ul>");
1692                 }
1693             }
1694         }
1695         // This kind of layout error can occur with valid code, e.g. if you try to
1696         // get the layout of a generic type such as `Vec<T>`.
1697         Err(LayoutError::Unknown(_)) => {
1698             writeln!(
1699                 w,
1700                 "<p><strong>Note:</strong> Unable to compute type layout, \
1701                  possibly due to this type having generic parameters. \
1702                  Layout can only be computed for concrete, fully-instantiated types.</p>"
1703             );
1704         }
1705         // This kind of error probably can't happen with valid code, but we don't
1706         // want to panic and prevent the docs from building, so we just let the
1707         // user know that we couldn't compute the layout.
1708         Err(LayoutError::SizeOverflow(_)) => {
1709             writeln!(
1710                 w,
1711                 "<p><strong>Note:</strong> Encountered an error during type layout; \
1712                  the type was too big.</p>"
1713             );
1714         }
1715     }
1716
1717     writeln!(w, "</div>");
1718 }
1719
1720 fn pluralize(count: usize) -> &'static str {
1721     if count > 1 { "s" } else { "" }
1722 }