1 use std::cmp::Ordering;
3 use rustc_data_structures::fx::FxHashMap;
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};
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,
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;
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) => {
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 ",
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 ",
58 // We don't generate pages for any other type.
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) {
69 "<a href=\"{}index.html\">{}</a>::<wbr>",
70 "../".repeat(cur.len() - i - 1),
75 write!(buf, "<a class=\"{}\" href=\"\">{}</a>", item.type_(), item.name.as_ref().unwrap());
77 buf.write_str("</span>"); // in-band
78 buf.write_str("<span class=\"out-of-band\">");
79 render_stability_since_raw(
81 item.stable_since(cx.tcx()).as_deref(),
82 item.const_stable_since(cx.tcx()).as_deref(),
87 "<span id=\"render-detail\">\
88 <a id=\"toggle-all-docs\" href=\"javascript:void(0)\" \
89 title=\"collapse all docs\">\
90 [<span class=\"inner\">−</span>]\
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);
105 buf.write_str("</span></h1>"); // out-of-band
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)
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),
127 // We don't generate pages for any other type.
133 fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) {
134 document(w, cx, item, None);
136 let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::<Vec<usize>>();
138 // the order of item types in the listing
139 fn reorder(ty: ItemType) -> u8 {
141 ItemType::ExternCrate => 0,
142 ItemType::Import => 1,
143 ItemType::Primitive => 2,
144 ItemType::Module => 3,
145 ItemType::Macro => 4,
146 ItemType::Struct => 5,
148 ItemType::Constant => 7,
149 ItemType::Static => 8,
150 ItemType::Trait => 9,
151 ItemType::Function => 10,
152 ItemType::Typedef => 12,
153 ItemType::Union => 13,
165 let ty1 = i1.type_();
166 let ty2 = i2.type_();
168 return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2));
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,
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)
184 if cx.shared.sort_modules_alphabetically {
185 indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2, cx.tcx()));
187 // This call is to remove re-export duplicates in cases such as:
192 // crate trait Double { fn foo(); }
196 // crate use foo::bar::*;
200 // `Double` will appear twice in the generated docs.
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| {
209 if items[*i].name.as_ref().is_some() { Some(full_path(cx, &items[*i])) } else { None },
211 if items[*i].is_import() { *i } else { 0 },
215 debug!("{:?}", indices);
216 let mut curty = None;
217 for &idx in &indices {
218 let myitem = &items[idx];
219 if myitem.is_stripped() {
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.
227 } else if myty != curty {
229 w.write_str("</table>");
232 let (short, name) = item_ty_to_strs(&myty.unwrap());
235 "<h2 id=\"{id}\" class=\"section-header\">\
236 <a href=\"#{id}\">{name}</a></h2>\n<table>",
237 id = cx.derive_id(short.to_owned()),
243 clean::ExternCrateItem { ref src } => {
244 use crate::html::format::anchor;
247 Some(ref src) => write!(
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(),
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()),
261 w.write_str("</code></td></tr>");
264 clean::ImportItem(ref import) => {
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()),
274 if myitem.name.is_none() {
278 let unsafety_flag = match *myitem.kind {
279 clean::FunctionItem(ref func) | clean::ForeignFunctionItem(ref func)
280 if func.header.unsafety == hir::Unsafety::Unsafe =>
282 "<a title=\"unsafe function\" href=\"#\"><sup>âš </sup></a>"
287 let stab = myitem.stability_class(cx.tcx());
288 let add = if stab.is_some() { " " } else { "" };
290 let doc_value = myitem.doc_value().unwrap_or_default();
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>\
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_(),
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()]
308 .filter_map(|s| if !s.is_empty() { Some(s.as_str()) } else { None })
317 w.write_str("</table>");
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();
326 fn tag_html(class: &str, title: &str, contents: &str) -> String {
327 format!(r#"<span class="stab {}" title="{}">{}</span>"#, class, Escape(title), contents)
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(),
337 message = "Deprecation planned";
339 tags += &tag_html("deprecated", "", message);
342 // The "rustc_private" crates are permanently unstable so it makes no sense
343 // to render "unstable" everywhere.
347 .map(|s| s.level.is_unstable() && s.feature != sym::rustc_private)
350 tags += &tag_html("unstable", "", "Experimental");
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(),
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());
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())
378 w.write_str("<pre class=\"rust fn\">");
379 render_attributes(w, it, false);
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()),
395 document(w, cx, it, None)
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<_>>();
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);
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()),
420 if !t.generics.where_predicates.is_empty() {
421 write!(w, "{}", print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true));
426 if t.items.is_empty() {
429 // FIXME: we should be using a derived_id for the Anchors here
432 render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
435 if !types.is_empty() && !consts.is_empty() {
439 render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
442 if !consts.is_empty() && !required.is_empty() {
445 for (pos, m) in required.iter().enumerate() {
446 render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx);
449 if pos < required.len() - 1 {
450 w.write_str("<div class=\"item-spacer\"></div>");
453 if !required.is_empty() && !provided.is_empty() {
456 for (pos, m) in provided.iter().enumerate() {
457 render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx);
459 clean::MethodItem(ref inner, _)
460 if !inner.generics.where_predicates.is_empty() =>
462 w.write_str(",\n { ... }\n");
465 w.write_str(" { ... }\n");
468 if pos < provided.len() - 1 {
469 w.write_str("<div class=\"item-spacer\"></div>");
474 w.write_str("</pre>")
477 // Trait documentation
478 document(w, cx, it, None);
480 fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) {
483 "<h2 id=\"{0}\" class=\"small-section-header\">\
484 {1}<a href=\"#{0}\" class=\"anchor\"></a>\
486 id, title, extra_content
490 fn write_loading_content(w: &mut Buffer, extra_content: &str) {
491 write!(w, "{}<span class=\"loading-content\">Loading content...</span>", extra_content)
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));
508 if !types.is_empty() {
509 write_small_section_header(
513 "<div class=\"methods\">",
516 trait_item(w, cx, t, it);
518 write_loading_content(w, "</div>");
521 if !consts.is_empty() {
522 write_small_section_header(
525 "Associated Constants",
526 "<div class=\"methods\">",
529 trait_item(w, cx, t, it);
531 write_loading_content(w, "</div>");
534 // Output the documentation for each function individually
535 if !required.is_empty() {
536 write_small_section_header(
540 "<div class=\"methods\">",
543 trait_item(w, cx, m, it);
545 write_loading_content(w, "</div>");
547 if !provided.is_empty() {
548 write_small_section_header(
552 "<div class=\"methods\">",
555 trait_item(w, cx, m, it);
557 write_loading_content(w, "</div>");
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);
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, .. },
574 let &mut (prev_did, ref mut has_duplicates) =
575 implementor_dups.entry(path.last()).or_insert((did, false));
577 *has_duplicates = true;
584 let (local, foreign) = implementors.iter().partition::<Vec<_>, _>(|i| {
587 .def_id_full(cx.cache())
588 .map_or(true, |d| cx.cache.paths.contains_key(&d))
591 let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) =
592 local.iter().partition(|i| i.inner_impl().synthetic);
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()));
597 if !foreign.is_empty() {
598 write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", "");
600 for implementor in foreign {
601 let assoc_link = AssocItemLink::GotoSource(
602 implementor.impl_item.def_id,
603 &implementor.inner_impl().provided_trait_methods,
612 implementor.impl_item.stable_since(cx.tcx()).as_deref(),
613 implementor.impl_item.const_stable_since(cx.tcx()).as_deref(),
621 write_loading_content(w, "");
624 write_small_section_header(
628 "<div class=\"item-list\" id=\"implementors-list\">",
630 for implementor in concrete {
631 render_implementor(cx, implementor, it, w, &implementor_dups, &[]);
633 write_loading_content(w, "</div>");
636 write_small_section_header(
638 "synthetic-implementors",
640 "<div class=\"item-list\" id=\"synthetic-implementors-list\">",
642 for implementor in synthetic {
649 &collect_paths_for_type(implementor.inner_impl().for_.clone(), &cx.cache),
652 write_loading_content(w, "</div>");
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(
661 "<div class=\"item-list\" id=\"implementors-list\">",
663 write_loading_content(w, "</div>");
666 write_small_section_header(
668 "synthetic-implementors",
670 "<div class=\"item-list\" id=\"synthetic-implementors-list\">",
672 write_loading_content(w, "</div>");
678 "<script type=\"text/javascript\" \
679 src=\"{root_path}/implementors/{path}/{ty}.{name}.js\" async>\
681 root_path = vec![".."; cx.current.len()].join("/"),
682 path = if it.def_id.is_local() {
685 let (ref path, _) = cx.cache.external_paths[&it.def_id];
686 path[..path.len() - 1].join("/")
689 name = *it.name.as_ref().unwrap()
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);
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())
705 document(w, cx, it, None);
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)
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);
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()),
726 document(w, cx, it, None);
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)
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);
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()),
747 document(w, cx, it, None);
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)
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>")
764 document(w, cx, it, None);
768 .filter_map(|f| match *f.kind {
769 clean::StructFieldItem(ref ty) => Some((f, ty)),
773 if fields.peek().is_some() {
776 "<h2 id=\"fields\" class=\"fields small-section-header\">
777 Fields<a href=\"#fields\" class=\"anchor\"></a></h2>"
779 for (field, ty) in fields {
780 let name = field.name.as_ref().expect("union field name");
781 let id = format!("{}.{}", ItemType::StructField, name);
784 "<span id=\"{id}\" class=\"{shortty} small-section-header\">\
785 <a href=\"#{id}\" class=\"anchor field\"></a>\
786 <code>{name}: {ty}</code>\
790 shortty = ItemType::StructField,
791 ty = ty.print(cx.cache(), cx.tcx()),
793 if let Some(stability_class) = field.stability_class(cx.tcx()) {
794 write!(w, "<span class=\"stab {stab}\"></span>", stab = stability_class);
796 document(w, cx, field, Some(it));
799 render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
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);
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),
814 if e.variants.is_empty() && !e.variants_stripped {
818 for v in &e.variants {
820 let name = v.name.as_ref().unwrap();
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() {
828 w.write_str(", ")
830 write!(w, "{}", ty.print(cx.cache(), cx.tcx()));
834 clean::Variant::Struct(ref s) => {
835 render_struct(w, v, None, s.struct_type, &s.fields, " ", false, cx);
843 if e.variants_stripped {
844 w.write_str(" // some variants omitted\n");
848 w.write_str("</pre>")
851 document(w, cx, it, None);
852 if !e.variants.is_empty() {
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)
859 document_non_exhaustive(w, it);
860 for variant in &e.variants {
862 cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.as_ref().unwrap()));
865 "<div id=\"{id}\" class=\"variant small-section-header\">\
866 <a href=\"#{id}\" class=\"anchor field\"></a>\
869 name = variant.name.as_ref().unwrap()
871 if let clean::VariantItem(clean::Variant::Tuple(ref tys)) = *variant.kind {
873 for (i, ty) in tys.iter().enumerate() {
875 w.write_str(", ");
877 write!(w, "{}", ty.print(cx.cache(), cx.tcx()));
881 w.write_str("</code></div>");
882 document(w, cx, variant, Some(it));
883 document_non_exhaustive(w, variant);
885 use crate::clean::Variant;
886 if let clean::VariantItem(Variant::Struct(ref s)) = *variant.kind {
887 let variant_id = cx.derive_id(format!(
890 variant.name.as_ref().unwrap()
892 write!(w, "<div class=\"autohide sub-variant\" id=\"{id}\">", id = variant_id);
895 "<h3>Fields of <b>{name}</b></h3><div>",
896 name = variant.name.as_ref().unwrap()
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()
908 "<span id=\"{id}\" class=\"variant small-section-header\">\
909 <a href=\"#{id}\" class=\"anchor field\"></a>\
910 <code>{f}: {t}</code>\
913 f = field.name.as_ref().unwrap(),
914 t = ty.print(cx.cache(), cx.tcx())
916 document(w, cx, field, Some(variant));
919 w.write_str("</div></div>");
921 render_stability_since(w, variant, it, cx.tcx());
924 render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
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(
935 it.span.inner().edition(),
938 document(w, cx, it, None)
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");
945 w.push_str("<pre class=\"rust macro\">");
946 write!(w, "{}!() {{ /* proc-macro */ }}", name);
947 w.push_str("</pre>");
950 w.push_str("<pre class=\"rust attr\">");
951 write!(w, "#[{}]", name);
952 w.push_str("</pre>");
954 MacroKind::Derive => {
955 w.push_str("<pre class=\"rust derive\">");
956 write!(w, "#[derive({})]", name);
957 if !m.helpers.is_empty() {
959 w.push_str(" // Attributes available to this derive:\n");
960 for attr in &m.helpers {
961 writeln!(w, " #[{}]", attr);
965 w.push_str("</pre>");
968 document(w, cx, it, None)
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)
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);
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()),
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));
998 if let Some(value) = &value {
999 let value_lowercase = value.to_lowercase();
1000 let expr_lowercase = expr.to_lowercase();
1002 if value_lowercase != expr_lowercase
1003 && value_lowercase.trim_end_matches("i32") != expr_lowercase
1005 write!(w, " // {value}", value = Escape(value));
1010 w.write_str("</pre>");
1011 document(w, cx, it, None)
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>")
1022 document(w, cx, it, None);
1026 .filter_map(|f| match *f.kind {
1027 clean::StructFieldItem(ref ty) => Some((f, ty)),
1031 if let CtorKind::Fictive = s.struct_type {
1032 if fields.peek().is_some() {
1035 "<h2 id=\"fields\" class=\"fields small-section-header\">
1036 Fields{}<a href=\"#fields\" class=\"anchor\"></a></h2>",
1037 document_non_exhaustive_header(it)
1039 document_non_exhaustive(w, it);
1040 for (field, ty) in fields {
1041 let id = cx.derive_id(format!(
1043 ItemType::StructField,
1044 field.name.as_ref().unwrap()
1048 "<span id=\"{id}\" class=\"{item_type} small-section-header\">\
1049 <a href=\"#{id}\" class=\"anchor field\"></a>\
1050 <code>{name}: {ty}</code>\
1052 item_type = ItemType::StructField,
1054 name = field.name.as_ref().unwrap(),
1055 ty = ty.print(cx.cache(), cx.tcx())
1057 document(w, cx, field, Some(it));
1061 render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
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);
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())
1075 document(w, cx, it, None)
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);
1083 " {}type {};\n}}</pre>",
1084 it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
1085 it.name.as_ref().unwrap(),
1088 document(w, cx, it, None);
1090 render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
1093 fn item_keyword(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
1094 document(w, cx, it, None)
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()));
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.
1114 Ordering::Equal => (),
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>()) {
1120 Ordering::Equal => (),
1124 // Then process the numeric part again, but this time as strings.
1126 Ordering::Equal => (),
1134 pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
1135 let mut s = cx.current.join("::");
1137 s.push_str(&item.name.unwrap().as_str());
1141 pub(super) fn item_path(ty: ItemType, name: &str) -> String {
1143 ItemType::Module => format!("{}index.html", ensure_trailing_slash(name)),
1144 _ => format!("{}.{}.html", ty, name),
1149 t_bounds: &[clean::GenericBound],
1154 let mut bounds = String::new();
1155 if !t_bounds.is_empty() {
1157 bounds.push_str(": ");
1159 for (i, p) in t_bounds.iter().enumerate() {
1161 bounds.push_str(" + ");
1163 bounds.push_str(&p.print(cache, tcx).to_string());
1169 fn wrap_into_docblock<F>(w: &mut Buffer, f: F)
1171 F: FnOnce(&mut Buffer),
1173 w.write_str("<div class=\"docblock type-decl hidden-by-usual-hider\">");
1175 w.write_str("</div>")
1178 fn render_stability_since(
1181 containing_item: &clean::Item,
1184 render_stability_since_raw(
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(),
1193 fn compare_impl<'a, 'b>(
1199 let lhs = format!("{}", lhs.inner_impl().print(cache, false, tcx));
1200 let rhs = format!("{}", rhs.inner_impl().print(cache, false, tcx));
1202 // lhs and rhs are formatted as HTML, which may be unnecessary
1203 compare_names(&lhs, &rhs)
1206 fn render_implementor(
1209 trait_: &clean::Item,
1211 implementor_dups: &FxHashMap<Symbol, (DefId, bool)>,
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, .. },
1221 } => implementor_dups[&path.last()].1,
1229 AssocItemLink::Anchor(None),
1231 trait_.stable_since(cx.tcx()).as_deref(),
1232 trait_.const_stable_since(cx.tcx()).as_deref(),
1244 g: Option<&clean::Generics>,
1245 fields: &[clean::Item],
1253 it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
1254 if structhead { "union " } else { "" },
1255 it.name.as_ref().unwrap()
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));
1262 write!(w, " {{\n{}", tab);
1263 for field in fields {
1264 if let clean::StructFieldItem(ref ty) = *field.kind {
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()),
1276 if it.has_stripped_fields().unwrap() {
1277 write!(w, " // some fields omitted\n{}", tab);
1285 g: Option<&clean::Generics>,
1287 fields: &[clean::Item],
1295 it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
1296 if structhead { "struct " } else { "" },
1297 it.name.as_ref().unwrap()
1299 if let Some(g) = g {
1300 write!(w, "{}", g.print(cx.cache(), cx.tcx()))
1303 CtorKind::Fictive => {
1304 if let Some(g) = g {
1305 write!(w, "{}", print_where_clause(g, cx.cache(), cx.tcx(), 0, true),)
1307 let mut has_visible_fields = false;
1309 for field in fields {
1310 if let clean::StructFieldItem(ref ty) = *field.kind {
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()),
1319 has_visible_fields = true;
1323 if has_visible_fields {
1324 if it.has_stripped_fields().unwrap() {
1325 write!(w, "\n{} // some fields omitted", tab);
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 */ ");
1337 for (i, field) in fields.iter().enumerate() {
1342 clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
1343 clean::StructFieldItem(ref ty) => {
1347 field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()),
1348 ty.print(cx.cache(), cx.tcx()),
1351 _ => unreachable!(),
1355 if let Some(g) = g {
1356 write!(w, "{}", print_where_clause(g, cx.cache(), cx.tcx(), 0, false),)
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),)
1370 fn document_non_exhaustive_header(item: &clean::Item) -> &str {
1371 if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
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() {
1379 } else if item.is_enum() {
1381 } else if item.is_variant() {
1388 if item.is_struct() {
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.",
1396 } else if item.is_enum() {
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.",
1402 } else if item.is_variant() {
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.",
1410 "This type will require a wildcard arm in any match statements or constructors.",
1414 w.write_str("</div>");