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