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