1 use clean
::AttributesExt
;
3 use std
::cmp
::Ordering
;
6 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
8 use rustc_hir
::def
::CtorKind
;
9 use rustc_hir
::def_id
::DefId
;
10 use rustc_middle
::middle
::stability
;
11 use rustc_middle
::span_bug
;
12 use rustc_middle
::ty
::layout
::LayoutError
;
13 use rustc_middle
::ty
::{Adt, TyCtxt}
;
14 use rustc_span
::hygiene
::MacroKind
;
15 use rustc_span
::symbol
::{kw, sym, Symbol}
;
16 use rustc_target
::abi
::{Layout, Primitive, TagEncoding, Variants}
;
19 collect_paths_for_type
, document
, ensure_trailing_slash
, item_ty_to_section
,
20 notable_traits_decl
, render_assoc_item
, render_assoc_items
, render_attributes_in_code
,
21 render_attributes_in_pre
, render_impl
, render_stability_since_raw
, write_srclink
,
22 AssocItemLink
, Context
, ImplRenderingParameters
,
25 use crate::formats
::item_type
::ItemType
;
26 use crate::formats
::{AssocItemRender, Impl, RenderMode}
;
27 use crate::html
::escape
::Escape
;
28 use crate::html
::format
::{
29 join_with_double_colon
, print_abi_with_space
, print_constness_with_space
, print_where_clause
,
30 Buffer
, PrintWithSpace
,
32 use crate::html
::highlight
;
33 use crate::html
::layout
::Page
;
34 use crate::html
::markdown
::{HeadingOffset, MarkdownSummaryLine}
;
35 use crate::html
::url_parts_builder
::UrlPartsBuilder
;
39 const ITEM_TABLE_OPEN
: &str = "<div class=\"item-table\">";
40 const ITEM_TABLE_CLOSE
: &str = "</div>";
41 const ITEM_TABLE_ROW_OPEN
: &str = "<div class=\"item-row\">";
42 const ITEM_TABLE_ROW_CLOSE
: &str = "</div>";
44 // A component in a `use` path, like `string` in std::string::ToString
45 struct PathComponent
{
51 #[template(path = "print_item.html")]
54 static_root_path
: &'a
str,
58 path_components
: Vec
<PathComponent
>,
59 stability_since_raw
: &'a
str,
60 src_href
: Option
<&'a
str>,
63 pub(super) fn print_item(cx
: &Context
<'_
>, item
: &clean
::Item
, buf
: &mut Buffer
, page
: &Page
<'_
>) {
64 debug_assert
!(!item
.is_stripped());
65 let typ
= match *item
.kind
{
66 clean
::ModuleItem(_
) => {
73 clean
::FunctionItem(..) | clean
::ForeignFunctionItem(..) => "Function ",
74 clean
::TraitItem(..) => "Trait ",
75 clean
::StructItem(..) => "Struct ",
76 clean
::UnionItem(..) => "Union ",
77 clean
::EnumItem(..) => "Enum ",
78 clean
::TypedefItem(..) => "Type Definition ",
79 clean
::MacroItem(..) => "Macro ",
80 clean
::ProcMacroItem(ref mac
) => match mac
.kind
{
81 MacroKind
::Bang
=> "Macro ",
82 MacroKind
::Attr
=> "Attribute Macro ",
83 MacroKind
::Derive
=> "Derive Macro ",
85 clean
::PrimitiveItem(..) => "Primitive Type ",
86 clean
::StaticItem(..) | clean
::ForeignStaticItem(..) => "Static ",
87 clean
::ConstantItem(..) => "Constant ",
88 clean
::ForeignTypeItem
=> "Foreign Type ",
89 clean
::KeywordItem(..) => "Keyword ",
90 clean
::OpaqueTyItem(..) => "Opaque Type ",
91 clean
::TraitAliasItem(..) => "Trait Alias ",
93 // We don't generate pages for any other type.
97 let mut stability_since_raw
= Buffer
::new();
98 render_stability_since_raw(
99 &mut stability_since_raw
,
100 item
.stable_since(cx
.tcx()),
101 item
.const_stability(cx
.tcx()),
105 let stability_since_raw
: String
= stability_since_raw
.into_inner();
109 // When this item is part of a `crate use` in a downstream crate, the
110 // source link in the downstream documentation will actually come back to
111 // this page, and this link will be auto-clicked. The `id` attribute is
112 // used to find the link to auto-click.
114 if cx
.include_sources
&& !item
.is_primitive() { cx.src_href(item) }
else { None }
;
116 let path_components
= if item
.is_primitive() || item
.is_keyword() {
119 let cur
= &cx
.current
;
120 let amt
= if item
.is_mod() { cur.len() - 1 }
else { cur.len() }
;
124 .map(|(i
, component
)| PathComponent
{
125 path
: "../".repeat(cur
.len() - i
- 1),
131 let item_vars
= ItemVars
{
133 static_root_path
: page
.get_static_root_path(),
135 name
: item
.name
.as_ref().unwrap().as_str(),
136 item_type
: &item
.type_().to_string(),
138 stability_since_raw
: &stability_since_raw
,
139 src_href
: src_href
.as_deref(),
142 item_vars
.render_into(buf
).unwrap();
145 clean
::ModuleItem(ref m
) => item_module(buf
, cx
, item
, &m
.items
),
146 clean
::FunctionItem(ref f
) | clean
::ForeignFunctionItem(ref f
) => {
147 item_function(buf
, cx
, item
, f
)
149 clean
::TraitItem(ref t
) => item_trait(buf
, cx
, item
, t
),
150 clean
::StructItem(ref s
) => item_struct(buf
, cx
, item
, s
),
151 clean
::UnionItem(ref s
) => item_union(buf
, cx
, item
, s
),
152 clean
::EnumItem(ref e
) => item_enum(buf
, cx
, item
, e
),
153 clean
::TypedefItem(ref t
) => item_typedef(buf
, cx
, item
, t
),
154 clean
::MacroItem(ref m
) => item_macro(buf
, cx
, item
, m
),
155 clean
::ProcMacroItem(ref m
) => item_proc_macro(buf
, cx
, item
, m
),
156 clean
::PrimitiveItem(_
) => item_primitive(buf
, cx
, item
),
157 clean
::StaticItem(ref i
) | clean
::ForeignStaticItem(ref i
) => item_static(buf
, cx
, item
, i
),
158 clean
::ConstantItem(ref c
) => item_constant(buf
, cx
, item
, c
),
159 clean
::ForeignTypeItem
=> item_foreign_type(buf
, cx
, item
),
160 clean
::KeywordItem(_
) => item_keyword(buf
, cx
, item
),
161 clean
::OpaqueTyItem(ref e
) => item_opaque_ty(buf
, cx
, item
, e
),
162 clean
::TraitAliasItem(ref ta
) => item_trait_alias(buf
, cx
, item
, ta
),
164 // We don't generate pages for any other type.
170 /// For large structs, enums, unions, etc, determine whether to hide their fields
171 fn should_hide_fields(n_fields
: usize) -> bool
{
175 fn toggle_open(w
: &mut Buffer
, text
: impl fmt
::Display
) {
178 "<details class=\"rustdoc-toggle type-contents-toggle\">\
179 <summary class=\"hideme\">\
180 <span>Show {}</span>\
186 fn toggle_close(w
: &mut Buffer
) {
187 w
.write_str("</details>");
190 fn item_module(w
: &mut Buffer
, cx
: &Context
<'_
>, item
: &clean
::Item
, items
: &[clean
::Item
]) {
191 document(w
, cx
, item
, None
, HeadingOffset
::H2
);
193 let mut indices
= (0..items
.len()).filter(|i
| !items
[*i
].is_stripped()).collect
::<Vec
<usize>>();
195 // the order of item types in the listing
196 fn reorder(ty
: ItemType
) -> u8 {
198 ItemType
::ExternCrate
=> 0,
199 ItemType
::Import
=> 1,
200 ItemType
::Primitive
=> 2,
201 ItemType
::Module
=> 3,
202 ItemType
::Macro
=> 4,
203 ItemType
::Struct
=> 5,
205 ItemType
::Constant
=> 7,
206 ItemType
::Static
=> 8,
207 ItemType
::Trait
=> 9,
208 ItemType
::Function
=> 10,
209 ItemType
::Typedef
=> 12,
210 ItemType
::Union
=> 13,
222 let ty1
= i1
.type_();
223 let ty2
= i2
.type_();
224 if item_ty_to_section(ty1
) != item_ty_to_section(ty2
)
225 || (ty1
!= ty2
&& (ty1
== ItemType
::ExternCrate
|| ty2
== ItemType
::ExternCrate
))
227 return (reorder(ty1
), idx1
).cmp(&(reorder(ty2
), idx2
));
229 let s1
= i1
.stability(tcx
).as_ref().map(|s
| s
.level
);
230 let s2
= i2
.stability(tcx
).as_ref().map(|s
| s
.level
);
231 if let (Some(a
), Some(b
)) = (s1
, s2
) {
232 match (a
.is_stable(), b
.is_stable()) {
233 (true, true) | (false, false) => {}
234 (false, true) => return Ordering
::Less
,
235 (true, false) => return Ordering
::Greater
,
238 let lhs
= i1
.name
.unwrap_or(kw
::Empty
);
239 let rhs
= i2
.name
.unwrap_or(kw
::Empty
);
240 compare_names(lhs
.as_str(), rhs
.as_str())
243 if cx
.shared
.sort_modules_alphabetically
{
244 indices
.sort_by(|&i1
, &i2
| cmp(&items
[i1
], &items
[i2
], i1
, i2
, cx
.tcx()));
246 // This call is to remove re-export duplicates in cases such as:
251 // crate trait Double { fn foo(); }
255 // crate use foo::bar::*;
259 // `Double` will appear twice in the generated docs.
261 // FIXME: This code is quite ugly and could be improved. Small issue: DefId
262 // can be identical even if the elements are different (mostly in imports).
263 // So in case this is an import, we keep everything by adding a "unique id"
264 // (which is the position in the vector).
265 indices
.dedup_by_key(|i
| {
268 if items
[*i
].name
.is_some() { Some(full_path(cx, &items[*i])) }
else { None }
,
270 if items
[*i
].is_import() { *i }
else { 0 }
,
274 debug
!("{:?}", indices
);
275 let mut last_section
= None
;
277 for &idx
in &indices
{
278 let myitem
= &items
[idx
];
279 if myitem
.is_stripped() {
283 let my_section
= item_ty_to_section(myitem
.type_());
284 if Some(my_section
) != last_section
{
285 if last_section
.is_some() {
286 w
.write_str(ITEM_TABLE_CLOSE
);
288 last_section
= Some(my_section
);
291 "<h2 id=\"{id}\" class=\"small-section-header\">\
292 <a href=\"#{id}\">{name}</a>\
295 id
= cx
.derive_id(my_section
.id().to_owned()),
296 name
= my_section
.name(),
301 clean
::ExternCrateItem { ref src }
=> {
302 use crate::html
::format
::anchor
;
304 w
.write_str(ITEM_TABLE_ROW_OPEN
);
308 "<div class=\"item-left\"><code>{}extern crate {} as {};",
309 myitem
.visibility
.print_with_space(myitem
.item_id
, cx
),
310 anchor(myitem
.item_id
.expect_def_id(), src
, cx
),
311 myitem
.name
.unwrap(),
315 "<div class=\"item-left\"><code>{}extern crate {};",
316 myitem
.visibility
.print_with_space(myitem
.item_id
, cx
),
317 anchor(myitem
.item_id
.expect_def_id(), myitem
.name
.unwrap(), cx
),
320 w
.write_str("</code></div>");
321 w
.write_str(ITEM_TABLE_ROW_CLOSE
);
324 clean
::ImportItem(ref import
) => {
325 let (stab
, stab_tags
) = if let Some(import_def_id
) = import
.source
.did
{
326 let ast_attrs
= cx
.tcx().get_attrs_unchecked(import_def_id
);
327 let import_attrs
= Box
::new(clean
::Attributes
::from_ast(ast_attrs
, None
));
329 // Just need an item with the correct def_id and attrs
330 let import_item
= clean
::Item
{
331 item_id
: import_def_id
.into(),
333 cfg
: ast_attrs
.cfg(cx
.tcx(), &cx
.cache().hidden_cfg
),
337 let stab
= import_item
.stability_class(cx
.tcx());
338 let stab_tags
= Some(extra_info_tags(&import_item
, item
, cx
.tcx()));
344 let add
= if stab
.is_some() { " " }
else { "" }
;
346 w
.write_str(ITEM_TABLE_ROW_OPEN
);
349 "<div class=\"item-left {stab}{add}import-item\"{id}>\
350 <code>{vis}{imp}</code>\
352 <div class=\"item-right docblock-short\">{stab_tags}</div>",
353 stab
= stab
.unwrap_or_default(),
355 vis
= myitem
.visibility
.print_with_space(myitem
.item_id
, cx
),
356 imp
= import
.print(cx
),
357 stab_tags
= stab_tags
.unwrap_or_default(),
358 id
= match import
.kind
{
359 clean
::ImportKind
::Simple(s
) =>
360 format
!(" id=\"{}\"", cx
.derive_id(format
!("reexport.{}", s
))),
361 clean
::ImportKind
::Glob
=> String
::new(),
364 w
.write_str(ITEM_TABLE_ROW_CLOSE
);
368 if myitem
.name
.is_none() {
372 let unsafety_flag
= match *myitem
.kind
{
373 clean
::FunctionItem(_
) | clean
::ForeignFunctionItem(_
)
374 if myitem
.fn_header(cx
.tcx()).unwrap().unsafety
375 == hir
::Unsafety
::Unsafe
=>
377 "<a title=\"unsafe function\" href=\"#\"><sup>⚠</sup></a>"
382 let stab
= myitem
.stability_class(cx
.tcx());
383 let add
= if stab
.is_some() { " " }
else { "" }
;
385 let visibility_emoji
= match myitem
.visibility
{
386 clean
::Visibility
::Restricted(_
) => {
387 "<span title=\"Restricted Visibility\"> 🔒</span> "
392 let doc_value
= myitem
.doc_value().unwrap_or_default();
393 w
.write_str(ITEM_TABLE_ROW_OPEN
);
396 "<div class=\"item-left {stab}{add}module-item\">\
397 <a class=\"{class}\" href=\"{href}\" title=\"{title}\">{name}</a>\
402 <div class=\"item-right docblock-short\">{docs}</div>",
403 name
= myitem
.name
.unwrap(),
404 visibility_emoji
= visibility_emoji
,
405 stab_tags
= extra_info_tags(myitem
, item
, cx
.tcx()),
406 docs
= MarkdownSummaryLine(&doc_value
, &myitem
.links(cx
)).into_string(),
407 class
= myitem
.type_(),
409 stab
= stab
.unwrap_or_default(),
410 unsafety_flag
= unsafety_flag
,
411 href
= item_path(myitem
.type_(), myitem
.name
.unwrap().as_str()),
412 title
= [full_path(cx
, myitem
), myitem
.type_().to_string()]
414 .filter_map(|s
| if !s
.is_empty() { Some(s.as_str()) }
else { None }
)
418 w
.write_str(ITEM_TABLE_ROW_CLOSE
);
423 if last_section
.is_some() {
424 w
.write_str(ITEM_TABLE_CLOSE
);
428 /// Render the stability, deprecation and portability tags that are displayed in the item's summary
429 /// at the module level.
430 fn extra_info_tags(item
: &clean
::Item
, parent
: &clean
::Item
, tcx
: TyCtxt
<'_
>) -> String
{
431 let mut tags
= String
::new();
433 fn tag_html(class
: &str, title
: &str, contents
: &str) -> String
{
434 format
!(r
#"<span class="stab {}" title="{}">{}</span>"#, class, Escape(title), contents)
437 // The trailing space after each tag is to space it properly against the rest of the docs.
438 if let Some(depr
) = &item
.deprecation(tcx
) {
439 let mut message
= "Deprecated";
440 if !stability
::deprecation_in_effect(depr
) {
441 message
= "Deprecation planned";
443 tags
+= &tag_html("deprecated", "", message
);
446 // The "rustc_private" crates are permanently unstable so it makes no sense
447 // to render "unstable" everywhere.
451 .map(|s
| s
.level
.is_unstable() && s
.feature
!= sym
::rustc_private
)
454 tags
+= &tag_html("unstable", "", "Experimental");
457 let cfg
= match (&item
.cfg
, parent
.cfg
.as_ref()) {
458 (Some(cfg
), Some(parent_cfg
)) => cfg
.simplify_with(parent_cfg
),
459 (cfg
, _
) => cfg
.as_deref().cloned(),
462 debug
!("Portability {:?} - {:?} = {:?}", item
.cfg
, parent
.cfg
, cfg
);
463 if let Some(ref cfg
) = cfg
{
464 tags
+= &tag_html("portability", &cfg
.render_long_plain(), &cfg
.render_short_html());
470 fn item_function(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
, f
: &clean
::Function
) {
471 let header
= it
.fn_header(cx
.tcx()).expect("printing a function which isn't a function");
472 let constness
= print_constness_with_space(&header
.constness
, it
.const_stability(cx
.tcx()));
473 let unsafety
= header
.unsafety
.print_with_space();
474 let abi
= print_abi_with_space(header
.abi
).to_string();
475 let asyncness
= header
.asyncness
.print_with_space();
476 let visibility
= it
.visibility
.print_with_space(it
.item_id
, cx
).to_string();
477 let name
= it
.name
.unwrap();
479 let generics_len
= format
!("{:#}", f
.generics
.print(cx
)).len();
480 let header_len
= "fn ".len()
486 + name
.as_str().len()
489 wrap_into_docblock(w
, |w
| {
490 wrap_item(w
, "fn", |w
| {
491 render_attributes_in_pre(w
, it
, "");
492 w
.reserve(header_len
);
495 "{vis}{constness}{asyncness}{unsafety}{abi}fn \
496 {name}{generics}{decl}{notable_traits}{where_clause}",
498 constness
= constness
,
499 asyncness
= asyncness
,
503 generics
= f
.generics
.print(cx
),
504 where_clause
= print_where_clause(&f
.generics
, cx
, 0, true),
505 decl
= f
.decl
.full_print(header_len
, 0, header
.asyncness
, cx
),
506 notable_traits
= notable_traits_decl(&f
.decl
, cx
),
510 document(w
, cx
, it
, None
, HeadingOffset
::H2
)
513 fn item_trait(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
, t
: &clean
::Trait
) {
514 let bounds
= bounds(&t
.bounds
, false, cx
);
515 let required_types
= t
.items
.iter().filter(|m
| m
.is_ty_associated_type()).collect
::<Vec
<_
>>();
516 let provided_types
= t
.items
.iter().filter(|m
| m
.is_associated_type()).collect
::<Vec
<_
>>();
517 let required_consts
= t
.items
.iter().filter(|m
| m
.is_ty_associated_const()).collect
::<Vec
<_
>>();
518 let provided_consts
= t
.items
.iter().filter(|m
| m
.is_associated_const()).collect
::<Vec
<_
>>();
519 let required_methods
= t
.items
.iter().filter(|m
| m
.is_ty_method()).collect
::<Vec
<_
>>();
520 let provided_methods
= t
.items
.iter().filter(|m
| m
.is_method()).collect
::<Vec
<_
>>();
521 let count_types
= required_types
.len() + provided_types
.len();
522 let count_consts
= required_consts
.len() + provided_consts
.len();
523 let count_methods
= required_methods
.len() + provided_methods
.len();
525 // Output the trait definition
526 wrap_into_docblock(w
, |w
| {
527 wrap_item(w
, "trait", |w
| {
528 render_attributes_in_pre(w
, it
, "");
531 "{}{}{}trait {}{}{}",
532 it
.visibility
.print_with_space(it
.item_id
, cx
),
533 t
.unsafety
.print_with_space(),
534 if t
.is_auto { "auto " }
else { "" }
,
536 t
.generics
.print(cx
),
540 if !t
.generics
.where_predicates
.is_empty() {
541 write
!(w
, "{}", print_where_clause(&t
.generics
, cx
, 0, true));
546 if t
.items
.is_empty() {
549 // FIXME: we should be using a derived_id for the Anchors here
551 let mut toggle
= false;
553 // If there are too many associated types, hide _everything_
554 if should_hide_fields(count_types
) {
559 "{} associated items",
560 count_types
+ count_consts
+ count_methods
564 for types
in [&required_types
, &provided_types
] {
569 AssocItemLink
::Anchor(None
),
577 // If there are too many associated constants, hide everything after them
578 // We also do this if the types + consts is large because otherwise we could
579 // render a bunch of types and _then_ a bunch of consts just because both were
580 // _just_ under the limit
581 if !toggle
&& should_hide_fields(count_types
+ count_consts
) {
586 "{} associated constant{} and {} method{}",
588 pluralize(count_consts
),
590 pluralize(count_methods
),
594 if count_types
!= 0 && (count_consts
!= 0 || count_methods
!= 0) {
597 for consts
in [&required_consts
, &provided_consts
] {
602 AssocItemLink
::Anchor(None
),
610 if !toggle
&& should_hide_fields(count_methods
) {
612 toggle_open(w
, format_args
!("{} methods", count_methods
));
614 if count_consts
!= 0 && count_methods
!= 0 {
617 for (pos
, m
) in required_methods
.iter().enumerate() {
621 AssocItemLink
::Anchor(None
),
628 if pos
< required_methods
.len() - 1 {
629 w
.write_str("<span class=\"item-spacer\"></span>");
632 if !required_methods
.is_empty() && !provided_methods
.is_empty() {
635 for (pos
, m
) in provided_methods
.iter().enumerate() {
639 AssocItemLink
::Anchor(None
),
645 clean
::MethodItem(ref inner
, _
)
646 if !inner
.generics
.where_predicates
.is_empty() =>
648 w
.write_str(",\n { ... }\n");
651 w
.write_str(" { ... }\n");
655 if pos
< provided_methods
.len() - 1 {
656 w
.write_str("<span class=\"item-spacer\"></span>");
667 // Trait documentation
668 document(w
, cx
, it
, None
, HeadingOffset
::H2
);
670 fn write_small_section_header(w
: &mut Buffer
, id
: &str, title
: &str, extra_content
: &str) {
673 "<h2 id=\"{0}\" class=\"small-section-header\">\
674 {1}<a href=\"#{0}\" class=\"anchor\"></a>\
676 id
, title
, extra_content
680 fn trait_item(w
: &mut Buffer
, cx
: &Context
<'_
>, m
: &clean
::Item
, t
: &clean
::Item
) {
681 let name
= m
.name
.unwrap();
682 info
!("Documenting {} on {:?}", name
, t
.name
);
683 let item_type
= m
.type_();
684 let id
= cx
.derive_id(format
!("{}.{}", item_type
, name
));
685 let mut content
= Buffer
::empty_from(w
);
686 document(&mut content
, cx
, m
, Some(t
), HeadingOffset
::H5
);
687 let toggled
= !content
.is_empty();
689 write
!(w
, "<details class=\"rustdoc-toggle\" open><summary>");
691 write
!(w
, "<div id=\"{}\" class=\"method has-srclink\">", id
);
692 write
!(w
, "<div class=\"rightside\">");
694 let has_stability
= render_stability_since(w
, m
, t
, cx
.tcx());
698 write_srclink(cx
, m
, w
);
700 write
!(w
, "<h4 class=\"code-header\">");
704 AssocItemLink
::Anchor(Some(&id
)),
709 w
.write_str("</h4>");
710 w
.write_str("</div>");
712 write
!(w
, "</summary>");
713 w
.push_buffer(content
);
714 write
!(w
, "</details>");
718 if !required_types
.is_empty() {
719 write_small_section_header(
721 "required-associated-types",
722 "Required Associated Types",
723 "<div class=\"methods\">",
725 for t
in required_types
{
726 trait_item(w
, cx
, t
, it
);
728 w
.write_str("</div>");
730 if !provided_types
.is_empty() {
731 write_small_section_header(
733 "provided-associated-types",
734 "Provided Associated Types",
735 "<div class=\"methods\">",
737 for t
in provided_types
{
738 trait_item(w
, cx
, t
, it
);
740 w
.write_str("</div>");
743 if !required_consts
.is_empty() {
744 write_small_section_header(
746 "required-associated-consts",
747 "Required Associated Constants",
748 "<div class=\"methods\">",
750 for t
in required_consts
{
751 trait_item(w
, cx
, t
, it
);
753 w
.write_str("</div>");
755 if !provided_consts
.is_empty() {
756 write_small_section_header(
758 "provided-associated-consts",
759 "Provided Associated Constants",
760 "<div class=\"methods\">",
762 for t
in provided_consts
{
763 trait_item(w
, cx
, t
, it
);
765 w
.write_str("</div>");
768 // Output the documentation for each function individually
769 if !required_methods
.is_empty() {
770 write_small_section_header(
774 "<div class=\"methods\">",
776 for m
in required_methods
{
777 trait_item(w
, cx
, m
, it
);
779 w
.write_str("</div>");
781 if !provided_methods
.is_empty() {
782 write_small_section_header(
786 "<div class=\"methods\">",
788 for m
in provided_methods
{
789 trait_item(w
, cx
, m
, it
);
791 w
.write_str("</div>");
794 // If there are methods directly on this trait object, render them here.
795 render_assoc_items(w
, cx
, it
, it
.item_id
.expect_def_id(), AssocItemRender
::All
);
797 let cache
= cx
.cache();
798 let mut extern_crates
= FxHashSet
::default();
799 if let Some(implementors
) = cache
.implementors
.get(&it
.item_id
.expect_def_id()) {
800 // The DefId is for the first Type found with that name. The bool is
801 // if any Types with the same name but different DefId have been found.
802 let mut implementor_dups
: FxHashMap
<Symbol
, (DefId
, bool
)> = FxHashMap
::default();
803 for implementor
in implementors
{
804 if let Some(did
) = implementor
.inner_impl().for_
.without_borrowed_ref().def_id(cx
.cache()) &&
806 extern_crates
.insert(did
.krate
);
808 match implementor
.inner_impl().for_
.without_borrowed_ref() {
809 clean
::Type
::Path { ref path }
if !path
.is_assoc_ty() => {
810 let did
= path
.def_id();
811 let &mut (prev_did
, ref mut has_duplicates
) =
812 implementor_dups
.entry(path
.last()).or_insert((did
, false));
814 *has_duplicates
= true;
821 let (local
, foreign
) = implementors
.iter().partition
::<Vec
<_
>, _
>(|i
| {
822 i
.inner_impl().for_
.def_id(cache
).map_or(true, |d
| cache
.paths
.contains_key(&d
))
825 let (mut synthetic
, mut concrete
): (Vec
<&&Impl
>, Vec
<&&Impl
>) =
826 local
.iter().partition(|i
| i
.inner_impl().kind
.is_auto());
828 synthetic
.sort_by(|a
, b
| compare_impl(a
, b
, cx
));
829 concrete
.sort_by(|a
, b
| compare_impl(a
, b
, cx
));
831 if !foreign
.is_empty() {
832 write_small_section_header(w
, "foreign-impls", "Implementations on Foreign Types", "");
834 for implementor
in foreign
{
835 let provided_methods
= implementor
.inner_impl().provided_trait_methods(cx
.tcx());
837 AssocItemLink
::GotoSource(implementor
.impl_item
.item_id
, &provided_methods
);
847 ImplRenderingParameters
{
848 show_def_docs
: false,
849 is_on_foreign_type
: true,
850 show_default_items
: false,
851 show_non_assoc_items
: true,
852 toggle_open_by_default
: false,
858 write_small_section_header(
862 "<div class=\"item-list\" id=\"implementors-list\">",
864 for implementor
in concrete
{
865 render_implementor(cx
, implementor
, it
, w
, &implementor_dups
, &[]);
867 w
.write_str("</div>");
870 write_small_section_header(
872 "synthetic-implementors",
874 "<div class=\"item-list\" id=\"synthetic-implementors-list\">",
876 for implementor
in synthetic
{
883 &collect_paths_for_type(implementor
.inner_impl().for_
.clone(), cache
),
886 w
.write_str("</div>");
889 // even without any implementations to write in, we still want the heading and list, so the
890 // implementors javascript file pulled in below has somewhere to write the impls into
891 write_small_section_header(
895 "<div class=\"item-list\" id=\"implementors-list\"></div>",
899 write_small_section_header(
901 "synthetic-implementors",
903 "<div class=\"item-list\" id=\"synthetic-implementors-list\"></div>",
908 // Include implementors in crates that depend on the current crate.
910 // This is complicated by the way rustdoc is invoked, which is basically
911 // the same way rustc is invoked: it gets called, one at a time, for each
912 // crate. When building the rustdocs for the current crate, rustdoc can
913 // see crate metadata for its dependencies, but cannot see metadata for its
916 // To make this work, we generate a "hook" at this stage, and our
917 // dependents can "plug in" to it when they build. For simplicity's sake,
918 // it's [JSONP]: a JavaScript file with the data we need (and can parse),
919 // surrounded by a tiny wrapper that the Rust side ignores, but allows the
920 // JavaScript side to include without having to worry about Same Origin
921 // Policy. The code for *that* is in `write_shared.rs`.
923 // This is further complicated by `#[doc(inline)]`. We want all copies
924 // of an inlined trait to reference the same JS file, to address complex
925 // dependency graphs like this one (lower crates depend on higher crates):
928 // --------------------------------------------
929 // | crate A: trait Foo |
930 // --------------------------------------------
932 // -------------------------------- |
933 // | crate B: impl A::Foo for Bar | |
934 // -------------------------------- |
936 // ---------------------------------------------
937 // | crate C: #[doc(inline)] use A::Foo as Baz |
938 // | impl Baz for Quux |
939 // ---------------------------------------------
942 // Basically, we want `C::Baz` and `A::Foo` to show the same set of
943 // impls, which is easier if they both treat `/implementors/A/trait.Foo.js`
944 // as the Single Source of Truth.
946 // We also want the `impl Baz for Quux` to be written to
947 // `trait.Foo.js`. However, when we generate plain HTML for `C::Baz`,
948 // we're going to want to generate plain HTML for `impl Baz for Quux` too,
949 // because that'll load faster, and it's better for SEO. And we don't want
950 // the same impl to show up twice on the same page.
952 // To make this work, the implementors JS file has a structure kinda
957 // "B": {"impl A::Foo for Bar"},
958 // "C": {"impl Baz for Quux"},
962 // First of all, this means we can rebuild a crate, and it'll replace its own
963 // data if something changes. That is, `rustdoc` is idempotent. The other
964 // advantage is that we can list the crates that get included in the HTML,
965 // and ignore them when doing the JavaScript-based part of rendering.
966 // So C's HTML will have something like this:
969 // <script type="text/javascript" src="/implementors/A/trait.Foo.js"
970 // data-ignore-extern-crates="A,B" async></script>
973 // And, when the JS runs, anything in data-ignore-extern-crates is known
974 // to already be in the HTML, and will be ignored.
976 // [JSONP]: https://en.wikipedia.org/wiki/JSONP
977 let mut js_src_path
: UrlPartsBuilder
= std
::iter
::repeat("..")
978 .take(cx
.current
.len())
979 .chain(std
::iter
::once("implementors"))
981 if let Some(did
) = it
.item_id
.as_def_id() &&
982 let get_extern
= { || cache.external_paths.get(&did).map(|s| s.0.clone()) }
&&
983 let Some(fqp
) = cache
.exact_paths
.get(&did
).cloned().or_else(get_extern
) {
984 js_src_path
.extend(fqp
[..fqp
.len() - 1].iter().copied());
985 js_src_path
.push_fmt(format_args
!("{}.{}.js", it
.type_(), fqp
.last().unwrap()));
987 js_src_path
.extend(cx
.current
.iter().copied());
988 js_src_path
.push_fmt(format_args
!("{}.{}.js", it
.type_(), it
.name
.unwrap()));
990 let extern_crates
= extern_crates
992 .map(|cnum
| cx
.shared
.tcx
.crate_name(cnum
).to_string())
997 "<script type=\"text/javascript\" src=\"{src}\" data-ignore-extern-crates=\"{extern_crates}\" async></script>",
998 src
= js_src_path
.finish(),
1002 fn item_trait_alias(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
, t
: &clean
::TraitAlias
) {
1003 wrap_into_docblock(w
, |w
| {
1004 wrap_item(w
, "trait-alias", |w
| {
1005 render_attributes_in_pre(w
, it
, "");
1008 "trait {}{}{} = {};",
1010 t
.generics
.print(cx
),
1011 print_where_clause(&t
.generics
, cx
, 0, true),
1012 bounds(&t
.bounds
, true, cx
)
1017 document(w
, cx
, it
, None
, HeadingOffset
::H2
);
1019 // Render any items associated directly to this alias, as otherwise they
1020 // won't be visible anywhere in the docs. It would be nice to also show
1021 // associated items from the aliased type (see discussion in #32077), but
1022 // we need #14072 to make sense of the generics.
1023 render_assoc_items(w
, cx
, it
, it
.item_id
.expect_def_id(), AssocItemRender
::All
)
1026 fn item_opaque_ty(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
, t
: &clean
::OpaqueTy
) {
1027 wrap_into_docblock(w
, |w
| {
1028 wrap_item(w
, "opaque", |w
| {
1029 render_attributes_in_pre(w
, it
, "");
1032 "type {}{}{where_clause} = impl {bounds};",
1034 t
.generics
.print(cx
),
1035 where_clause
= print_where_clause(&t
.generics
, cx
, 0, true),
1036 bounds
= bounds(&t
.bounds
, false, cx
),
1041 document(w
, cx
, it
, None
, HeadingOffset
::H2
);
1043 // Render any items associated directly to this alias, as otherwise they
1044 // won't be visible anywhere in the docs. It would be nice to also show
1045 // associated items from the aliased type (see discussion in #32077), but
1046 // we need #14072 to make sense of the generics.
1047 render_assoc_items(w
, cx
, it
, it
.item_id
.expect_def_id(), AssocItemRender
::All
)
1050 fn item_typedef(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
, t
: &clean
::Typedef
) {
1051 fn write_content(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
, t
: &clean
::Typedef
) {
1052 wrap_item(w
, "typedef", |w
| {
1053 render_attributes_in_pre(w
, it
, "");
1054 write
!(w
, "{}", it
.visibility
.print_with_space(it
.item_id
, cx
));
1057 "type {}{}{where_clause} = {type_};",
1059 t
.generics
.print(cx
),
1060 where_clause
= print_where_clause(&t
.generics
, cx
, 0, true),
1061 type_
= t
.type_
.print(cx
),
1066 wrap_into_docblock(w
, |w
| write_content(w
, cx
, it
, t
));
1068 document(w
, cx
, it
, None
, HeadingOffset
::H2
);
1070 let def_id
= it
.item_id
.expect_def_id();
1071 // Render any items associated directly to this alias, as otherwise they
1072 // won't be visible anywhere in the docs. It would be nice to also show
1073 // associated items from the aliased type (see discussion in #32077), but
1074 // we need #14072 to make sense of the generics.
1075 render_assoc_items(w
, cx
, it
, def_id
, AssocItemRender
::All
);
1076 document_type_layout(w
, cx
, def_id
);
1079 fn item_union(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
, s
: &clean
::Union
) {
1080 wrap_into_docblock(w
, |w
| {
1081 wrap_item(w
, "union", |w
| {
1082 render_attributes_in_pre(w
, it
, "");
1083 render_union(w
, it
, Some(&s
.generics
), &s
.fields
, "", cx
);
1087 document(w
, cx
, it
, None
, HeadingOffset
::H2
);
1092 .filter_map(|f
| match *f
.kind
{
1093 clean
::StructFieldItem(ref ty
) => Some((f
, ty
)),
1097 if fields
.peek().is_some() {
1100 "<h2 id=\"fields\" class=\"fields small-section-header\">\
1101 Fields<a href=\"#fields\" class=\"anchor\"></a></h2>"
1103 for (field
, ty
) in fields
{
1104 let name
= field
.name
.expect("union field name");
1105 let id
= format
!("{}.{}", ItemType
::StructField
, name
);
1108 "<span id=\"{id}\" class=\"{shortty} small-section-header\">\
1109 <a href=\"#{id}\" class=\"anchor field\"></a>\
1110 <code>{name}: {ty}</code>\
1114 shortty
= ItemType
::StructField
,
1117 if let Some(stability_class
) = field
.stability_class(cx
.tcx()) {
1118 write
!(w
, "<span class=\"stab {stab}\"></span>", stab
= stability_class
);
1120 document(w
, cx
, field
, Some(it
), HeadingOffset
::H3
);
1123 let def_id
= it
.item_id
.expect_def_id();
1124 render_assoc_items(w
, cx
, it
, def_id
, AssocItemRender
::All
);
1125 document_type_layout(w
, cx
, def_id
);
1128 fn print_tuple_struct_fields(w
: &mut Buffer
, cx
: &Context
<'_
>, s
: &[clean
::Item
]) {
1129 for (i
, ty
) in s
.iter().enumerate() {
1131 w
.write_str(", ");
1134 clean
::StrippedItem(box clean
::StructFieldItem(_
)) => w
.write_str("_"),
1135 clean
::StructFieldItem(ref ty
) => write
!(w
, "{}", ty
.print(cx
)),
1136 _
=> unreachable
!(),
1141 fn item_enum(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
, e
: &clean
::Enum
) {
1142 wrap_into_docblock(w
, |w
| {
1143 wrap_item(w
, "enum", |w
| {
1144 render_attributes_in_pre(w
, it
, "");
1148 it
.visibility
.print_with_space(it
.item_id
, cx
),
1150 e
.generics
.print(cx
),
1151 print_where_clause(&e
.generics
, cx
, 0, true),
1153 if e
.variants
.is_empty() && !e
.variants_stripped
{
1156 w
.write_str(" {\n");
1157 let count_variants
= e
.variants
.len();
1158 let toggle
= should_hide_fields(count_variants
);
1160 toggle_open(w
, format_args
!("{} variants", count_variants
));
1162 for v
in &e
.variants
{
1164 let name
= v
.name
.unwrap();
1166 clean
::VariantItem(ref var
) => match var
{
1167 clean
::Variant
::CLike
=> write
!(w
, "{}", name
),
1168 clean
::Variant
::Tuple(ref s
) => {
1169 write
!(w
, "{}(", name
);
1170 print_tuple_struct_fields(w
, cx
, s
);
1173 clean
::Variant
::Struct(ref s
) => {
1186 _
=> unreachable
!(),
1191 if e
.variants_stripped
{
1192 w
.write_str(" // some variants omitted\n");
1202 document(w
, cx
, it
, None
, HeadingOffset
::H2
);
1204 if !e
.variants
.is_empty() {
1207 "<h2 id=\"variants\" class=\"variants small-section-header\">\
1208 Variants{}<a href=\"#variants\" class=\"anchor\"></a></h2>",
1209 document_non_exhaustive_header(it
)
1211 document_non_exhaustive(w
, it
);
1212 for variant
in &e
.variants
{
1213 let id
= cx
.derive_id(format
!("{}.{}", ItemType
::Variant
, variant
.name
.unwrap()));
1216 "<h3 id=\"{id}\" class=\"variant small-section-header\">\
1217 <a href=\"#{id}\" class=\"anchor field\"></a>\
1220 name
= variant
.name
.unwrap()
1222 if let clean
::VariantItem(clean
::Variant
::Tuple(ref s
)) = *variant
.kind
{
1224 print_tuple_struct_fields(w
, cx
, s
);
1227 w
.write_str("</code>");
1228 render_stability_since(w
, variant
, it
, cx
.tcx());
1229 w
.write_str("</h3>");
1231 use crate::clean
::Variant
;
1233 let heading_and_fields
= match &*variant
.kind
{
1234 clean
::VariantItem(Variant
::Struct(s
)) => Some(("Fields", &s
.fields
)),
1235 // Documentation on tuple variant fields is rare, so to reduce noise we only emit
1236 // the section if at least one field is documented.
1237 clean
::VariantItem(Variant
::Tuple(fields
))
1238 if fields
.iter().any(|f
| f
.doc_value().is_some()) =>
1240 Some(("Tuple Fields", fields
))
1245 if let Some((heading
, fields
)) = heading_and_fields
{
1247 cx
.derive_id(format
!("{}.{}.fields", ItemType
::Variant
, variant
.name
.unwrap()));
1248 write
!(w
, "<div class=\"sub-variant\" id=\"{id}\">", id
= variant_id
);
1249 write
!(w
, "<h4>{heading}</h4>", heading
= heading
);
1250 document_non_exhaustive(w
, variant
);
1251 for field
in fields
{
1253 clean
::StrippedItem(box clean
::StructFieldItem(_
)) => {}
1254 clean
::StructFieldItem(ref ty
) => {
1255 let id
= cx
.derive_id(format
!(
1256 "variant.{}.field.{}",
1257 variant
.name
.unwrap(),
1262 "<div class=\"sub-variant-field\">\
1263 <span id=\"{id}\" class=\"variant small-section-header\">\
1264 <a href=\"#{id}\" class=\"anchor field\"></a>\
1265 <code>{f}: {t}</code>\
1268 f
= field
.name
.unwrap(),
1271 document(w
, cx
, field
, Some(variant
), HeadingOffset
::H5
);
1272 write
!(w
, "</div>");
1274 _
=> unreachable
!(),
1277 w
.write_str("</div>");
1280 document(w
, cx
, variant
, Some(it
), HeadingOffset
::H4
);
1283 let def_id
= it
.item_id
.expect_def_id();
1284 render_assoc_items(w
, cx
, it
, def_id
, AssocItemRender
::All
);
1285 document_type_layout(w
, cx
, def_id
);
1288 fn item_macro(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
, t
: &clean
::Macro
) {
1289 wrap_into_docblock(w
, |w
| {
1290 highlight
::render_with_highlighting(
1296 it
.span(cx
.tcx()).inner().edition(),
1302 document(w
, cx
, it
, None
, HeadingOffset
::H2
)
1305 fn item_proc_macro(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
, m
: &clean
::ProcMacro
) {
1306 wrap_into_docblock(w
, |w
| {
1307 let name
= it
.name
.expect("proc-macros always have names");
1309 MacroKind
::Bang
=> {
1310 wrap_item(w
, "macro", |w
| {
1311 write
!(w
, "{}!() {{ /* proc-macro */ }}", name
);
1314 MacroKind
::Attr
=> {
1315 wrap_item(w
, "attr", |w
| {
1316 write
!(w
, "#[{}]", name
);
1319 MacroKind
::Derive
=> {
1320 wrap_item(w
, "derive", |w
| {
1321 write
!(w
, "#[derive({})]", name
);
1322 if !m
.helpers
.is_empty() {
1323 w
.push_str("\n{\n");
1324 w
.push_str(" // Attributes available to this derive:\n");
1325 for attr
in &m
.helpers
{
1326 writeln
!(w
, " #[{}]", attr
);
1334 document(w
, cx
, it
, None
, HeadingOffset
::H2
)
1337 fn item_primitive(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
) {
1338 document(w
, cx
, it
, None
, HeadingOffset
::H2
);
1339 render_assoc_items(w
, cx
, it
, it
.item_id
.expect_def_id(), AssocItemRender
::All
)
1342 fn item_constant(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
, c
: &clean
::Constant
) {
1343 wrap_into_docblock(w
, |w
| {
1344 wrap_item(w
, "const", |w
| {
1345 render_attributes_in_code(w
, it
);
1349 "{vis}const {name}: {typ}",
1350 vis
= it
.visibility
.print_with_space(it
.item_id
, cx
),
1351 name
= it
.name
.unwrap(),
1352 typ
= c
.type_
.print(cx
),
1355 let value
= c
.value(cx
.tcx());
1356 let is_literal
= c
.is_literal(cx
.tcx());
1357 let expr
= c
.expr(cx
.tcx());
1358 if value
.is_some() || is_literal
{
1359 write
!(w
, " = {expr};", expr
= Escape(&expr
));
1365 if let Some(value
) = &value
{
1366 let value_lowercase
= value
.to_lowercase();
1367 let expr_lowercase
= expr
.to_lowercase();
1369 if value_lowercase
!= expr_lowercase
1370 && value_lowercase
.trim_end_matches("i32") != expr_lowercase
1372 write
!(w
, " // {value}", value
= Escape(value
));
1379 document(w
, cx
, it
, None
, HeadingOffset
::H2
)
1382 fn item_struct(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
, s
: &clean
::Struct
) {
1383 wrap_into_docblock(w
, |w
| {
1384 wrap_item(w
, "struct", |w
| {
1385 render_attributes_in_code(w
, it
);
1386 render_struct(w
, it
, Some(&s
.generics
), s
.struct_type
, &s
.fields
, "", true, cx
);
1390 document(w
, cx
, it
, None
, HeadingOffset
::H2
);
1395 .filter_map(|f
| match *f
.kind
{
1396 clean
::StructFieldItem(ref ty
) => Some((f
, ty
)),
1400 if let CtorKind
::Fictive
| CtorKind
::Fn
= s
.struct_type
{
1401 if fields
.peek().is_some() {
1404 "<h2 id=\"fields\" class=\"fields small-section-header\">\
1405 {}{}<a href=\"#fields\" class=\"anchor\"></a>\
1407 if let CtorKind
::Fictive
= s
.struct_type { "Fields" }
else { "Tuple Fields" }
,
1408 document_non_exhaustive_header(it
)
1410 document_non_exhaustive(w
, it
);
1411 for (index
, (field
, ty
)) in fields
.enumerate() {
1413 field
.name
.map_or_else(|| index
.to_string(), |sym
| sym
.as_str().to_string());
1414 let id
= cx
.derive_id(format
!("{}.{}", ItemType
::StructField
, field_name
));
1417 "<span id=\"{id}\" class=\"{item_type} small-section-header\">\
1418 <a href=\"#{id}\" class=\"anchor field\"></a>\
1419 <code>{name}: {ty}</code>\
1421 item_type
= ItemType
::StructField
,
1426 document(w
, cx
, field
, Some(it
), HeadingOffset
::H3
);
1430 let def_id
= it
.item_id
.expect_def_id();
1431 render_assoc_items(w
, cx
, it
, def_id
, AssocItemRender
::All
);
1432 document_type_layout(w
, cx
, def_id
);
1435 fn item_static(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
, s
: &clean
::Static
) {
1436 wrap_into_docblock(w
, |w
| {
1437 wrap_item(w
, "static", |w
| {
1438 render_attributes_in_code(w
, it
);
1441 "{vis}static {mutability}{name}: {typ}",
1442 vis
= it
.visibility
.print_with_space(it
.item_id
, cx
),
1443 mutability
= s
.mutability
.print_with_space(),
1444 name
= it
.name
.unwrap(),
1445 typ
= s
.type_
.print(cx
)
1449 document(w
, cx
, it
, None
, HeadingOffset
::H2
)
1452 fn item_foreign_type(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
) {
1453 wrap_into_docblock(w
, |w
| {
1454 wrap_item(w
, "foreigntype", |w
| {
1455 w
.write_str("extern {\n");
1456 render_attributes_in_code(w
, it
);
1460 it
.visibility
.print_with_space(it
.item_id
, cx
),
1466 document(w
, cx
, it
, None
, HeadingOffset
::H2
);
1468 render_assoc_items(w
, cx
, it
, it
.item_id
.expect_def_id(), AssocItemRender
::All
)
1471 fn item_keyword(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
) {
1472 document(w
, cx
, it
, None
, HeadingOffset
::H2
)
1475 /// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order).
1476 crate fn compare_names(mut lhs
: &str, mut rhs
: &str) -> Ordering
{
1477 /// Takes a non-numeric and a numeric part from the given &str.
1478 fn take_parts
<'a
>(s
: &mut &'a
str) -> (&'a
str, &'a
str) {
1479 let i
= s
.find(|c
: char| c
.is_ascii_digit());
1480 let (a
, b
) = s
.split_at(i
.unwrap_or(s
.len()));
1481 let i
= b
.find(|c
: char| !c
.is_ascii_digit());
1482 let (b
, c
) = b
.split_at(i
.unwrap_or(b
.len()));
1487 while !lhs
.is_empty() || !rhs
.is_empty() {
1488 let (la
, lb
) = take_parts(&mut lhs
);
1489 let (ra
, rb
) = take_parts(&mut rhs
);
1490 // First process the non-numeric part.
1492 Ordering
::Equal
=> (),
1495 // Then process the numeric part, if both sides have one (and they fit in a u64).
1496 if let (Ok(ln
), Ok(rn
)) = (lb
.parse
::<u64>(), rb
.parse
::<u64>()) {
1498 Ordering
::Equal
=> (),
1502 // Then process the numeric part again, but this time as strings.
1504 Ordering
::Equal
=> (),
1512 pub(super) fn full_path(cx
: &Context
<'_
>, item
: &clean
::Item
) -> String
{
1513 let mut s
= join_with_double_colon(&cx
.current
);
1515 s
.push_str(item
.name
.unwrap().as_str());
1519 pub(super) fn item_path(ty
: ItemType
, name
: &str) -> String
{
1521 ItemType
::Module
=> format
!("{}index.html", ensure_trailing_slash(name
)),
1522 _
=> format
!("{}.{}.html", ty
, name
),
1526 fn bounds(t_bounds
: &[clean
::GenericBound
], trait_alias
: bool
, cx
: &Context
<'_
>) -> String
{
1527 let mut bounds
= String
::new();
1528 if !t_bounds
.is_empty() {
1530 bounds
.push_str(": ");
1532 for (i
, p
) in t_bounds
.iter().enumerate() {
1534 bounds
.push_str(" + ");
1536 bounds
.push_str(&p
.print(cx
).to_string());
1542 fn wrap_into_docblock
<F
>(w
: &mut Buffer
, f
: F
)
1544 F
: FnOnce(&mut Buffer
),
1546 w
.write_str("<div class=\"docblock item-decl\">");
1548 w
.write_str("</div>")
1551 fn wrap_item
<F
>(w
: &mut Buffer
, item_name
: &str, f
: F
)
1553 F
: FnOnce(&mut Buffer
),
1555 w
.write_fmt(format_args
!("<pre class=\"rust {}\"><code>", item_name
));
1557 w
.write_str("</code></pre>");
1560 fn render_stability_since(
1563 containing_item
: &clean
::Item
,
1566 render_stability_since_raw(
1568 item
.stable_since(tcx
),
1569 item
.const_stability(tcx
),
1570 containing_item
.stable_since(tcx
),
1571 containing_item
.const_stable_since(tcx
),
1575 fn compare_impl
<'a
, 'b
>(lhs
: &'a
&&Impl
, rhs
: &'b
&&Impl
, cx
: &Context
<'_
>) -> Ordering
{
1576 let lhss
= format
!("{}", lhs
.inner_impl().print(false, cx
));
1577 let rhss
= format
!("{}", rhs
.inner_impl().print(false, cx
));
1579 // lhs and rhs are formatted as HTML, which may be unnecessary
1580 compare_names(&lhss
, &rhss
)
1583 fn render_implementor(
1586 trait_
: &clean
::Item
,
1588 implementor_dups
: &FxHashMap
<Symbol
, (DefId
, bool
)>,
1591 // If there's already another implementor that has the same abridged name, use the
1592 // full path, for example in `std::iter::ExactSizeIterator`
1593 let use_absolute
= match implementor
.inner_impl().for_
{
1594 clean
::Type
::Path { ref path, .. }
1595 | clean
::BorrowedRef { type_: box clean::Type::Path { ref path, .. }
, .. }
1596 if !path
.is_assoc_ty() =>
1598 implementor_dups
[&path
.last()].1
1607 AssocItemLink
::Anchor(None
),
1611 ImplRenderingParameters
{
1612 show_def_docs
: false,
1613 is_on_foreign_type
: false,
1614 show_default_items
: false,
1615 show_non_assoc_items
: false,
1616 toggle_open_by_default
: false,
1624 g
: Option
<&clean
::Generics
>,
1625 fields
: &[clean
::Item
],
1629 write
!(w
, "{}union {}", it
.visibility
.print_with_space(it
.item_id
, cx
), it
.name
.unwrap());
1630 if let Some(g
) = g
{
1631 write
!(w
, "{}", g
.print(cx
));
1632 write
!(w
, "{}", print_where_clause(g
, cx
, 0, true));
1635 write
!(w
, " {{\n{}", tab
);
1637 fields
.iter().filter(|f
| matches
!(*f
.kind
, clean
::StructFieldItem(..))).count();
1638 let toggle
= should_hide_fields(count_fields
);
1640 toggle_open(w
, format_args
!("{} fields", count_fields
));
1643 for field
in fields
{
1644 if let clean
::StructFieldItem(ref ty
) = *field
.kind
{
1648 field
.visibility
.print_with_space(field
.item_id
, cx
),
1649 field
.name
.unwrap(),
1656 if it
.has_stripped_fields().unwrap() {
1657 write
!(w
, " /* private fields */\n{}", tab
);
1668 g
: Option
<&clean
::Generics
>,
1670 fields
: &[clean
::Item
],
1678 it
.visibility
.print_with_space(it
.item_id
, cx
),
1679 if structhead { "struct " }
else { "" }
,
1682 if let Some(g
) = g
{
1683 write
!(w
, "{}", g
.print(cx
))
1686 CtorKind
::Fictive
=> {
1687 if let Some(g
) = g
{
1688 write
!(w
, "{}", print_where_clause(g
, cx
, 0, true),)
1692 fields
.iter().filter(|f
| matches
!(*f
.kind
, clean
::StructFieldItem(..))).count();
1693 let has_visible_fields
= count_fields
> 0;
1694 let toggle
= should_hide_fields(count_fields
);
1696 toggle_open(w
, format_args
!("{} fields", count_fields
));
1698 for field
in fields
{
1699 if let clean
::StructFieldItem(ref ty
) = *field
.kind
{
1704 field
.visibility
.print_with_space(field
.item_id
, cx
),
1705 field
.name
.unwrap(),
1711 if has_visible_fields
{
1712 if it
.has_stripped_fields().unwrap() {
1713 write
!(w
, "\n{} /* private fields */", tab
);
1715 write
!(w
, "\n{}", tab
);
1716 } else if it
.has_stripped_fields().unwrap() {
1717 write
!(w
, " /* private fields */ ");
1726 for (i
, field
) in fields
.iter().enumerate() {
1731 clean
::StrippedItem(box clean
::StructFieldItem(..)) => write
!(w
, "_"),
1732 clean
::StructFieldItem(ref ty
) => {
1736 field
.visibility
.print_with_space(field
.item_id
, cx
),
1740 _
=> unreachable
!(),
1744 if let Some(g
) = g
{
1745 write
!(w
, "{}", print_where_clause(g
, cx
, 0, false),)
1747 // We only want a ";" when we are displaying a tuple struct, not a variant tuple struct.
1752 CtorKind
::Const
=> {
1753 // Needed for PhantomData.
1754 if let Some(g
) = g
{
1755 write
!(w
, "{}", print_where_clause(g
, cx
, 0, false),)
1762 fn document_non_exhaustive_header(item
: &clean
::Item
) -> &str {
1763 if item
.is_non_exhaustive() { " (Non-exhaustive)" }
else { "" }
1766 fn document_non_exhaustive(w
: &mut Buffer
, item
: &clean
::Item
) {
1767 if item
.is_non_exhaustive() {
1770 "<details class=\"rustdoc-toggle non-exhaustive\">\
1771 <summary class=\"hideme\"><span>{}</span></summary>\
1772 <div class=\"docblock\">",
1774 if item
.is_struct() {
1775 "This struct is marked as non-exhaustive"
1776 } else if item
.is_enum() {
1777 "This enum is marked as non-exhaustive"
1778 } else if item
.is_variant() {
1779 "This variant is marked as non-exhaustive"
1781 "This type is marked as non-exhaustive"
1786 if item
.is_struct() {
1788 "Non-exhaustive structs could have additional fields added in future. \
1789 Therefore, non-exhaustive structs cannot be constructed in external crates \
1790 using the traditional <code>Struct { .. }</code> syntax; cannot be \
1791 matched against without a wildcard <code>..</code>; and \
1792 struct update syntax will not work.",
1794 } else if item
.is_enum() {
1796 "Non-exhaustive enums could have additional variants added in future. \
1797 Therefore, when matching against variants of non-exhaustive enums, an \
1798 extra wildcard arm must be added to account for any future variants.",
1800 } else if item
.is_variant() {
1802 "Non-exhaustive enum variants could have additional fields added in future. \
1803 Therefore, non-exhaustive enum variants cannot be constructed in external \
1804 crates and cannot be matched against.",
1808 "This type will require a wildcard arm in any match statements or constructors.",
1812 w
.write_str("</div></details>");
1816 fn document_type_layout(w
: &mut Buffer
, cx
: &Context
<'_
>, ty_def_id
: DefId
) {
1817 fn write_size_of_layout(w
: &mut Buffer
, layout
: Layout
<'_
>, tag_size
: u64) {
1818 if layout
.abi().is_unsized() {
1819 write
!(w
, "(unsized)");
1821 let bytes
= layout
.size().bytes() - tag_size
;
1822 write
!(w
, "{size} byte{pl}", size
= bytes
, pl
= if bytes
== 1 { "" }
else { "s" }
,);
1826 if !cx
.shared
.show_type_layout
{
1830 writeln
!(w
, "<h2 class=\"small-section-header\">Layout</h2>");
1831 writeln
!(w
, "<div class=\"docblock\">");
1834 let param_env
= tcx
.param_env(ty_def_id
);
1835 let ty
= tcx
.type_of(ty_def_id
);
1836 match tcx
.layout_of(param_env
.and(ty
)) {
1840 "<div class=\"warning\"><p><strong>Note:</strong> Most layout information is \
1841 <strong>completely unstable</strong> and may even differ between compilations. \
1842 The only exception is types with certain <code>repr(...)</code> attributes. \
1843 Please see the Rust Reference’s \
1844 <a href=\"https://doc.rust-lang.org/reference/type-layout.html\">“Type Layout”</a> \
1845 chapter for details on type layout guarantees.</p></div>"
1847 w
.write_str("<p><strong>Size:</strong> ");
1848 write_size_of_layout(w
, ty_layout
.layout
, 0);
1849 writeln
!(w
, "</p>");
1850 if let Variants
::Multiple { variants, tag, tag_encoding, .. }
=
1851 &ty_layout
.layout
.variants()
1853 if !variants
.is_empty() {
1855 "<p><strong>Size for each variant:</strong></p>\
1859 let Adt(adt
, _
) = ty_layout
.ty
.kind() else {
1860 span_bug
!(tcx
.def_span(ty_def_id
), "not an adt")
1863 let tag_size
= if let TagEncoding
::Niche { .. }
= tag_encoding
{
1865 } else if let Primitive
::Int(i
, _
) = tag
.primitive() {
1868 span_bug
!(tcx
.def_span(ty_def_id
), "tag is neither niche nor int")
1871 for (index
, layout
) in variants
.iter_enumerated() {
1872 let name
= adt
.variant(index
).name
;
1873 write
!(w
, "<li><code>{name}</code>: ", name
= name
);
1874 write_size_of_layout(w
, *layout
, tag_size
);
1875 writeln
!(w
, "</li>");
1877 w
.write_str("</ul>");
1881 // This kind of layout error can occur with valid code, e.g. if you try to
1882 // get the layout of a generic type such as `Vec<T>`.
1883 Err(LayoutError
::Unknown(_
)) => {
1886 "<p><strong>Note:</strong> Unable to compute type layout, \
1887 possibly due to this type having generic parameters. \
1888 Layout can only be computed for concrete, fully-instantiated types.</p>"
1891 // This kind of error probably can't happen with valid code, but we don't
1892 // want to panic and prevent the docs from building, so we just let the
1893 // user know that we couldn't compute the layout.
1894 Err(LayoutError
::SizeOverflow(_
)) => {
1897 "<p><strong>Note:</strong> Encountered an error during type layout; \
1898 the type was too big.</p>"
1901 Err(LayoutError
::NormalizationFailure(_
, _
)) => {
1904 "<p><strong>Note:</strong> Encountered an error during type layout; \
1905 the type failed to be normalized.</p>"
1910 writeln
!(w
, "</div>");
1913 fn pluralize(count
: usize) -> &'
static str {
1914 if count
> 1 { "s" }
else { "" }