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