1 use clean
::AttributesExt
;
3 use rustc_data_structures
::captures
::Captures
;
4 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
6 use rustc_hir
::def
::CtorKind
;
7 use rustc_hir
::def_id
::DefId
;
8 use rustc_index
::IndexVec
;
9 use rustc_middle
::query
::Key
;
10 use rustc_middle
::ty
::{self, TyCtxt}
;
11 use rustc_span
::hygiene
::MacroKind
;
12 use rustc_span
::symbol
::{kw, sym, Symbol}
;
13 use rustc_target
::abi
::VariantIdx
;
14 use std
::cell
::{RefCell, RefMut}
;
15 use std
::cmp
::Ordering
;
19 use super::type_layout
::document_type_layout
;
21 collect_paths_for_type
, document
, ensure_trailing_slash
, get_filtered_impls_for_reference
,
22 item_ty_to_section
, notable_traits_button
, notable_traits_json
, render_all_impls
,
23 render_assoc_item
, render_assoc_items
, render_attributes_in_code
, render_attributes_in_pre
,
24 render_impl
, render_rightside
, render_stability_since_raw
,
25 render_stability_since_raw_with_extra
, AssocItemLink
, Context
, ImplRenderingParameters
,
28 use crate::config
::ModuleSorting
;
29 use crate::formats
::item_type
::ItemType
;
30 use crate::formats
::{AssocItemRender, Impl, RenderMode}
;
31 use crate::html
::escape
::Escape
;
32 use crate::html
::format
::{
33 display_fn
, join_with_double_colon
, print_abi_with_space
, print_constness_with_space
,
34 print_where_clause
, visibility_print_with_space
, Buffer
, Ending
, PrintWithSpace
,
36 use crate::html
::layout
::Page
;
37 use crate::html
::markdown
::{HeadingOffset, MarkdownSummaryLine}
;
38 use crate::html
::url_parts_builder
::UrlPartsBuilder
;
39 use crate::html
::{highlight, static_files}
;
42 use itertools
::Itertools
;
44 /// Generates an Askama template struct for rendering items with common methods.
47 /// ```ignore (illustrative)
49 /// #[template(path = "<template.html>", /* additional values */)]
50 /// /* additional meta items */
51 /// struct MyItem<'a, 'cx> {
52 /// cx: RefCell<&'a mut Context<'cx>>,
53 /// it: &'a clean::Item,
54 /// /* additional fields */
56 /// methods = [ /* method names (comma separated; refer to macro definition of `item_template_methods!()`) */ ]
60 /// NOTE: ensure that the generic lifetimes (`'a`, `'cx`) and
61 /// required fields (`cx`, `it`) are identical (in terms of order and definition).
62 macro_rules
! item_template
{
65 struct $name
:ident
<'a
, 'cx
> {
66 cx
: RefCell
<&'a
mut Context
<'cx
>>,
68 $
($field_name
:ident
: $field_ty
:ty
),*,
70 methods
= [$
($methods
:tt
),* $
(,)?
]
74 struct $name
<'a
, 'cx
> {
75 cx
: RefCell
<&'a
mut Context
<'cx
>>,
77 $
($field_name
: $field_ty
),*
80 impl<'a
, 'cx
: 'a
> ItemTemplate
<'a
, 'cx
> for $name
<'a
, 'cx
> {
81 fn item_and_mut_cx(&self) -> (&'a clean
::Item
, RefMut
<'_
, &'a
mut Context
<'cx
>>) {
82 (&self.it
, self.cx
.borrow_mut())
86 impl<'a
, 'cx
: 'a
> $name
<'a
, 'cx
> {
87 item_template_methods
!($
($methods
)*);
92 /// Implement common methods for item template structs generated by `item_template!()`.
94 /// NOTE: this macro is intended to be used only by `item_template!()`.
95 macro_rules
! item_template_methods
{
97 (document $
($rest
:tt
)*) => {
98 fn document
<'b
>(&'b
self) -> impl fmt
::Display
+ Captures
<'a
> + 'b
+ Captures
<'cx
> {
100 let (item
, mut cx
) = self.item_and_mut_cx();
101 let v
= document(*cx
, item
, None
, HeadingOffset
::H2
);
105 item_template_methods
!($
($rest
)*);
107 (document_type_layout $
($rest
:tt
)*) => {
108 fn document_type_layout
<'b
>(&'b
self) -> impl fmt
::Display
+ Captures
<'a
> + 'b
+ Captures
<'cx
> {
109 display_fn(move |f
| {
110 let (item
, cx
) = self.item_and_mut_cx();
111 let def_id
= item
.item_id
.expect_def_id();
112 let v
= document_type_layout(*cx
, def_id
);
116 item_template_methods
!($
($rest
)*);
118 (render_attributes_in_pre $
($rest
:tt
)*) => {
119 fn render_attributes_in_pre
<'b
>(&'b
self) -> impl fmt
::Display
+ Captures
<'a
> + 'b
+ Captures
<'cx
> {
120 display_fn(move |f
| {
121 let (item
, cx
) = self.item_and_mut_cx();
122 let v
= render_attributes_in_pre(item
, "", &cx
);
126 item_template_methods
!($
($rest
)*);
128 (render_assoc_items $
($rest
:tt
)*) => {
129 fn render_assoc_items
<'b
>(&'b
self) -> impl fmt
::Display
+ Captures
<'a
> + 'b
+ Captures
<'cx
> {
130 display_fn(move |f
| {
131 let (item
, mut cx
) = self.item_and_mut_cx();
132 let def_id
= item
.item_id
.expect_def_id();
133 let v
= render_assoc_items(*cx
, item
, def_id
, AssocItemRender
::All
);
137 item_template_methods
!($
($rest
)*);
139 ($method
:ident $
($rest
:tt
)*) => {
140 compile_error
!(concat
!("unknown method: ", stringify
!($method
)));
142 ($token
:tt $
($rest
:tt
)*) => {
143 compile_error
!(concat
!("unexpected token: ", stringify
!($token
)));
147 const ITEM_TABLE_OPEN
: &str = "<ul class=\"item-table\">";
148 const ITEM_TABLE_CLOSE
: &str = "</ul>";
149 const ITEM_TABLE_ROW_OPEN
: &str = "<li>";
150 const ITEM_TABLE_ROW_CLOSE
: &str = "</li>";
152 // A component in a `use` path, like `string` in std::string::ToString
153 struct PathComponent
{
159 #[template(path = "print_item.html")]
160 struct ItemVars
<'a
> {
161 static_root_path
: &'a
str,
162 clipboard_svg
: &'
static static_files
::StaticFile
,
166 path_components
: Vec
<PathComponent
>,
167 stability_since_raw
: &'a
str,
168 src_href
: Option
<&'a
str>,
171 /// Calls `print_where_clause` and returns `true` if a `where` clause was generated.
172 fn print_where_clause_and_check
<'a
, 'tcx
: 'a
>(
174 gens
: &'a clean
::Generics
,
175 cx
: &'a Context
<'tcx
>,
177 let len_before
= buffer
.len();
178 write
!(buffer
, "{}", print_where_clause(gens
, cx
, 0, Ending
::Newline
));
179 len_before
!= buffer
.len()
182 pub(super) fn print_item(
183 cx
: &mut Context
<'_
>,
188 debug_assert
!(!item
.is_stripped());
189 let typ
= match *item
.kind
{
190 clean
::ModuleItem(_
) => {
197 clean
::FunctionItem(..) | clean
::ForeignFunctionItem(..) => "Function ",
198 clean
::TraitItem(..) => "Trait ",
199 clean
::StructItem(..) => "Struct ",
200 clean
::UnionItem(..) => "Union ",
201 clean
::EnumItem(..) => "Enum ",
202 clean
::TypeAliasItem(..) => "Type Alias ",
203 clean
::MacroItem(..) => "Macro ",
204 clean
::ProcMacroItem(ref mac
) => match mac
.kind
{
205 MacroKind
::Bang
=> "Macro ",
206 MacroKind
::Attr
=> "Attribute Macro ",
207 MacroKind
::Derive
=> "Derive Macro ",
209 clean
::PrimitiveItem(..) => "Primitive Type ",
210 clean
::StaticItem(..) | clean
::ForeignStaticItem(..) => "Static ",
211 clean
::ConstantItem(..) => "Constant ",
212 clean
::ForeignTypeItem
=> "Foreign Type ",
213 clean
::KeywordItem
=> "Keyword ",
214 clean
::OpaqueTyItem(..) => "Opaque Type ",
215 clean
::TraitAliasItem(..) => "Trait Alias ",
217 // We don't generate pages for any other type.
221 let mut stability_since_raw
= Buffer
::new();
222 render_stability_since_raw(
223 &mut stability_since_raw
,
224 item
.stable_since(cx
.tcx()),
225 item
.const_stability(cx
.tcx()),
229 let stability_since_raw
: String
= stability_since_raw
.into_inner();
233 // When this item is part of a `crate use` in a downstream crate, the
234 // source link in the downstream documentation will actually come back to
235 // this page, and this link will be auto-clicked. The `id` attribute is
236 // used to find the link to auto-click.
238 if cx
.include_sources
&& !item
.is_primitive() { cx.src_href(item) }
else { None }
;
240 let path_components
= if item
.is_primitive() || item
.is_keyword() {
243 let cur
= &cx
.current
;
244 let amt
= if item
.is_mod() { cur.len() - 1 }
else { cur.len() }
;
248 .map(|(i
, component
)| PathComponent
{
249 path
: "../".repeat(cur
.len() - i
- 1),
255 let item_vars
= ItemVars
{
256 static_root_path
: &page
.get_static_root_path(),
257 clipboard_svg
: &static_files
::STATIC_FILES
.clipboard_svg
,
259 name
: item
.name
.as_ref().unwrap().as_str(),
260 item_type
: &item
.type_().to_string(),
262 stability_since_raw
: &stability_since_raw
,
263 src_href
: src_href
.as_deref(),
266 item_vars
.render_into(buf
).unwrap();
269 clean
::ModuleItem(ref m
) => item_module(buf
, cx
, item
, &m
.items
),
270 clean
::FunctionItem(ref f
) | clean
::ForeignFunctionItem(ref f
) => {
271 item_function(buf
, cx
, item
, f
)
273 clean
::TraitItem(ref t
) => item_trait(buf
, cx
, item
, t
),
274 clean
::StructItem(ref s
) => item_struct(buf
, cx
, item
, s
),
275 clean
::UnionItem(ref s
) => item_union(buf
, cx
, item
, s
),
276 clean
::EnumItem(ref e
) => item_enum(buf
, cx
, item
, e
),
277 clean
::TypeAliasItem(ref t
) => item_type_alias(buf
, cx
, item
, t
),
278 clean
::MacroItem(ref m
) => item_macro(buf
, cx
, item
, m
),
279 clean
::ProcMacroItem(ref m
) => item_proc_macro(buf
, cx
, item
, m
),
280 clean
::PrimitiveItem(_
) => item_primitive(buf
, cx
, item
),
281 clean
::StaticItem(ref i
) | clean
::ForeignStaticItem(ref i
) => item_static(buf
, cx
, item
, i
),
282 clean
::ConstantItem(ref c
) => item_constant(buf
, cx
, item
, c
),
283 clean
::ForeignTypeItem
=> item_foreign_type(buf
, cx
, item
),
284 clean
::KeywordItem
=> item_keyword(buf
, cx
, item
),
285 clean
::OpaqueTyItem(ref e
) => item_opaque_ty(buf
, cx
, item
, e
),
286 clean
::TraitAliasItem(ref ta
) => item_trait_alias(buf
, cx
, item
, ta
),
288 // We don't generate pages for any other type.
293 // Render notable-traits.js used for all methods in this module.
294 if !cx
.types_with_notable_traits
.is_empty() {
297 r
#"<script type="text/json" id="notable-traits-data">{}</script>"#,
298 notable_traits_json(cx
.types_with_notable_traits
.iter(), cx
)
300 cx
.types_with_notable_traits
.clear();
304 /// For large structs, enums, unions, etc, determine whether to hide their fields
305 fn should_hide_fields(n_fields
: usize) -> bool
{
309 fn toggle_open(mut w
: impl fmt
::Write
, text
: impl fmt
::Display
) {
312 "<details class=\"toggle type-contents-toggle\">\
313 <summary class=\"hideme\">\
314 <span>Show {text}</span>\
320 fn toggle_close(mut w
: impl fmt
::Write
) {
321 w
.write_str("</details>").unwrap();
324 trait ItemTemplate
<'a
, 'cx
: 'a
>: askama
::Template
+ fmt
::Display
{
325 fn item_and_mut_cx(&self) -> (&'a clean
::Item
, RefMut
<'_
, &'a
mut Context
<'cx
>>);
328 fn item_module(w
: &mut Buffer
, cx
: &mut Context
<'_
>, item
: &clean
::Item
, items
: &[clean
::Item
]) {
329 write
!(w
, "{}", document(cx
, item
, None
, HeadingOffset
::H2
));
331 let mut indices
= (0..items
.len()).filter(|i
| !items
[*i
].is_stripped()).collect
::<Vec
<usize>>();
333 // the order of item types in the listing
334 fn reorder(ty
: ItemType
) -> u8 {
336 ItemType
::ExternCrate
=> 0,
337 ItemType
::Import
=> 1,
338 ItemType
::Primitive
=> 2,
339 ItemType
::Module
=> 3,
340 ItemType
::Macro
=> 4,
341 ItemType
::Struct
=> 5,
343 ItemType
::Constant
=> 7,
344 ItemType
::Static
=> 8,
345 ItemType
::Trait
=> 9,
346 ItemType
::Function
=> 10,
347 ItemType
::TypeAlias
=> 12,
348 ItemType
::Union
=> 13,
360 let ty1
= i1
.type_();
361 let ty2
= i2
.type_();
362 if item_ty_to_section(ty1
) != item_ty_to_section(ty2
)
363 || (ty1
!= ty2
&& (ty1
== ItemType
::ExternCrate
|| ty2
== ItemType
::ExternCrate
))
365 return (reorder(ty1
), idx1
).cmp(&(reorder(ty2
), idx2
));
367 let s1
= i1
.stability(tcx
).as_ref().map(|s
| s
.level
);
368 let s2
= i2
.stability(tcx
).as_ref().map(|s
| s
.level
);
369 if let (Some(a
), Some(b
)) = (s1
, s2
) {
370 match (a
.is_stable(), b
.is_stable()) {
371 (true, true) | (false, false) => {}
372 (false, true) => return Ordering
::Less
,
373 (true, false) => return Ordering
::Greater
,
376 let lhs
= i1
.name
.unwrap_or(kw
::Empty
);
377 let rhs
= i2
.name
.unwrap_or(kw
::Empty
);
378 compare_names(lhs
.as_str(), rhs
.as_str())
381 match cx
.shared
.module_sorting
{
382 ModuleSorting
::Alphabetical
=> {
383 indices
.sort_by(|&i1
, &i2
| cmp(&items
[i1
], &items
[i2
], i1
, i2
, cx
.tcx()));
385 ModuleSorting
::DeclarationOrder
=> {}
387 // This call is to remove re-export duplicates in cases such as:
390 // pub(crate) mod foo {
391 // pub(crate) mod bar {
392 // pub(crate) trait Double { fn foo(); }
396 // pub(crate) use foo::bar::*;
397 // pub(crate) use foo::*;
400 // `Double` will appear twice in the generated docs.
402 // FIXME: This code is quite ugly and could be improved. Small issue: DefId
403 // can be identical even if the elements are different (mostly in imports).
404 // So in case this is an import, we keep everything by adding a "unique id"
405 // (which is the position in the vector).
406 indices
.dedup_by_key(|i
| {
409 if items
[*i
].name
.is_some() { Some(full_path(cx, &items[*i])) }
else { None }
,
411 if items
[*i
].is_import() { *i }
else { 0 }
,
415 debug
!("{indices:?}");
416 let mut last_section
= None
;
418 for &idx
in &indices
{
419 let myitem
= &items
[idx
];
420 if myitem
.is_stripped() {
424 let my_section
= item_ty_to_section(myitem
.type_());
425 if Some(my_section
) != last_section
{
426 if last_section
.is_some() {
427 w
.write_str(ITEM_TABLE_CLOSE
);
429 last_section
= Some(my_section
);
432 "<h2 id=\"{id}\" class=\"small-section-header\">\
433 <a href=\"#{id}\">{name}</a>\
434 </h2>{ITEM_TABLE_OPEN}",
435 id
= cx
.derive_id(my_section
.id()),
436 name
= my_section
.name(),
442 clean
::ExternCrateItem { ref src }
=> {
443 use crate::html
::format
::anchor
;
445 w
.write_str(ITEM_TABLE_ROW_OPEN
);
449 "<div class=\"item-name\"><code>{}extern crate {} as {};",
450 visibility_print_with_space(myitem
.visibility(tcx
), myitem
.item_id
, cx
),
451 anchor(myitem
.item_id
.expect_def_id(), src
, cx
),
452 myitem
.name
.unwrap(),
456 "<div class=\"item-name\"><code>{}extern crate {};",
457 visibility_print_with_space(myitem
.visibility(tcx
), myitem
.item_id
, cx
),
458 anchor(myitem
.item_id
.expect_def_id(), myitem
.name
.unwrap(), cx
),
461 w
.write_str("</code></div>");
462 w
.write_str(ITEM_TABLE_ROW_CLOSE
);
465 clean
::ImportItem(ref import
) => {
466 let stab_tags
= if let Some(import_def_id
) = import
.source
.did
{
467 let ast_attrs
= tcx
.get_attrs_unchecked(import_def_id
);
468 let import_attrs
= Box
::new(clean
::Attributes
::from_ast(ast_attrs
));
470 // Just need an item with the correct def_id and attrs
471 let import_item
= clean
::Item
{
472 item_id
: import_def_id
.into(),
474 cfg
: ast_attrs
.cfg(tcx
, &cx
.cache().hidden_cfg
),
478 let stab_tags
= Some(extra_info_tags(&import_item
, item
, tcx
).to_string());
484 w
.write_str(ITEM_TABLE_ROW_OPEN
);
485 let id
= match import
.kind
{
486 clean
::ImportKind
::Simple(s
) => {
487 format
!(" id=\"{}\"", cx
.derive_id(format
!("reexport.{s}")))
489 clean
::ImportKind
::Glob
=> String
::new(),
491 let stab_tags
= stab_tags
.unwrap_or_default();
492 let (stab_tags_before
, stab_tags_after
) = if stab_tags
.is_empty() {
495 ("<div class=\"desc docblock-short\">", "</div>")
499 "<div class=\"item-name\"{id}>\
500 <code>{vis}{imp}</code>\
502 {stab_tags_before}{stab_tags}{stab_tags_after}",
503 vis
= visibility_print_with_space(myitem
.visibility(tcx
), myitem
.item_id
, cx
),
504 imp
= import
.print(cx
),
506 w
.write_str(ITEM_TABLE_ROW_CLOSE
);
510 if myitem
.name
.is_none() {
514 let unsafety_flag
= match *myitem
.kind
{
515 clean
::FunctionItem(_
) | clean
::ForeignFunctionItem(_
)
516 if myitem
.fn_header(tcx
).unwrap().unsafety
== hir
::Unsafety
::Unsafe
=>
518 "<sup title=\"unsafe function\">âš </sup>"
523 let visibility_emoji
= match myitem
.visibility(tcx
) {
524 Some(ty
::Visibility
::Restricted(_
)) => {
525 "<span title=\"Restricted Visibility\"> 🔒</span> "
530 w
.write_str(ITEM_TABLE_ROW_OPEN
);
532 MarkdownSummaryLine(&myitem
.doc_value(), &myitem
.links(cx
)).into_string();
533 let (docs_before
, docs_after
) = if docs
.is_empty() {
536 ("<div class=\"desc docblock-short\">", "</div>")
540 "<div class=\"item-name\">\
541 <a class=\"{class}\" href=\"{href}\" title=\"{title}\">{name}</a>\
546 {docs_before}{docs}{docs_after}",
547 name
= myitem
.name
.unwrap(),
548 visibility_emoji
= visibility_emoji
,
549 stab_tags
= extra_info_tags(myitem
, item
, tcx
),
550 class
= myitem
.type_(),
551 unsafety_flag
= unsafety_flag
,
552 href
= item_path(myitem
.type_(), myitem
.name
.unwrap().as_str()),
553 title
= [myitem
.type_().to_string(), full_path(cx
, myitem
)]
555 .filter_map(|s
| if !s
.is_empty() { Some(s.as_str()) }
else { None }
)
559 w
.write_str(ITEM_TABLE_ROW_CLOSE
);
564 if last_section
.is_some() {
565 w
.write_str(ITEM_TABLE_CLOSE
);
569 /// Render the stability, deprecation and portability tags that are displayed in the item's summary
570 /// at the module level.
571 fn extra_info_tags
<'a
, 'tcx
: 'a
>(
572 item
: &'a clean
::Item
,
573 parent
: &'a clean
::Item
,
575 ) -> impl fmt
::Display
+ 'a
+ Captures
<'tcx
> {
576 display_fn(move |f
| {
581 ) -> impl fmt
::Display
+ 'a
{
582 display_fn(move |f
| {
585 r
#"<span class="stab {class}" title="{title}">{contents}</span>"#,
586 title
= Escape(title
),
591 // The trailing space after each tag is to space it properly against the rest of the docs.
592 if let Some(depr
) = &item
.deprecation(tcx
) {
593 let message
= if depr
.is_in_effect() { "Deprecated" }
else { "Deprecation planned" }
;
594 write
!(f
, "{}", tag_html("deprecated", "", message
))?
;
597 // The "rustc_private" crates are permanently unstable so it makes no sense
598 // to render "unstable" everywhere.
599 if item
.stability(tcx
).as_ref().map(|s
| s
.is_unstable() && s
.feature
!= sym
::rustc_private
)
602 write
!(f
, "{}", tag_html("unstable", "", "Experimental"))?
;
605 let cfg
= match (&item
.cfg
, parent
.cfg
.as_ref()) {
606 (Some(cfg
), Some(parent_cfg
)) => cfg
.simplify_with(parent_cfg
),
607 (cfg
, _
) => cfg
.as_deref().cloned(),
611 "Portability name={name:?} {cfg:?} - {parent_cfg:?} = {cfg:?}",
614 parent_cfg
= parent
.cfg
616 if let Some(ref cfg
) = cfg
{
620 tag_html("portability", &cfg
.render_long_plain(), &cfg
.render_short_html())
628 fn item_function(w
: &mut Buffer
, cx
: &mut Context
<'_
>, it
: &clean
::Item
, f
: &clean
::Function
) {
630 let header
= it
.fn_header(tcx
).expect("printing a function which isn't a function");
631 let constness
= print_constness_with_space(&header
.constness
, it
.const_stability(tcx
));
632 let unsafety
= header
.unsafety
.print_with_space();
633 let abi
= print_abi_with_space(header
.abi
).to_string();
634 let asyncness
= header
.asyncness
.print_with_space();
635 let visibility
= visibility_print_with_space(it
.visibility(tcx
), it
.item_id
, cx
).to_string();
636 let name
= it
.name
.unwrap();
638 let generics_len
= format
!("{:#}", f
.generics
.print(cx
)).len();
639 let header_len
= "fn ".len()
645 + name
.as_str().len()
648 let notable_traits
= notable_traits_button(&f
.decl
.output
, cx
);
651 w
.reserve(header_len
);
654 "{attrs}{vis}{constness}{asyncness}{unsafety}{abi}fn \
655 {name}{generics}{decl}{notable_traits}{where_clause}",
656 attrs
= render_attributes_in_pre(it
, "", cx
),
658 constness
= constness
,
659 asyncness
= asyncness
,
663 generics
= f
.generics
.print(cx
),
664 where_clause
= print_where_clause(&f
.generics
, cx
, 0, Ending
::Newline
),
665 decl
= f
.decl
.full_print(header_len
, 0, cx
),
666 notable_traits
= notable_traits
.unwrap_or_default(),
669 write
!(w
, "{}", document(cx
, it
, None
, HeadingOffset
::H2
));
672 fn item_trait(w
: &mut Buffer
, cx
: &mut Context
<'_
>, it
: &clean
::Item
, t
: &clean
::Trait
) {
674 let bounds
= bounds(&t
.bounds
, false, cx
);
675 let required_types
= t
.items
.iter().filter(|m
| m
.is_ty_associated_type()).collect
::<Vec
<_
>>();
676 let provided_types
= t
.items
.iter().filter(|m
| m
.is_associated_type()).collect
::<Vec
<_
>>();
677 let required_consts
= t
.items
.iter().filter(|m
| m
.is_ty_associated_const()).collect
::<Vec
<_
>>();
678 let provided_consts
= t
.items
.iter().filter(|m
| m
.is_associated_const()).collect
::<Vec
<_
>>();
679 let required_methods
= t
.items
.iter().filter(|m
| m
.is_ty_method()).collect
::<Vec
<_
>>();
680 let provided_methods
= t
.items
.iter().filter(|m
| m
.is_method()).collect
::<Vec
<_
>>();
681 let count_types
= required_types
.len() + provided_types
.len();
682 let count_consts
= required_consts
.len() + provided_consts
.len();
683 let count_methods
= required_methods
.len() + provided_methods
.len();
684 let must_implement_one_of_functions
= tcx
.trait_def(t
.def_id
).must_implement_one_of
.clone();
686 // Output the trait definition
687 wrap_item(w
, |mut w
| {
690 "{attrs}{vis}{unsafety}{is_auto}trait {name}{generics}{bounds}",
691 attrs
= render_attributes_in_pre(it
, "", cx
),
692 vis
= visibility_print_with_space(it
.visibility(tcx
), it
.item_id
, cx
),
693 unsafety
= t
.unsafety(tcx
).print_with_space(),
694 is_auto
= if t
.is_auto(tcx
) { "auto " }
else { "" }
,
695 name
= it
.name
.unwrap(),
696 generics
= t
.generics
.print(cx
),
699 if !t
.generics
.where_predicates
.is_empty() {
700 write
!(w
, "{}", print_where_clause(&t
.generics
, cx
, 0, Ending
::Newline
));
705 if t
.items
.is_empty() {
708 // FIXME: we should be using a derived_id for the Anchors here
710 let mut toggle
= false;
712 // If there are too many associated types, hide _everything_
713 if should_hide_fields(count_types
) {
717 format_args
!("{} associated items", count_types
+ count_consts
+ count_methods
),
720 for types
in [&required_types
, &provided_types
] {
725 AssocItemLink
::Anchor(None
),
733 // If there are too many associated constants, hide everything after them
734 // We also do this if the types + consts is large because otherwise we could
735 // render a bunch of types and _then_ a bunch of consts just because both were
736 // _just_ under the limit
737 if !toggle
&& should_hide_fields(count_types
+ count_consts
) {
742 "{count_consts} associated constant{plural_const} and \
743 {count_methods} method{plural_method}",
744 plural_const
= pluralize(count_consts
),
745 plural_method
= pluralize(count_methods
),
749 if count_types
!= 0 && (count_consts
!= 0 || count_methods
!= 0) {
752 for consts
in [&required_consts
, &provided_consts
] {
757 AssocItemLink
::Anchor(None
),
765 if !toggle
&& should_hide_fields(count_methods
) {
767 toggle_open(&mut w
, format_args
!("{count_methods} methods"));
769 if count_consts
!= 0 && count_methods
!= 0 {
773 if !required_methods
.is_empty() {
774 write
!(w
, " // Required method{}\n", pluralize(required_methods
.len()));
776 for (pos
, m
) in required_methods
.iter().enumerate() {
780 AssocItemLink
::Anchor(None
),
787 if pos
< required_methods
.len() - 1 {
788 w
.write_str("<span class=\"item-spacer\"></span>");
791 if !required_methods
.is_empty() && !provided_methods
.is_empty() {
795 if !provided_methods
.is_empty() {
796 write
!(w
, " // Provided method{}\n", pluralize(provided_methods
.len()));
798 for (pos
, m
) in provided_methods
.iter().enumerate() {
802 AssocItemLink
::Anchor(None
),
808 w
.write_str(" { ... }\n");
810 if pos
< provided_methods
.len() - 1 {
811 w
.write_str("<span class=\"item-spacer\"></span>");
815 toggle_close(&mut w
);
821 // Trait documentation
822 write
!(w
, "{}", document(cx
, it
, None
, HeadingOffset
::H2
));
824 fn write_small_section_header(w
: &mut Buffer
, id
: &str, title
: &str, extra_content
: &str) {
827 "<h2 id=\"{0}\" class=\"small-section-header\">\
828 {1}<a href=\"#{0}\" class=\"anchor\">§</a>\
830 id
, title
, extra_content
834 fn trait_item(w
: &mut Buffer
, cx
: &mut Context
<'_
>, m
: &clean
::Item
, t
: &clean
::Item
) {
835 let name
= m
.name
.unwrap();
836 info
!("Documenting {name} on {ty_name:?}", ty_name
= t
.name
);
837 let item_type
= m
.type_();
838 let id
= cx
.derive_id(format
!("{item_type}.{name}"));
839 let mut content
= Buffer
::empty_from(w
);
840 write
!(&mut content
, "{}", document(cx
, m
, Some(t
), HeadingOffset
::H5
));
841 let toggled
= !content
.is_empty();
843 let method_toggle_class
= if item_type
.is_method() { " method-toggle" }
else { "" }
;
844 write
!(w
, "<details class=\"toggle{method_toggle_class}\" open><summary>");
846 write
!(w
, "<section id=\"{id}\" class=\"method\">");
847 render_rightside(w
, cx
, m
, t
, RenderMode
::Normal
);
848 write
!(w
, "<h4 class=\"code-header\">");
852 AssocItemLink
::Anchor(Some(&id
)),
857 w
.write_str("</h4>");
858 w
.write_str("</section>");
860 write
!(w
, "</summary>");
861 w
.push_buffer(content
);
862 write
!(w
, "</details>");
866 if !required_types
.is_empty() {
867 write_small_section_header(
869 "required-associated-types",
870 "Required Associated Types",
871 "<div class=\"methods\">",
873 for t
in required_types
{
874 trait_item(w
, cx
, t
, it
);
876 w
.write_str("</div>");
878 if !provided_types
.is_empty() {
879 write_small_section_header(
881 "provided-associated-types",
882 "Provided Associated Types",
883 "<div class=\"methods\">",
885 for t
in provided_types
{
886 trait_item(w
, cx
, t
, it
);
888 w
.write_str("</div>");
891 if !required_consts
.is_empty() {
892 write_small_section_header(
894 "required-associated-consts",
895 "Required Associated Constants",
896 "<div class=\"methods\">",
898 for t
in required_consts
{
899 trait_item(w
, cx
, t
, it
);
901 w
.write_str("</div>");
903 if !provided_consts
.is_empty() {
904 write_small_section_header(
906 "provided-associated-consts",
907 "Provided Associated Constants",
908 "<div class=\"methods\">",
910 for t
in provided_consts
{
911 trait_item(w
, cx
, t
, it
);
913 w
.write_str("</div>");
916 // Output the documentation for each function individually
917 if !required_methods
.is_empty() || must_implement_one_of_functions
.is_some() {
918 write_small_section_header(
922 "<div class=\"methods\">",
925 if let Some(list
) = must_implement_one_of_functions
.as_deref() {
928 "<div class=\"stab must_implement\">At least one of the `{}` methods is required.</div>",
929 list
.iter().join("`, `")
933 for m
in required_methods
{
934 trait_item(w
, cx
, m
, it
);
936 w
.write_str("</div>");
938 if !provided_methods
.is_empty() {
939 write_small_section_header(
943 "<div class=\"methods\">",
945 for m
in provided_methods
{
946 trait_item(w
, cx
, m
, it
);
948 w
.write_str("</div>");
951 // If there are methods directly on this trait object, render them here.
952 write
!(w
, "{}", render_assoc_items(cx
, it
, it
.item_id
.expect_def_id(), AssocItemRender
::All
));
954 let cloned_shared
= Rc
::clone(&cx
.shared
);
955 let cache
= &cloned_shared
.cache
;
956 let mut extern_crates
= FxHashSet
::default();
958 if !t
.is_object_safe(cx
.tcx()) {
959 write_small_section_header(
964 "<div class=\"object-safety-info\">This trait is <b>not</b> \
965 <a href=\"{base}/reference/items/traits.html#object-safety\">\
966 object safe</a>.</div>",
967 base
= crate::clean
::utils
::DOC_RUST_LANG_ORG_CHANNEL
972 if let Some(implementors
) = cache
.implementors
.get(&it
.item_id
.expect_def_id()) {
973 // The DefId is for the first Type found with that name. The bool is
974 // if any Types with the same name but different DefId have been found.
975 let mut implementor_dups
: FxHashMap
<Symbol
, (DefId
, bool
)> = FxHashMap
::default();
976 for implementor
in implementors
{
977 if let Some(did
) = implementor
.inner_impl().for_
.without_borrowed_ref().def_id(cache
) &&
979 extern_crates
.insert(did
.krate
);
981 match implementor
.inner_impl().for_
.without_borrowed_ref() {
982 clean
::Type
::Path { ref path }
if !path
.is_assoc_ty() => {
983 let did
= path
.def_id();
984 let &mut (prev_did
, ref mut has_duplicates
) =
985 implementor_dups
.entry(path
.last()).or_insert((did
, false));
987 *has_duplicates
= true;
994 let (local
, mut foreign
) =
995 implementors
.iter().partition
::<Vec
<_
>, _
>(|i
| i
.is_on_local_type(cx
));
997 let (mut synthetic
, mut concrete
): (Vec
<&&Impl
>, Vec
<&&Impl
>) =
998 local
.iter().partition(|i
| i
.inner_impl().kind
.is_auto());
1000 synthetic
.sort_by_cached_key(|i
| ImplString
::new(i
, cx
));
1001 concrete
.sort_by_cached_key(|i
| ImplString
::new(i
, cx
));
1002 foreign
.sort_by_cached_key(|i
| ImplString
::new(i
, cx
));
1004 if !foreign
.is_empty() {
1005 write_small_section_header(w
, "foreign-impls", "Implementations on Foreign Types", "");
1007 for implementor
in foreign
{
1008 let provided_methods
= implementor
.inner_impl().provided_trait_methods(tcx
);
1010 AssocItemLink
::GotoSource(implementor
.impl_item
.item_id
, &provided_methods
);
1020 ImplRenderingParameters
{
1021 show_def_docs
: false,
1022 show_default_items
: false,
1023 show_non_assoc_items
: true,
1024 toggle_open_by_default
: false,
1030 write_small_section_header(
1034 "<div id=\"implementors-list\">",
1036 for implementor
in concrete
{
1037 render_implementor(cx
, implementor
, it
, w
, &implementor_dups
, &[]);
1039 w
.write_str("</div>");
1042 write_small_section_header(
1044 "synthetic-implementors",
1045 "Auto implementors",
1046 "<div id=\"synthetic-implementors-list\">",
1048 for implementor
in synthetic
{
1055 &collect_paths_for_type(implementor
.inner_impl().for_
.clone(), cache
),
1058 w
.write_str("</div>");
1061 // even without any implementations to write in, we still want the heading and list, so the
1062 // implementors javascript file pulled in below has somewhere to write the impls into
1063 write_small_section_header(
1067 "<div id=\"implementors-list\"></div>",
1071 write_small_section_header(
1073 "synthetic-implementors",
1074 "Auto implementors",
1075 "<div id=\"synthetic-implementors-list\"></div>",
1080 // [RUSTDOCIMPL] trait.impl
1082 // Include implementors in crates that depend on the current crate.
1084 // This is complicated by the way rustdoc is invoked, which is basically
1085 // the same way rustc is invoked: it gets called, one at a time, for each
1086 // crate. When building the rustdocs for the current crate, rustdoc can
1087 // see crate metadata for its dependencies, but cannot see metadata for its
1090 // To make this work, we generate a "hook" at this stage, and our
1091 // dependents can "plug in" to it when they build. For simplicity's sake,
1092 // it's [JSONP]: a JavaScript file with the data we need (and can parse),
1093 // surrounded by a tiny wrapper that the Rust side ignores, but allows the
1094 // JavaScript side to include without having to worry about Same Origin
1095 // Policy. The code for *that* is in `write_shared.rs`.
1097 // This is further complicated by `#[doc(inline)]`. We want all copies
1098 // of an inlined trait to reference the same JS file, to address complex
1099 // dependency graphs like this one (lower crates depend on higher crates):
1102 // --------------------------------------------
1103 // | crate A: trait Foo |
1104 // --------------------------------------------
1106 // -------------------------------- |
1107 // | crate B: impl A::Foo for Bar | |
1108 // -------------------------------- |
1110 // ---------------------------------------------
1111 // | crate C: #[doc(inline)] use A::Foo as Baz |
1112 // | impl Baz for Quux |
1113 // ---------------------------------------------
1116 // Basically, we want `C::Baz` and `A::Foo` to show the same set of
1117 // impls, which is easier if they both treat `/trait.impl/A/trait.Foo.js`
1118 // as the Single Source of Truth.
1120 // We also want the `impl Baz for Quux` to be written to
1121 // `trait.Foo.js`. However, when we generate plain HTML for `C::Baz`,
1122 // we're going to want to generate plain HTML for `impl Baz for Quux` too,
1123 // because that'll load faster, and it's better for SEO. And we don't want
1124 // the same impl to show up twice on the same page.
1126 // To make this work, the trait.impl/A/trait.Foo.js JS file has a structure kinda
1131 // "B": {"impl A::Foo for Bar"},
1132 // "C": {"impl Baz for Quux"},
1136 // First of all, this means we can rebuild a crate, and it'll replace its own
1137 // data if something changes. That is, `rustdoc` is idempotent. The other
1138 // advantage is that we can list the crates that get included in the HTML,
1139 // and ignore them when doing the JavaScript-based part of rendering.
1140 // So C's HTML will have something like this:
1143 // <script src="/trait.impl/A/trait.Foo.js"
1144 // data-ignore-extern-crates="A,B" async></script>
1147 // And, when the JS runs, anything in data-ignore-extern-crates is known
1148 // to already be in the HTML, and will be ignored.
1150 // [JSONP]: https://en.wikipedia.org/wiki/JSONP
1151 let mut js_src_path
: UrlPartsBuilder
= std
::iter
::repeat("..")
1152 .take(cx
.current
.len())
1153 .chain(std
::iter
::once("trait.impl"))
1155 if let Some(did
) = it
.item_id
.as_def_id() &&
1156 let get_extern
= { || cache.external_paths.get(&did).map(|s| &s.0) }
&&
1157 let Some(fqp
) = cache
.exact_paths
.get(&did
).or_else(get_extern
) {
1158 js_src_path
.extend(fqp
[..fqp
.len() - 1].iter().copied());
1159 js_src_path
.push_fmt(format_args
!("{}.{}.js", it
.type_(), fqp
.last().unwrap()));
1161 js_src_path
.extend(cx
.current
.iter().copied());
1162 js_src_path
.push_fmt(format_args
!("{}.{}.js", it
.type_(), it
.name
.unwrap()));
1164 let extern_crates
= extern_crates
1166 .map(|cnum
| tcx
.crate_name(cnum
).to_string())
1167 .collect
::<Vec
<_
>>()
1169 let (extern_before
, extern_after
) =
1170 if extern_crates
.is_empty() { ("", "") }
else { (" data-ignore-extern-crates=\"", "\"") }
;
1173 "<script src=\"{src}\"{extern_before}{extern_crates}{extern_after} async></script>",
1174 src
= js_src_path
.finish(),
1178 fn item_trait_alias(
1179 w
: &mut impl fmt
::Write
,
1180 cx
: &mut Context
<'_
>,
1182 t
: &clean
::TraitAlias
,
1187 "{attrs}trait {name}{generics}{where_b} = {bounds};",
1188 attrs
= render_attributes_in_pre(it
, "", cx
),
1189 name
= it
.name
.unwrap(),
1190 generics
= t
.generics
.print(cx
),
1191 where_b
= print_where_clause(&t
.generics
, cx
, 0, Ending
::Newline
),
1192 bounds
= bounds(&t
.bounds
, true, cx
),
1197 write
!(w
, "{}", document(cx
, it
, None
, HeadingOffset
::H2
)).unwrap();
1198 // Render any items associated directly to this alias, as otherwise they
1199 // won't be visible anywhere in the docs. It would be nice to also show
1200 // associated items from the aliased type (see discussion in #32077), but
1201 // we need #14072 to make sense of the generics.
1202 write
!(w
, "{}", render_assoc_items(cx
, it
, it
.item_id
.expect_def_id(), AssocItemRender
::All
))
1207 w
: &mut impl fmt
::Write
,
1208 cx
: &mut Context
<'_
>,
1210 t
: &clean
::OpaqueTy
,
1215 "{attrs}type {name}{generics}{where_clause} = impl {bounds};",
1216 attrs
= render_attributes_in_pre(it
, "", cx
),
1217 name
= it
.name
.unwrap(),
1218 generics
= t
.generics
.print(cx
),
1219 where_clause
= print_where_clause(&t
.generics
, cx
, 0, Ending
::Newline
),
1220 bounds
= bounds(&t
.bounds
, false, cx
),
1225 write
!(w
, "{}", document(cx
, it
, None
, HeadingOffset
::H2
)).unwrap();
1227 // Render any items associated directly to this alias, as otherwise they
1228 // won't be visible anywhere in the docs. It would be nice to also show
1229 // associated items from the aliased type (see discussion in #32077), but
1230 // we need #14072 to make sense of the generics.
1231 write
!(w
, "{}", render_assoc_items(cx
, it
, it
.item_id
.expect_def_id(), AssocItemRender
::All
))
1235 fn item_type_alias(w
: &mut Buffer
, cx
: &mut Context
<'_
>, it
: &clean
::Item
, t
: &clean
::TypeAlias
) {
1236 fn write_content(w
: &mut Buffer
, cx
: &Context
<'_
>, it
: &clean
::Item
, t
: &clean
::TypeAlias
) {
1240 "{attrs}{vis}type {name}{generics}{where_clause} = {type_};",
1241 attrs
= render_attributes_in_pre(it
, "", cx
),
1242 vis
= visibility_print_with_space(it
.visibility(cx
.tcx()), it
.item_id
, cx
),
1243 name
= it
.name
.unwrap(),
1244 generics
= t
.generics
.print(cx
),
1245 where_clause
= print_where_clause(&t
.generics
, cx
, 0, Ending
::Newline
),
1246 type_
= t
.type_
.print(cx
),
1251 write_content(w
, cx
, it
, t
);
1253 write
!(w
, "{}", document(cx
, it
, None
, HeadingOffset
::H2
));
1255 if let Some(inner_type
) = &t
.inner_type
{
1258 "<h2 id=\"aliased-type\" class=\"small-section-header\">\
1259 Aliased Type<a href=\"#aliased-type\" class=\"anchor\">§</a></h2>"
1263 clean
::TypeAliasInnerType
::Enum { variants, is_non_exhaustive }
=> {
1264 let variants_iter
= || variants
.iter().filter(|i
| !i
.is_stripped());
1265 let ty
= cx
.tcx().type_of(it
.def_id().unwrap()).instantiate_identity();
1266 let enum_def_id
= ty
.ty_adt_id().unwrap();
1269 let variants_len
= variants
.len();
1270 let variants_count
= variants_iter().count();
1271 let has_stripped_entries
= variants_len
!= variants_count
;
1273 write
!(w
, "enum {}{}", it
.name
.unwrap(), t
.generics
.print(cx
));
1280 has_stripped_entries
,
1285 item_variants(w
, cx
, it
, &variants
, enum_def_id
);
1287 clean
::TypeAliasInnerType
::Union { fields }
=> {
1289 let fields_count
= fields
.iter().filter(|i
| !i
.is_stripped()).count();
1290 let has_stripped_fields
= fields
.len() != fields_count
;
1292 write
!(w
, "union {}{}", it
.name
.unwrap(), t
.generics
.print(cx
));
1293 render_struct_fields(
1300 has_stripped_fields
,
1304 item_fields(w
, cx
, it
, fields
, None
);
1306 clean
::TypeAliasInnerType
::Struct { ctor_kind, fields }
=> {
1308 let fields_count
= fields
.iter().filter(|i
| !i
.is_stripped()).count();
1309 let has_stripped_fields
= fields
.len() != fields_count
;
1311 write
!(w
, "struct {}{}", it
.name
.unwrap(), t
.generics
.print(cx
));
1312 render_struct_fields(
1319 has_stripped_fields
,
1323 item_fields(w
, cx
, it
, fields
, None
);
1328 let def_id
= it
.item_id
.expect_def_id();
1329 // Render any items associated directly to this alias, as otherwise they
1330 // won't be visible anywhere in the docs. It would be nice to also show
1331 // associated items from the aliased type (see discussion in #32077), but
1332 // we need #14072 to make sense of the generics.
1333 write
!(w
, "{}", render_assoc_items(cx
, it
, def_id
, AssocItemRender
::All
));
1334 write
!(w
, "{}", document_type_layout(cx
, def_id
));
1336 // [RUSTDOCIMPL] type.impl
1338 // Include type definitions from the alias target type.
1340 // Earlier versions of this code worked by having `render_assoc_items`
1341 // include this data directly. That generates *O*`(types*impls)` of HTML
1342 // text, and some real crates have a lot of types and impls.
1344 // To create the same UX without generating half a gigabyte of HTML for a
1345 // crate that only contains 20 megabytes of actual documentation[^115718],
1346 // rustdoc stashes these type-alias-inlined docs in a [JSONP]
1347 // "database-lite". The file itself is generated in `write_shared.rs`,
1348 // and hooks into functions provided by `main.js`.
1350 // The format of `trait.impl` and `type.impl` JS files are superficially
1351 // similar. Each line, except the JSONP wrapper itself, belongs to a crate,
1352 // and they are otherwise separate (rustdoc should be idempotent). The
1353 // "meat" of the file is HTML strings, so the frontend code is very simple.
1354 // Links are relative to the doc root, though, so the frontend needs to fix
1355 // that up, and inlined docs can reuse these files.
1357 // However, there are a few differences, caused by the sophisticated
1358 // features that type aliases have. Consider this crate graph:
1361 // ---------------------------------
1362 // | crate A: struct Foo<T> |
1363 // | type Bar = Foo<i32> |
1364 // | impl X for Foo<i8> |
1365 // | impl Y for Foo<i32> |
1366 // ---------------------------------
1368 // ----------------------------------
1369 // | crate B: type Baz = A::Foo<i8> |
1370 // | type Xyy = A::Foo<i8> |
1371 // | impl Z for Xyy |
1372 // ----------------------------------
1375 // The type.impl/A/struct.Foo.js JS file has a structure kinda like this:
1379 // "A": [["impl Y for Foo<i32>", "Y", "A::Bar"]],
1380 // "B": [["impl X for Foo<i8>", "X", "B::Baz", "B::Xyy"], ["impl Z for Xyy", "Z", "B::Baz"]],
1384 // When the type.impl file is loaded, only the current crate's docs are
1385 // actually used. The main reason to bundle them together is that there's
1386 // enough duplication in them for DEFLATE to remove the redundancy.
1388 // The contents of a crate are a list of impl blocks, themselves
1389 // represented as lists. The first item in the sublist is the HTML block,
1390 // the second item is the name of the trait (which goes in the sidebar),
1391 // and all others are the names of type aliases that successfully match.
1395 // - There's no need to generate these files for types that have no aliases
1396 // in the current crate. If a dependent crate makes a type alias, it'll
1397 // take care of generating its own docs.
1398 // - There's no need to reimplement parts of the type checker in
1399 // JavaScript. The Rust backend does the checking, and includes its
1400 // results in the file.
1401 // - Docs defined directly on the type alias are dropped directly in the
1402 // HTML by `render_assoc_items`, and are accessible without JavaScript.
1403 // The JSONP file will not list impl items that are known to be part
1404 // of the main HTML file already.
1406 // [JSONP]: https://en.wikipedia.org/wiki/JSONP
1407 // [^115718]: https://github.com/rust-lang/rust/issues/115718
1408 let cloned_shared
= Rc
::clone(&cx
.shared
);
1409 let cache
= &cloned_shared
.cache
;
1410 if let Some(target_did
) = t
.type_
.def_id(cache
) &&
1411 let get_extern
= { || cache.external_paths.get(&target_did) }
&&
1412 let Some(&(ref target_fqp
, target_type
)) = cache
.paths
.get(&target_did
).or_else(get_extern
) &&
1413 target_type
.is_adt() && // primitives cannot be inlined
1414 let Some(self_did
) = it
.item_id
.as_def_id() &&
1415 let get_local
= { || cache.paths.get(&self_did).map(|(p, _)| p) }
&&
1416 let Some(self_fqp
) = cache
.exact_paths
.get(&self_did
).or_else(get_local
)
1418 let mut js_src_path
: UrlPartsBuilder
= std
::iter
::repeat("..")
1419 .take(cx
.current
.len())
1420 .chain(std
::iter
::once("type.impl"))
1422 js_src_path
.extend(target_fqp
[..target_fqp
.len() - 1].iter().copied());
1423 js_src_path
.push_fmt(format_args
!("{target_type}.{}.js", target_fqp
.last().unwrap()));
1424 let self_path
= self_fqp
.iter().map(Symbol
::as_str
).collect
::<Vec
<&str>>().join("::");
1427 "<script src=\"{src}\" data-self-path=\"{self_path}\" async></script>",
1428 src
= js_src_path
.finish(),
1433 fn item_union(w
: &mut Buffer
, cx
: &mut Context
<'_
>, it
: &clean
::Item
, s
: &clean
::Union
) {
1435 #[template(path = "item_union.html")]
1436 struct ItemUnion
<'a
, 'cx
> {
1437 cx
: RefCell
<&'a
mut Context
<'cx
>>,
1438 it
: &'a clean
::Item
,
1439 s
: &'a clean
::Union
,
1441 methods
= [document
, document_type_layout
, render_attributes_in_pre
, render_assoc_items
]
1444 impl<'a
, 'cx
: 'a
> ItemUnion
<'a
, 'cx
> {
1445 fn render_union
<'b
>(&'b
self) -> impl fmt
::Display
+ Captures
<'a
> + 'b
+ Captures
<'cx
> {
1446 display_fn(move |f
| {
1447 let cx
= self.cx
.borrow_mut();
1448 let v
= render_union(self.it
, Some(&self.s
.generics
), &self.s
.fields
, *cx
);
1453 fn document_field
<'b
>(
1455 field
: &'a clean
::Item
,
1456 ) -> impl fmt
::Display
+ Captures
<'a
> + 'b
+ Captures
<'cx
> {
1457 display_fn(move |f
| {
1458 let mut cx
= self.cx
.borrow_mut();
1459 let v
= document(*cx
, field
, Some(self.it
), HeadingOffset
::H3
);
1464 fn stability_field(&self, field
: &clean
::Item
) -> Option
<String
> {
1465 let cx
= self.cx
.borrow();
1466 field
.stability_class(cx
.tcx())
1471 ty
: &'a clean
::Type
,
1472 ) -> impl fmt
::Display
+ Captures
<'a
> + 'b
+ Captures
<'cx
> {
1473 display_fn(move |f
| {
1474 let cx
= self.cx
.borrow();
1475 let v
= ty
.print(*cx
);
1482 ) -> std
::iter
::Peekable
<impl Iterator
<Item
= (&'a clean
::Item
, &'a clean
::Type
)>> {
1486 .filter_map(|f
| match *f
.kind
{
1487 clean
::StructFieldItem(ref ty
) => Some((f
, ty
)),
1494 ItemUnion { cx: RefCell::new(cx), it, s }
.render_into(w
).unwrap();
1497 fn print_tuple_struct_fields
<'a
, 'cx
: 'a
>(
1498 cx
: &'a Context
<'cx
>,
1499 s
: &'a
[clean
::Item
],
1500 ) -> impl fmt
::Display
+ 'a
+ Captures
<'cx
> {
1503 .all(|field
| matches
!(*field
.kind
, clean
::StrippedItem(box clean
::StructFieldItem(..))))
1505 return f
.write_str("/* private fields */");
1508 for (i
, ty
) in s
.iter().enumerate() {
1513 clean
::StrippedItem(box clean
::StructFieldItem(_
)) => f
.write_str("_")?
,
1514 clean
::StructFieldItem(ref ty
) => write
!(f
, "{}", ty
.print(cx
))?
,
1515 _
=> unreachable
!(),
1522 fn item_enum(w
: &mut Buffer
, cx
: &mut Context
<'_
>, it
: &clean
::Item
, e
: &clean
::Enum
) {
1524 let count_variants
= e
.variants().count();
1526 render_attributes_in_code(w
, it
, cx
);
1530 visibility_print_with_space(it
.visibility(tcx
), it
.item_id
, cx
),
1532 e
.generics
.print(cx
),
1541 e
.has_stripped_entries(),
1542 it
.is_non_exhaustive(),
1543 it
.def_id().unwrap(),
1547 write
!(w
, "{}", document(cx
, it
, None
, HeadingOffset
::H2
));
1549 if count_variants
!= 0 {
1550 item_variants(w
, cx
, it
, &e
.variants
, it
.def_id().unwrap());
1552 let def_id
= it
.item_id
.expect_def_id();
1553 write
!(w
, "{}", render_assoc_items(cx
, it
, def_id
, AssocItemRender
::All
));
1554 write
!(w
, "{}", document_type_layout(cx
, def_id
));
1557 /// It'll return false if any variant is not a C-like variant. Otherwise it'll return true if at
1558 /// least one of them has an explicit discriminant or if the enum has `#[repr(C)]` or an integer
1560 fn should_show_enum_discriminant(
1563 variants
: &IndexVec
<VariantIdx
, clean
::Item
>,
1565 let mut has_variants_with_value
= false;
1566 for variant
in variants
{
1567 if let clean
::VariantItem(ref var
) = *variant
.kind
&&
1568 matches
!(var
.kind
, clean
::VariantKind
::CLike
)
1570 has_variants_with_value
|= var
.discriminant
.is_some();
1575 if has_variants_with_value
{
1578 let repr
= cx
.tcx().adt_def(enum_def_id
).repr();
1579 repr
.c() || repr
.int
.is_some()
1582 fn display_c_like_variant(
1584 cx
: &mut Context
<'_
>,
1586 variant
: &clean
::Variant
,
1588 should_show_enum_discriminant
: bool
,
1591 let name
= item
.name
.unwrap();
1592 if let Some(ref value
) = variant
.discriminant
{
1593 write
!(w
, "{} = {}", name
.as_str(), value
.value(cx
.tcx(), true));
1594 } else if should_show_enum_discriminant
{
1595 let adt_def
= cx
.tcx().adt_def(enum_def_id
);
1596 let discr
= adt_def
.discriminant_for_variant(cx
.tcx(), index
);
1597 if discr
.ty
.is_signed() {
1598 write
!(w
, "{} = {}", name
.as_str(), discr
.val
as i128
);
1600 write
!(w
, "{} = {}", name
.as_str(), discr
.val
);
1603 w
.write_str(name
.as_str());
1607 fn render_enum_fields(
1609 cx
: &mut Context
<'_
>,
1610 g
: Option
<&clean
::Generics
>,
1611 variants
: &IndexVec
<VariantIdx
, clean
::Item
>,
1612 count_variants
: usize,
1613 has_stripped_entries
: bool
,
1614 is_non_exhaustive
: bool
,
1617 let should_show_enum_discriminant
= should_show_enum_discriminant(cx
, enum_def_id
, variants
);
1618 if !g
.is_some_and(|g
| print_where_clause_and_check(w
, g
, cx
)) {
1619 // If there wasn't a `where` clause, we add a whitespace.
1623 let variants_stripped
= has_stripped_entries
;
1624 if count_variants
== 0 && !variants_stripped
{
1628 let toggle
= should_hide_fields(count_variants
);
1630 toggle_open(&mut w
, format_args
!("{count_variants} variants"));
1632 const TAB
: &str = " ";
1633 for (index
, v
) in variants
.iter_enumerated() {
1634 if v
.is_stripped() {
1639 clean
::VariantItem(ref var
) => match var
.kind
{
1640 clean
::VariantKind
::CLike
=> display_c_like_variant(
1646 should_show_enum_discriminant
,
1649 clean
::VariantKind
::Tuple(ref s
) => {
1650 write
!(w
, "{}({})", v
.name
.unwrap(), print_tuple_struct_fields(cx
, s
));
1652 clean
::VariantKind
::Struct(ref s
) => {
1653 render_struct(w
, v
, None
, None
, &s
.fields
, TAB
, false, cx
);
1656 _
=> unreachable
!(),
1661 if variants_stripped
&& !is_non_exhaustive
{
1662 w
.write_str(" // some variants omitted\n");
1665 toggle_close(&mut w
);
1673 cx
: &mut Context
<'_
>,
1675 variants
: &IndexVec
<VariantIdx
, clean
::Item
>,
1681 "<h2 id=\"variants\" class=\"variants small-section-header\">\
1682 Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
1685 <div class=\"variants\">",
1686 document_non_exhaustive_header(it
),
1687 document_non_exhaustive(it
)
1689 let should_show_enum_discriminant
= should_show_enum_discriminant(cx
, enum_def_id
, variants
);
1690 for (index
, variant
) in variants
.iter_enumerated() {
1691 if variant
.is_stripped() {
1694 let id
= cx
.derive_id(format
!("{}.{}", ItemType
::Variant
, variant
.name
.unwrap()));
1697 "<section id=\"{id}\" class=\"variant\">\
1698 <a href=\"#{id}\" class=\"anchor\">§</a>",
1700 render_stability_since_raw_with_extra(
1702 variant
.stable_since(tcx
),
1703 variant
.const_stability(tcx
),
1704 it
.stable_since(tcx
),
1705 it
.const_stable_since(tcx
),
1708 w
.write_str("<h3 class=\"code-header\">");
1709 if let clean
::VariantItem(ref var
) = *variant
.kind
&&
1710 let clean
::VariantKind
::CLike
= var
.kind
1712 display_c_like_variant(
1718 should_show_enum_discriminant
,
1722 w
.write_str(variant
.name
.unwrap().as_str());
1725 let clean
::VariantItem(variant_data
) = &*variant
.kind
else { unreachable!() }
;
1727 if let clean
::VariantKind
::Tuple(ref s
) = variant_data
.kind
{
1728 write
!(w
, "({})", print_tuple_struct_fields(cx
, s
));
1730 w
.write_str("</h3></section>");
1732 let heading_and_fields
= match &variant_data
.kind
{
1733 clean
::VariantKind
::Struct(s
) => Some(("Fields", &s
.fields
)),
1734 clean
::VariantKind
::Tuple(fields
) => {
1735 // Documentation on tuple variant fields is rare, so to reduce noise we only emit
1736 // the section if at least one field is documented.
1737 if fields
.iter().any(|f
| !f
.doc_value().is_empty()) {
1738 Some(("Tuple Fields", fields
))
1743 clean
::VariantKind
::CLike
=> None
,
1746 if let Some((heading
, fields
)) = heading_and_fields
{
1748 cx
.derive_id(format
!("{}.{}.fields", ItemType
::Variant
, variant
.name
.unwrap()));
1751 "<div class=\"sub-variant\" id=\"{variant_id}\">\
1754 document_non_exhaustive(variant
)
1756 for field
in fields
{
1758 clean
::StrippedItem(box clean
::StructFieldItem(_
)) => {}
1759 clean
::StructFieldItem(ref ty
) => {
1760 let id
= cx
.derive_id(format
!(
1761 "variant.{}.field.{}",
1762 variant
.name
.unwrap(),
1767 "<div class=\"sub-variant-field\">\
1768 <span id=\"{id}\" class=\"small-section-header\">\
1769 <a href=\"#{id}\" class=\"anchor field\">§</a>\
1770 <code>{f}: {t}</code>\
1772 f
= field
.name
.unwrap(),
1778 document(cx
, field
, Some(variant
), HeadingOffset
::H5
)
1781 _
=> unreachable
!(),
1784 w
.write_str("</div>");
1787 write
!(w
, "{}", document(cx
, variant
, Some(it
), HeadingOffset
::H4
));
1789 write
!(w
, "</div>");
1792 fn item_macro(w
: &mut Buffer
, cx
: &mut Context
<'_
>, it
: &clean
::Item
, t
: &clean
::Macro
) {
1793 highlight
::render_item_decl_with_highlighting(&t
.source
, w
);
1794 write
!(w
, "{}", document(cx
, it
, None
, HeadingOffset
::H2
))
1798 w
: &mut impl fmt
::Write
,
1799 cx
: &mut Context
<'_
>,
1801 m
: &clean
::ProcMacro
,
1803 wrap_item(w
, |buffer
| {
1804 let name
= it
.name
.expect("proc-macros always have names");
1806 MacroKind
::Bang
=> {
1807 write
!(buffer
, "{name}!() {{ /* proc-macro */ }}").unwrap();
1809 MacroKind
::Attr
=> {
1810 write
!(buffer
, "#[{name}]").unwrap();
1812 MacroKind
::Derive
=> {
1813 write
!(buffer
, "#[derive({name})]").unwrap();
1814 if !m
.helpers
.is_empty() {
1815 buffer
.write_str("\n{\n // Attributes available to this derive:\n").unwrap();
1816 for attr
in &m
.helpers
{
1817 writeln
!(buffer
, " #[{attr}]").unwrap();
1819 buffer
.write_str("}\n").unwrap();
1824 write
!(w
, "{}", document(cx
, it
, None
, HeadingOffset
::H2
)).unwrap();
1827 fn item_primitive(w
: &mut impl fmt
::Write
, cx
: &mut Context
<'_
>, it
: &clean
::Item
) {
1828 let def_id
= it
.item_id
.expect_def_id();
1829 write
!(w
, "{}", document(cx
, it
, None
, HeadingOffset
::H2
)).unwrap();
1830 if it
.name
.map(|n
| n
.as_str() != "reference").unwrap_or(false) {
1831 write
!(w
, "{}", render_assoc_items(cx
, it
, def_id
, AssocItemRender
::All
)).unwrap();
1833 // We handle the "reference" primitive type on its own because we only want to list
1834 // implementations on generic types.
1835 let shared
= Rc
::clone(&cx
.shared
);
1836 let (concrete
, synthetic
, blanket_impl
) = get_filtered_impls_for_reference(&shared
, it
);
1838 render_all_impls(w
, cx
, it
, &concrete
, &synthetic
, &blanket_impl
);
1842 fn item_constant(w
: &mut Buffer
, cx
: &mut Context
<'_
>, it
: &clean
::Item
, c
: &clean
::Constant
) {
1845 render_attributes_in_code(w
, it
, cx
);
1849 "{vis}const {name}{generics}: {typ}{where_clause}",
1850 vis
= visibility_print_with_space(it
.visibility(tcx
), it
.item_id
, cx
),
1851 name
= it
.name
.unwrap(),
1852 generics
= c
.generics
.print(cx
),
1853 typ
= c
.type_
.print(cx
),
1854 where_clause
= print_where_clause(&c
.generics
, cx
, 0, Ending
::NoNewline
),
1857 // FIXME: The code below now prints
1858 // ` = _; // 100i32`
1859 // if the expression is
1861 // which looks just wrong.
1866 let value
= c
.value(tcx
);
1867 let is_literal
= c
.is_literal(tcx
);
1868 let expr
= c
.expr(tcx
);
1869 if value
.is_some() || is_literal
{
1870 write
!(w
, " = {expr};", expr
= Escape(&expr
));
1876 if let Some(value
) = &value
{
1877 let value_lowercase
= value
.to_lowercase();
1878 let expr_lowercase
= expr
.to_lowercase();
1880 if value_lowercase
!= expr_lowercase
1881 && value_lowercase
.trim_end_matches("i32") != expr_lowercase
1883 write
!(w
, " // {value}", value
= Escape(value
));
1889 write
!(w
, "{}", document(cx
, it
, None
, HeadingOffset
::H2
))
1892 fn item_struct(w
: &mut Buffer
, cx
: &mut Context
<'_
>, it
: &clean
::Item
, s
: &clean
::Struct
) {
1894 render_attributes_in_code(w
, it
, cx
);
1895 render_struct(w
, it
, Some(&s
.generics
), s
.ctor_kind
, &s
.fields
, "", true, cx
);
1898 write
!(w
, "{}", document(cx
, it
, None
, HeadingOffset
::H2
));
1900 item_fields(w
, cx
, it
, &s
.fields
, s
.ctor_kind
);
1902 let def_id
= it
.item_id
.expect_def_id();
1903 write
!(w
, "{}", render_assoc_items(cx
, it
, def_id
, AssocItemRender
::All
));
1904 write
!(w
, "{}", document_type_layout(cx
, def_id
));
1909 cx
: &mut Context
<'_
>,
1911 fields
: &Vec
<clean
::Item
>,
1912 ctor_kind
: Option
<CtorKind
>,
1914 let mut fields
= fields
1916 .filter_map(|f
| match *f
.kind
{
1917 clean
::StructFieldItem(ref ty
) => Some((f
, ty
)),
1921 if let None
| Some(CtorKind
::Fn
) = ctor_kind
{
1922 if fields
.peek().is_some() {
1925 "<h2 id=\"fields\" class=\"fields small-section-header\">\
1926 {}{}<a href=\"#fields\" class=\"anchor\">§</a>\
1929 if ctor_kind
.is_none() { "Fields" }
else { "Tuple Fields" }
,
1930 document_non_exhaustive_header(it
),
1931 document_non_exhaustive(it
)
1933 for (index
, (field
, ty
)) in fields
.enumerate() {
1935 field
.name
.map_or_else(|| index
.to_string(), |sym
| sym
.as_str().to_string());
1936 let id
= cx
.derive_id(format
!("{typ}.{field_name}", typ
= ItemType
::StructField
));
1939 "<span id=\"{id}\" class=\"{item_type} small-section-header\">\
1940 <a href=\"#{id}\" class=\"anchor field\">§</a>\
1941 <code>{field_name}: {ty}</code>\
1943 item_type
= ItemType
::StructField
,
1946 write
!(w
, "{}", document(cx
, field
, Some(it
), HeadingOffset
::H3
));
1952 fn item_static(w
: &mut impl fmt
::Write
, cx
: &mut Context
<'_
>, it
: &clean
::Item
, s
: &clean
::Static
) {
1953 wrap_item(w
, |buffer
| {
1954 render_attributes_in_code(buffer
, it
, cx
);
1957 "{vis}static {mutability}{name}: {typ}",
1958 vis
= visibility_print_with_space(it
.visibility(cx
.tcx()), it
.item_id
, cx
),
1959 mutability
= s
.mutability
.print_with_space(),
1960 name
= it
.name
.unwrap(),
1961 typ
= s
.type_
.print(cx
)
1966 write
!(w
, "{}", document(cx
, it
, None
, HeadingOffset
::H2
)).unwrap();
1969 fn item_foreign_type(w
: &mut impl fmt
::Write
, cx
: &mut Context
<'_
>, it
: &clean
::Item
) {
1970 wrap_item(w
, |buffer
| {
1971 buffer
.write_str("extern {\n").unwrap();
1972 render_attributes_in_code(buffer
, it
, cx
);
1976 visibility_print_with_space(it
.visibility(cx
.tcx()), it
.item_id
, cx
),
1982 write
!(w
, "{}", document(cx
, it
, None
, HeadingOffset
::H2
)).unwrap();
1983 write
!(w
, "{}", render_assoc_items(cx
, it
, it
.item_id
.expect_def_id(), AssocItemRender
::All
))
1987 fn item_keyword(w
: &mut Buffer
, cx
: &mut Context
<'_
>, it
: &clean
::Item
) {
1988 write
!(w
, "{}", document(cx
, it
, None
, HeadingOffset
::H2
))
1991 /// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order).
1992 pub(crate) fn compare_names(mut lhs
: &str, mut rhs
: &str) -> Ordering
{
1993 /// Takes a non-numeric and a numeric part from the given &str.
1994 fn take_parts
<'a
>(s
: &mut &'a
str) -> (&'a
str, &'a
str) {
1995 let i
= s
.find(|c
: char| c
.is_ascii_digit());
1996 let (a
, b
) = s
.split_at(i
.unwrap_or(s
.len()));
1997 let i
= b
.find(|c
: char| !c
.is_ascii_digit());
1998 let (b
, c
) = b
.split_at(i
.unwrap_or(b
.len()));
2003 while !lhs
.is_empty() || !rhs
.is_empty() {
2004 let (la
, lb
) = take_parts(&mut lhs
);
2005 let (ra
, rb
) = take_parts(&mut rhs
);
2006 // First process the non-numeric part.
2008 Ordering
::Equal
=> (),
2011 // Then process the numeric part, if both sides have one (and they fit in a u64).
2012 if let (Ok(ln
), Ok(rn
)) = (lb
.parse
::<u64>(), rb
.parse
::<u64>()) {
2014 Ordering
::Equal
=> (),
2018 // Then process the numeric part again, but this time as strings.
2020 Ordering
::Equal
=> (),
2028 pub(super) fn full_path(cx
: &Context
<'_
>, item
: &clean
::Item
) -> String
{
2029 let mut s
= join_with_double_colon(&cx
.current
);
2031 s
.push_str(item
.name
.unwrap().as_str());
2035 pub(super) fn item_path(ty
: ItemType
, name
: &str) -> String
{
2037 ItemType
::Module
=> format
!("{}index.html", ensure_trailing_slash(name
)),
2038 _
=> format
!("{ty}.{name}.html"),
2042 fn bounds(t_bounds
: &[clean
::GenericBound
], trait_alias
: bool
, cx
: &Context
<'_
>) -> String
{
2043 let mut bounds
= String
::new();
2044 if !t_bounds
.is_empty() {
2046 bounds
.push_str(": ");
2048 for (i
, p
) in t_bounds
.iter().enumerate() {
2050 bounds
.push_str(" + ");
2052 bounds
.push_str(&p
.print(cx
).to_string());
2058 fn wrap_item
<W
, F
>(w
: &mut W
, f
: F
)
2063 write
!(w
, r
#"<pre class="rust item-decl"><code>"#).unwrap();
2065 write
!(w
, "</code></pre>").unwrap();
2068 #[derive(PartialEq, Eq)]
2069 struct ImplString(String
);
2072 fn new(i
: &Impl
, cx
: &Context
<'_
>) -> ImplString
{
2073 ImplString(format
!("{}", i
.inner_impl().print(false, cx
)))
2077 impl PartialOrd
for ImplString
{
2078 fn partial_cmp(&self, other
: &Self) -> Option
<Ordering
> {
2079 Some(Ord
::cmp(self, other
))
2083 impl Ord
for ImplString
{
2084 fn cmp(&self, other
: &Self) -> Ordering
{
2085 compare_names(&self.0, &other
.0)
2089 fn render_implementor(
2090 cx
: &mut Context
<'_
>,
2092 trait_
: &clean
::Item
,
2094 implementor_dups
: &FxHashMap
<Symbol
, (DefId
, bool
)>,
2097 // If there's already another implementor that has the same abridged name, use the
2098 // full path, for example in `std::iter::ExactSizeIterator`
2099 let use_absolute
= match implementor
.inner_impl().for_
{
2100 clean
::Type
::Path { ref path, .. }
2101 | clean
::BorrowedRef { type_: box clean::Type::Path { ref path, .. }
, .. }
2102 if !path
.is_assoc_ty() =>
2104 implementor_dups
[&path
.last()].1
2113 AssocItemLink
::Anchor(None
),
2117 ImplRenderingParameters
{
2118 show_def_docs
: false,
2119 show_default_items
: false,
2120 show_non_assoc_items
: false,
2121 toggle_open_by_default
: false,
2126 fn render_union
<'a
, 'cx
: 'a
>(
2127 it
: &'a clean
::Item
,
2128 g
: Option
<&'a clean
::Generics
>,
2129 fields
: &'a
[clean
::Item
],
2130 cx
: &'a Context
<'cx
>,
2131 ) -> impl fmt
::Display
+ 'a
+ Captures
<'cx
> {
2132 display_fn(move |mut f
| {
2137 visibility_print_with_space(it
.visibility(tcx
), it
.item_id
, cx
),
2141 let where_displayed
= g
2143 let mut buf
= Buffer
::html();
2144 write
!(buf
, "{}", g
.print(cx
));
2145 let where_displayed
= print_where_clause_and_check(&mut buf
, g
, cx
);
2146 write
!(f
, "{buf}", buf
= buf
.into_inner()).unwrap();
2151 // If there wasn't a `where` clause, we add a whitespace.
2152 if !where_displayed
{
2158 fields
.iter().filter(|field
| matches
!(*field
.kind
, clean
::StructFieldItem(..))).count();
2159 let toggle
= should_hide_fields(count_fields
);
2161 toggle_open(&mut f
, format_args
!("{count_fields} fields"));
2164 for field
in fields
{
2165 if let clean
::StructFieldItem(ref ty
) = *field
.kind
{
2169 visibility_print_with_space(field
.visibility(tcx
), field
.item_id
, cx
),
2170 field
.name
.unwrap(),
2176 if it
.has_stripped_entries().unwrap() {
2177 write
!(f
, " /* private fields */\n")?
;
2180 toggle_close(&mut f
);
2182 f
.write_str("}").unwrap();
2190 g
: Option
<&clean
::Generics
>,
2191 ty
: Option
<CtorKind
>,
2192 fields
: &[clean
::Item
],
2201 visibility_print_with_space(it
.visibility(tcx
), it
.item_id
, cx
),
2202 if structhead { "struct " }
else { "" }
,
2205 if let Some(g
) = g
{
2206 write
!(w
, "{}", g
.print(cx
))
2208 render_struct_fields(
2215 it
.has_stripped_entries().unwrap_or(false),
2220 fn render_struct_fields(
2222 g
: Option
<&clean
::Generics
>,
2223 ty
: Option
<CtorKind
>,
2224 fields
: &[clean
::Item
],
2227 has_stripped_entries
: bool
,
2233 let where_displayed
=
2234 g
.map(|g
| print_where_clause_and_check(w
, g
, cx
)).unwrap_or(false);
2236 // If there wasn't a `where` clause, we add a whitespace.
2237 if !where_displayed
{
2243 fields
.iter().filter(|f
| matches
!(*f
.kind
, clean
::StructFieldItem(..))).count();
2244 let has_visible_fields
= count_fields
> 0;
2245 let toggle
= should_hide_fields(count_fields
);
2247 toggle_open(&mut w
, format_args
!("{count_fields} fields"));
2249 for field
in fields
{
2250 if let clean
::StructFieldItem(ref ty
) = *field
.kind
{
2253 "\n{tab} {vis}{name}: {ty},",
2254 vis
= visibility_print_with_space(field
.visibility(tcx
), field
.item_id
, cx
),
2255 name
= field
.name
.unwrap(),
2261 if has_visible_fields
{
2262 if has_stripped_entries
{
2263 write
!(w
, "\n{tab} /* private fields */");
2265 write
!(w
, "\n{tab}");
2266 } else if has_stripped_entries
{
2267 write
!(w
, " /* private fields */ ");
2270 toggle_close(&mut w
);
2274 Some(CtorKind
::Fn
) => {
2276 if fields
.iter().all(|field
| {
2277 matches
!(*field
.kind
, clean
::StrippedItem(box clean
::StructFieldItem(..)))
2279 write
!(w
, "/* private fields */");
2281 for (i
, field
) in fields
.iter().enumerate() {
2286 clean
::StrippedItem(box clean
::StructFieldItem(..)) => write
!(w
, "_"),
2287 clean
::StructFieldItem(ref ty
) => {
2291 visibility_print_with_space(
2292 field
.visibility(tcx
),
2299 _
=> unreachable
!(),
2304 if let Some(g
) = g
{
2305 write
!(w
, "{}", print_where_clause(g
, cx
, 0, Ending
::NoNewline
));
2307 // We only want a ";" when we are displaying a tuple struct, not a variant tuple struct.
2312 Some(CtorKind
::Const
) => {
2313 // Needed for PhantomData.
2314 if let Some(g
) = g
{
2315 write
!(w
, "{}", print_where_clause(g
, cx
, 0, Ending
::NoNewline
));
2322 fn document_non_exhaustive_header(item
: &clean
::Item
) -> &str {
2323 if item
.is_non_exhaustive() { " (Non-exhaustive)" }
else { "" }
2326 fn document_non_exhaustive
<'a
>(item
: &'a clean
::Item
) -> impl fmt
::Display
+ 'a
{
2328 if item
.is_non_exhaustive() {
2331 "<details class=\"toggle non-exhaustive\">\
2332 <summary class=\"hideme\"><span>{}</span></summary>\
2333 <div class=\"docblock\">",
2335 if item
.is_struct() {
2336 "This struct is marked as non-exhaustive"
2337 } else if item
.is_enum() {
2338 "This enum is marked as non-exhaustive"
2339 } else if item
.is_variant() {
2340 "This variant is marked as non-exhaustive"
2342 "This type is marked as non-exhaustive"
2347 if item
.is_struct() {
2349 "Non-exhaustive structs could have additional fields added in future. \
2350 Therefore, non-exhaustive structs cannot be constructed in external crates \
2351 using the traditional <code>Struct { .. }</code> syntax; cannot be \
2352 matched against without a wildcard <code>..</code>; and \
2353 struct update syntax will not work.",
2355 } else if item
.is_enum() {
2357 "Non-exhaustive enums could have additional variants added in future. \
2358 Therefore, when matching against variants of non-exhaustive enums, an \
2359 extra wildcard arm must be added to account for any future variants.",
2361 } else if item
.is_variant() {
2363 "Non-exhaustive enum variants could have additional fields added in future. \
2364 Therefore, non-exhaustive enum variants cannot be constructed in external \
2365 crates and cannot be matched against.",
2369 "This type will require a wildcard arm in any match statements or constructors.",
2373 f
.write_str("</div></details>")?
;
2379 fn pluralize(count
: usize) -> &'
static str {
2380 if count
> 1 { "s" }
else { "" }