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