]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 XL |
1 | use clean::AttributesExt; |
2 | ||
04454e1e | 3 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
6a06907d XL |
4 | use rustc_hir as hir; |
5 | use rustc_hir::def::CtorKind; | |
6 | use rustc_hir::def_id::DefId; | |
7 | use rustc_middle::middle::stability; | |
c295e0f8 | 8 | use rustc_middle::span_bug; |
17df50a5 | 9 | use rustc_middle::ty::layout::LayoutError; |
487cf647 | 10 | use rustc_middle::ty::{self, Adt, TyCtxt}; |
6a06907d XL |
11 | use rustc_span::hygiene::MacroKind; |
12 | use rustc_span::symbol::{kw, sym, Symbol}; | |
487cf647 | 13 | use rustc_target::abi::{LayoutS, Primitive, TagEncoding, VariantIdx, Variants}; |
064997fb FG |
14 | use std::cmp::Ordering; |
15 | use std::fmt; | |
16 | use std::rc::Rc; | |
6a06907d XL |
17 | |
18 | use super::{ | |
f2b60f7d | 19 | collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference, |
487cf647 FG |
20 | item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls, |
21 | render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre, | |
22 | render_impl, render_rightside, render_stability_since_raw, | |
23 | render_stability_since_raw_with_extra, AssocItemLink, Context, ImplRenderingParameters, | |
6a06907d | 24 | }; |
3c0e092e | 25 | use crate::clean; |
064997fb | 26 | use crate::config::ModuleSorting; |
6a06907d | 27 | use crate::formats::item_type::ItemType; |
cdc7bbd5 | 28 | use crate::formats::{AssocItemRender, Impl, RenderMode}; |
6a06907d | 29 | use crate::html::escape::Escape; |
136023e0 | 30 | use crate::html::format::{ |
5099ac24 | 31 | join_with_double_colon, print_abi_with_space, print_constness_with_space, print_where_clause, |
487cf647 | 32 | visibility_print_with_space, Buffer, Ending, PrintWithSpace, |
136023e0 | 33 | }; |
17df50a5 | 34 | use crate::html::layout::Page; |
c295e0f8 | 35 | use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine}; |
5099ac24 | 36 | use crate::html::url_parts_builder::UrlPartsBuilder; |
487cf647 | 37 | use crate::html::{highlight, static_files}; |
c295e0f8 | 38 | |
5099ac24 | 39 | use askama::Template; |
064997fb | 40 | use itertools::Itertools; |
6a06907d | 41 | |
3c0e092e XL |
42 | const ITEM_TABLE_OPEN: &str = "<div class=\"item-table\">"; |
43 | const ITEM_TABLE_CLOSE: &str = "</div>"; | |
44 | const ITEM_TABLE_ROW_OPEN: &str = "<div class=\"item-row\">"; | |
45 | const ITEM_TABLE_ROW_CLOSE: &str = "</div>"; | |
c295e0f8 XL |
46 | |
47 | // A component in a `use` path, like `string` in std::string::ToString | |
5099ac24 | 48 | struct PathComponent { |
c295e0f8 | 49 | path: String, |
5099ac24 | 50 | name: Symbol, |
c295e0f8 XL |
51 | } |
52 | ||
5099ac24 FG |
53 | #[derive(Template)] |
54 | #[template(path = "print_item.html")] | |
c295e0f8 | 55 | struct ItemVars<'a> { |
c295e0f8 | 56 | static_root_path: &'a str, |
487cf647 | 57 | clipboard_svg: &'static static_files::StaticFile, |
c295e0f8 XL |
58 | typ: &'a str, |
59 | name: &'a str, | |
60 | item_type: &'a str, | |
5099ac24 | 61 | path_components: Vec<PathComponent>, |
c295e0f8 XL |
62 | stability_since_raw: &'a str, |
63 | src_href: Option<&'a str>, | |
64 | } | |
136023e0 | 65 | |
064997fb FG |
66 | /// Calls `print_where_clause` and returns `true` if a `where` clause was generated. |
67 | fn print_where_clause_and_check<'a, 'tcx: 'a>( | |
68 | buffer: &mut Buffer, | |
69 | gens: &'a clean::Generics, | |
70 | cx: &'a Context<'tcx>, | |
71 | ) -> bool { | |
72 | let len_before = buffer.len(); | |
73 | write!(buffer, "{}", print_where_clause(gens, cx, 0, Ending::Newline)); | |
74 | len_before != buffer.len() | |
75 | } | |
76 | ||
923072b8 FG |
77 | pub(super) fn print_item( |
78 | cx: &mut Context<'_>, | |
79 | item: &clean::Item, | |
80 | buf: &mut Buffer, | |
81 | page: &Page<'_>, | |
82 | ) { | |
6a06907d | 83 | debug_assert!(!item.is_stripped()); |
c295e0f8 | 84 | let typ = match *item.kind { |
cdc7bbd5 XL |
85 | clean::ModuleItem(_) => { |
86 | if item.is_crate() { | |
6a06907d XL |
87 | "Crate " |
88 | } else { | |
89 | "Module " | |
90 | } | |
91 | } | |
92 | clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ", | |
93 | clean::TraitItem(..) => "Trait ", | |
94 | clean::StructItem(..) => "Struct ", | |
95 | clean::UnionItem(..) => "Union ", | |
96 | clean::EnumItem(..) => "Enum ", | |
97 | clean::TypedefItem(..) => "Type Definition ", | |
98 | clean::MacroItem(..) => "Macro ", | |
99 | clean::ProcMacroItem(ref mac) => match mac.kind { | |
100 | MacroKind::Bang => "Macro ", | |
101 | MacroKind::Attr => "Attribute Macro ", | |
102 | MacroKind::Derive => "Derive Macro ", | |
103 | }, | |
104 | clean::PrimitiveItem(..) => "Primitive Type ", | |
105 | clean::StaticItem(..) | clean::ForeignStaticItem(..) => "Static ", | |
106 | clean::ConstantItem(..) => "Constant ", | |
107 | clean::ForeignTypeItem => "Foreign Type ", | |
064997fb | 108 | clean::KeywordItem => "Keyword ", |
6a06907d XL |
109 | clean::OpaqueTyItem(..) => "Opaque Type ", |
110 | clean::TraitAliasItem(..) => "Trait Alias ", | |
111 | _ => { | |
112 | // We don't generate pages for any other type. | |
113 | unreachable!(); | |
114 | } | |
115 | }; | |
c295e0f8 | 116 | let mut stability_since_raw = Buffer::new(); |
6a06907d | 117 | render_stability_since_raw( |
c295e0f8 | 118 | &mut stability_since_raw, |
a2a8927a | 119 | item.stable_since(cx.tcx()), |
136023e0 | 120 | item.const_stability(cx.tcx()), |
6a06907d XL |
121 | None, |
122 | None, | |
123 | ); | |
c295e0f8 | 124 | let stability_since_raw: String = stability_since_raw.into_inner(); |
6a06907d | 125 | |
5099ac24 | 126 | // Write source tag |
6a06907d XL |
127 | // |
128 | // When this item is part of a `crate use` in a downstream crate, the | |
5099ac24 | 129 | // source link in the downstream documentation will actually come back to |
6a06907d XL |
130 | // this page, and this link will be auto-clicked. The `id` attribute is |
131 | // used to find the link to auto-click. | |
c295e0f8 XL |
132 | let src_href = |
133 | if cx.include_sources && !item.is_primitive() { cx.src_href(item) } else { None }; | |
134 | ||
135 | let path_components = if item.is_primitive() || item.is_keyword() { | |
136 | vec![] | |
137 | } else { | |
138 | let cur = &cx.current; | |
139 | let amt = if item.is_mod() { cur.len() - 1 } else { cur.len() }; | |
140 | cur.iter() | |
141 | .enumerate() | |
142 | .take(amt) | |
143 | .map(|(i, component)| PathComponent { | |
144 | path: "../".repeat(cur.len() - i - 1), | |
5099ac24 | 145 | name: *component, |
c295e0f8 XL |
146 | }) |
147 | .collect() | |
148 | }; | |
149 | ||
150 | let item_vars = ItemVars { | |
487cf647 FG |
151 | static_root_path: &page.get_static_root_path(), |
152 | clipboard_svg: &static_files::STATIC_FILES.clipboard_svg, | |
5099ac24 | 153 | typ, |
a2a8927a | 154 | name: item.name.as_ref().unwrap().as_str(), |
c295e0f8 | 155 | item_type: &item.type_().to_string(), |
5099ac24 | 156 | path_components, |
c295e0f8 XL |
157 | stability_since_raw: &stability_since_raw, |
158 | src_href: src_href.as_deref(), | |
159 | }; | |
6a06907d | 160 | |
5099ac24 | 161 | item_vars.render_into(buf).unwrap(); |
6a06907d | 162 | |
04454e1e | 163 | match &*item.kind { |
6a06907d XL |
164 | clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items), |
165 | clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => { | |
166 | item_function(buf, cx, item, f) | |
167 | } | |
168 | clean::TraitItem(ref t) => item_trait(buf, cx, item, t), | |
169 | clean::StructItem(ref s) => item_struct(buf, cx, item, s), | |
170 | clean::UnionItem(ref s) => item_union(buf, cx, item, s), | |
171 | clean::EnumItem(ref e) => item_enum(buf, cx, item, e), | |
04454e1e | 172 | clean::TypedefItem(ref t) => item_typedef(buf, cx, item, t), |
6a06907d XL |
173 | clean::MacroItem(ref m) => item_macro(buf, cx, item, m), |
174 | clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m), | |
175 | clean::PrimitiveItem(_) => item_primitive(buf, cx, item), | |
176 | clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => item_static(buf, cx, item, i), | |
177 | clean::ConstantItem(ref c) => item_constant(buf, cx, item, c), | |
178 | clean::ForeignTypeItem => item_foreign_type(buf, cx, item), | |
064997fb | 179 | clean::KeywordItem => item_keyword(buf, cx, item), |
6a06907d XL |
180 | clean::OpaqueTyItem(ref e) => item_opaque_ty(buf, cx, item, e), |
181 | clean::TraitAliasItem(ref ta) => item_trait_alias(buf, cx, item, ta), | |
182 | _ => { | |
183 | // We don't generate pages for any other type. | |
184 | unreachable!(); | |
185 | } | |
186 | } | |
487cf647 FG |
187 | |
188 | // Render notable-traits.js used for all methods in this module. | |
189 | if !cx.types_with_notable_traits.is_empty() { | |
190 | write!( | |
191 | buf, | |
192 | r#"<script type="text/json" id="notable-traits-data">{}</script>"#, | |
193 | notable_traits_json(cx.types_with_notable_traits.iter(), cx) | |
194 | ); | |
195 | cx.types_with_notable_traits.clear(); | |
196 | } | |
6a06907d XL |
197 | } |
198 | ||
cdc7bbd5 XL |
199 | /// For large structs, enums, unions, etc, determine whether to hide their fields |
200 | fn should_hide_fields(n_fields: usize) -> bool { | |
201 | n_fields > 12 | |
202 | } | |
203 | ||
136023e0 | 204 | fn toggle_open(w: &mut Buffer, text: impl fmt::Display) { |
cdc7bbd5 XL |
205 | write!( |
206 | w, | |
207 | "<details class=\"rustdoc-toggle type-contents-toggle\">\ | |
208 | <summary class=\"hideme\">\ | |
209 | <span>Show {}</span>\ | |
210 | </summary>", | |
211 | text | |
212 | ); | |
213 | } | |
214 | ||
215 | fn toggle_close(w: &mut Buffer) { | |
216 | w.write_str("</details>"); | |
217 | } | |
218 | ||
923072b8 | 219 | fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: &[clean::Item]) { |
c295e0f8 | 220 | document(w, cx, item, None, HeadingOffset::H2); |
6a06907d XL |
221 | |
222 | let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::<Vec<usize>>(); | |
223 | ||
224 | // the order of item types in the listing | |
225 | fn reorder(ty: ItemType) -> u8 { | |
226 | match ty { | |
227 | ItemType::ExternCrate => 0, | |
228 | ItemType::Import => 1, | |
229 | ItemType::Primitive => 2, | |
230 | ItemType::Module => 3, | |
231 | ItemType::Macro => 4, | |
232 | ItemType::Struct => 5, | |
233 | ItemType::Enum => 6, | |
234 | ItemType::Constant => 7, | |
235 | ItemType::Static => 8, | |
236 | ItemType::Trait => 9, | |
237 | ItemType::Function => 10, | |
238 | ItemType::Typedef => 12, | |
239 | ItemType::Union => 13, | |
240 | _ => 14 + ty as u8, | |
241 | } | |
242 | } | |
243 | ||
244 | fn cmp( | |
245 | i1: &clean::Item, | |
246 | i2: &clean::Item, | |
247 | idx1: usize, | |
248 | idx2: usize, | |
249 | tcx: TyCtxt<'_>, | |
250 | ) -> Ordering { | |
251 | let ty1 = i1.type_(); | |
252 | let ty2 = i2.type_(); | |
5099ac24 FG |
253 | if item_ty_to_section(ty1) != item_ty_to_section(ty2) |
254 | || (ty1 != ty2 && (ty1 == ItemType::ExternCrate || ty2 == ItemType::ExternCrate)) | |
255 | { | |
6a06907d XL |
256 | return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2)); |
257 | } | |
258 | let s1 = i1.stability(tcx).as_ref().map(|s| s.level); | |
259 | let s2 = i2.stability(tcx).as_ref().map(|s| s.level); | |
260 | if let (Some(a), Some(b)) = (s1, s2) { | |
261 | match (a.is_stable(), b.is_stable()) { | |
262 | (true, true) | (false, false) => {} | |
263 | (false, true) => return Ordering::Less, | |
264 | (true, false) => return Ordering::Greater, | |
265 | } | |
266 | } | |
a2a8927a XL |
267 | let lhs = i1.name.unwrap_or(kw::Empty); |
268 | let rhs = i2.name.unwrap_or(kw::Empty); | |
269 | compare_names(lhs.as_str(), rhs.as_str()) | |
6a06907d XL |
270 | } |
271 | ||
064997fb FG |
272 | match cx.shared.module_sorting { |
273 | ModuleSorting::Alphabetical => { | |
274 | indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2, cx.tcx())); | |
275 | } | |
276 | ModuleSorting::DeclarationOrder => {} | |
6a06907d XL |
277 | } |
278 | // This call is to remove re-export duplicates in cases such as: | |
279 | // | |
280 | // ``` | |
923072b8 FG |
281 | // pub(crate) mod foo { |
282 | // pub(crate) mod bar { | |
283 | // pub(crate) trait Double { fn foo(); } | |
6a06907d XL |
284 | // } |
285 | // } | |
286 | // | |
923072b8 FG |
287 | // pub(crate) use foo::bar::*; |
288 | // pub(crate) use foo::*; | |
6a06907d XL |
289 | // ``` |
290 | // | |
291 | // `Double` will appear twice in the generated docs. | |
292 | // | |
293 | // FIXME: This code is quite ugly and could be improved. Small issue: DefId | |
294 | // can be identical even if the elements are different (mostly in imports). | |
295 | // So in case this is an import, we keep everything by adding a "unique id" | |
296 | // (which is the position in the vector). | |
297 | indices.dedup_by_key(|i| { | |
298 | ( | |
04454e1e | 299 | items[*i].item_id, |
5099ac24 | 300 | if items[*i].name.is_some() { Some(full_path(cx, &items[*i])) } else { None }, |
6a06907d XL |
301 | items[*i].type_(), |
302 | if items[*i].is_import() { *i } else { 0 }, | |
303 | ) | |
304 | }); | |
305 | ||
306 | debug!("{:?}", indices); | |
5099ac24 | 307 | let mut last_section = None; |
94222f64 | 308 | |
6a06907d XL |
309 | for &idx in &indices { |
310 | let myitem = &items[idx]; | |
311 | if myitem.is_stripped() { | |
312 | continue; | |
313 | } | |
314 | ||
5099ac24 FG |
315 | let my_section = item_ty_to_section(myitem.type_()); |
316 | if Some(my_section) != last_section { | |
317 | if last_section.is_some() { | |
136023e0 | 318 | w.write_str(ITEM_TABLE_CLOSE); |
6a06907d | 319 | } |
5099ac24 | 320 | last_section = Some(my_section); |
6a06907d XL |
321 | write!( |
322 | w, | |
3c0e092e | 323 | "<h2 id=\"{id}\" class=\"small-section-header\">\ |
c295e0f8 | 324 | <a href=\"#{id}\">{name}</a>\ |
064997fb | 325 | </h2>{}", |
136023e0 | 326 | ITEM_TABLE_OPEN, |
5099ac24 FG |
327 | id = cx.derive_id(my_section.id().to_owned()), |
328 | name = my_section.name(), | |
6a06907d XL |
329 | ); |
330 | } | |
331 | ||
487cf647 | 332 | let tcx = cx.tcx(); |
6a06907d XL |
333 | match *myitem.kind { |
334 | clean::ExternCrateItem { ref src } => { | |
335 | use crate::html::format::anchor; | |
336 | ||
c295e0f8 | 337 | w.write_str(ITEM_TABLE_ROW_OPEN); |
6a06907d | 338 | match *src { |
5099ac24 | 339 | Some(src) => write!( |
6a06907d | 340 | w, |
136023e0 | 341 | "<div class=\"item-left\"><code>{}extern crate {} as {};", |
487cf647 | 342 | visibility_print_with_space(myitem.visibility(tcx), myitem.item_id, cx), |
04454e1e | 343 | anchor(myitem.item_id.expect_def_id(), src, cx), |
5099ac24 | 344 | myitem.name.unwrap(), |
6a06907d XL |
345 | ), |
346 | None => write!( | |
347 | w, | |
136023e0 | 348 | "<div class=\"item-left\"><code>{}extern crate {};", |
487cf647 | 349 | visibility_print_with_space(myitem.visibility(tcx), myitem.item_id, cx), |
04454e1e | 350 | anchor(myitem.item_id.expect_def_id(), myitem.name.unwrap(), cx), |
6a06907d XL |
351 | ), |
352 | } | |
136023e0 | 353 | w.write_str("</code></div>"); |
c295e0f8 | 354 | w.write_str(ITEM_TABLE_ROW_CLOSE); |
6a06907d XL |
355 | } |
356 | ||
357 | clean::ImportItem(ref import) => { | |
cdc7bbd5 | 358 | let (stab, stab_tags) = if let Some(import_def_id) = import.source.did { |
04454e1e | 359 | let ast_attrs = cx.tcx().get_attrs_unchecked(import_def_id); |
064997fb | 360 | let import_attrs = Box::new(clean::Attributes::from_ast(ast_attrs)); |
cdc7bbd5 XL |
361 | |
362 | // Just need an item with the correct def_id and attrs | |
363 | let import_item = clean::Item { | |
04454e1e | 364 | item_id: import_def_id.into(), |
cdc7bbd5 | 365 | attrs: import_attrs, |
c295e0f8 | 366 | cfg: ast_attrs.cfg(cx.tcx(), &cx.cache().hidden_cfg), |
cdc7bbd5 XL |
367 | ..myitem.clone() |
368 | }; | |
369 | ||
370 | let stab = import_item.stability_class(cx.tcx()); | |
371 | let stab_tags = Some(extra_info_tags(&import_item, item, cx.tcx())); | |
372 | (stab, stab_tags) | |
373 | } else { | |
374 | (None, None) | |
375 | }; | |
376 | ||
377 | let add = if stab.is_some() { " " } else { "" }; | |
378 | ||
c295e0f8 | 379 | w.write_str(ITEM_TABLE_ROW_OPEN); |
923072b8 FG |
380 | let id = match import.kind { |
381 | clean::ImportKind::Simple(s) => { | |
382 | format!(" id=\"{}\"", cx.derive_id(format!("reexport.{}", s))) | |
383 | } | |
384 | clean::ImportKind::Glob => String::new(), | |
385 | }; | |
f2b60f7d FG |
386 | let stab_tags = stab_tags.unwrap_or_default(); |
387 | let (stab_tags_before, stab_tags_after) = if stab_tags.is_empty() { | |
388 | ("", "") | |
389 | } else { | |
390 | ("<div class=\"item-right docblock-short\">", "</div>") | |
391 | }; | |
6a06907d XL |
392 | write!( |
393 | w, | |
04454e1e | 394 | "<div class=\"item-left {stab}{add}import-item\"{id}>\ |
136023e0 XL |
395 | <code>{vis}{imp}</code>\ |
396 | </div>\ | |
f2b60f7d | 397 | {stab_tags_before}{stab_tags}{stab_tags_after}", |
cdc7bbd5 | 398 | stab = stab.unwrap_or_default(), |
487cf647 | 399 | vis = visibility_print_with_space(myitem.visibility(tcx), myitem.item_id, cx), |
cdc7bbd5 | 400 | imp = import.print(cx), |
6a06907d | 401 | ); |
c295e0f8 | 402 | w.write_str(ITEM_TABLE_ROW_CLOSE); |
6a06907d XL |
403 | } |
404 | ||
405 | _ => { | |
406 | if myitem.name.is_none() { | |
407 | continue; | |
408 | } | |
409 | ||
410 | let unsafety_flag = match *myitem.kind { | |
5e7ed085 FG |
411 | clean::FunctionItem(_) | clean::ForeignFunctionItem(_) |
412 | if myitem.fn_header(cx.tcx()).unwrap().unsafety | |
413 | == hir::Unsafety::Unsafe => | |
6a06907d | 414 | { |
2b03887a | 415 | "<sup title=\"unsafe function\">⚠</sup>" |
6a06907d XL |
416 | } |
417 | _ => "", | |
418 | }; | |
419 | ||
420 | let stab = myitem.stability_class(cx.tcx()); | |
421 | let add = if stab.is_some() { " " } else { "" }; | |
422 | ||
487cf647 FG |
423 | let visibility_emoji = match myitem.visibility(tcx) { |
424 | Some(ty::Visibility::Restricted(_)) => { | |
5e7ed085 FG |
425 | "<span title=\"Restricted Visibility\"> 🔒</span> " |
426 | } | |
427 | _ => "", | |
428 | }; | |
429 | ||
6a06907d | 430 | let doc_value = myitem.doc_value().unwrap_or_default(); |
c295e0f8 | 431 | w.write_str(ITEM_TABLE_ROW_OPEN); |
f2b60f7d FG |
432 | let docs = MarkdownSummaryLine(&doc_value, &myitem.links(cx)).into_string(); |
433 | let (docs_before, docs_after) = if docs.is_empty() { | |
434 | ("", "") | |
435 | } else { | |
436 | ("<div class=\"item-right docblock-short\">", "</div>") | |
437 | }; | |
6a06907d XL |
438 | write!( |
439 | w, | |
136023e0 | 440 | "<div class=\"item-left {stab}{add}module-item\">\ |
064997fb FG |
441 | <a class=\"{class}\" href=\"{href}\" title=\"{title}\">{name}</a>\ |
442 | {visibility_emoji}\ | |
443 | {unsafety_flag}\ | |
444 | {stab_tags}\ | |
136023e0 | 445 | </div>\ |
f2b60f7d | 446 | {docs_before}{docs}{docs_after}", |
5099ac24 | 447 | name = myitem.name.unwrap(), |
5e7ed085 | 448 | visibility_emoji = visibility_emoji, |
6a06907d | 449 | stab_tags = extra_info_tags(myitem, item, cx.tcx()), |
6a06907d XL |
450 | class = myitem.type_(), |
451 | add = add, | |
cdc7bbd5 | 452 | stab = stab.unwrap_or_default(), |
6a06907d | 453 | unsafety_flag = unsafety_flag, |
a2a8927a | 454 | href = item_path(myitem.type_(), myitem.name.unwrap().as_str()), |
6a06907d XL |
455 | title = [full_path(cx, myitem), myitem.type_().to_string()] |
456 | .iter() | |
457 | .filter_map(|s| if !s.is_empty() { Some(s.as_str()) } else { None }) | |
458 | .collect::<Vec<_>>() | |
459 | .join(" "), | |
460 | ); | |
c295e0f8 | 461 | w.write_str(ITEM_TABLE_ROW_CLOSE); |
6a06907d XL |
462 | } |
463 | } | |
464 | } | |
465 | ||
5099ac24 | 466 | if last_section.is_some() { |
136023e0 | 467 | w.write_str(ITEM_TABLE_CLOSE); |
6a06907d XL |
468 | } |
469 | } | |
470 | ||
471 | /// Render the stability, deprecation and portability tags that are displayed in the item's summary | |
472 | /// at the module level. | |
473 | fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> String { | |
474 | let mut tags = String::new(); | |
475 | ||
476 | fn tag_html(class: &str, title: &str, contents: &str) -> String { | |
477 | format!(r#"<span class="stab {}" title="{}">{}</span>"#, class, Escape(title), contents) | |
478 | } | |
479 | ||
480 | // The trailing space after each tag is to space it properly against the rest of the docs. | |
481 | if let Some(depr) = &item.deprecation(tcx) { | |
482 | let mut message = "Deprecated"; | |
c295e0f8 | 483 | if !stability::deprecation_in_effect(depr) { |
6a06907d XL |
484 | message = "Deprecation planned"; |
485 | } | |
486 | tags += &tag_html("deprecated", "", message); | |
487 | } | |
488 | ||
489 | // The "rustc_private" crates are permanently unstable so it makes no sense | |
490 | // to render "unstable" everywhere. | |
923072b8 | 491 | if item.stability(tcx).as_ref().map(|s| s.is_unstable() && s.feature != sym::rustc_private) |
6a06907d XL |
492 | == Some(true) |
493 | { | |
494 | tags += &tag_html("unstable", "", "Experimental"); | |
495 | } | |
496 | ||
cdc7bbd5 | 497 | let cfg = match (&item.cfg, parent.cfg.as_ref()) { |
6a06907d XL |
498 | (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg), |
499 | (cfg, _) => cfg.as_deref().cloned(), | |
500 | }; | |
501 | ||
f2b60f7d | 502 | debug!("Portability name={:?} {:?} - {:?} = {:?}", item.name, item.cfg, parent.cfg, cfg); |
6a06907d XL |
503 | if let Some(ref cfg) = cfg { |
504 | tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html()); | |
505 | } | |
506 | ||
507 | tags | |
508 | } | |
509 | ||
923072b8 | 510 | fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &clean::Function) { |
487cf647 FG |
511 | let tcx = cx.tcx(); |
512 | let header = it.fn_header(tcx).expect("printing a function which isn't a function"); | |
513 | let constness = print_constness_with_space(&header.constness, it.const_stability(tcx)); | |
5e7ed085 FG |
514 | let unsafety = header.unsafety.print_with_space(); |
515 | let abi = print_abi_with_space(header.abi).to_string(); | |
516 | let asyncness = header.asyncness.print_with_space(); | |
487cf647 | 517 | let visibility = visibility_print_with_space(it.visibility(tcx), it.item_id, cx).to_string(); |
5099ac24 | 518 | let name = it.name.unwrap(); |
136023e0 XL |
519 | |
520 | let generics_len = format!("{:#}", f.generics.print(cx)).len(); | |
521 | let header_len = "fn ".len() | |
5e7ed085 | 522 | + visibility.len() |
136023e0 XL |
523 | + constness.len() |
524 | + asyncness.len() | |
525 | + unsafety.len() | |
526 | + abi.len() | |
527 | + name.as_str().len() | |
528 | + generics_len; | |
529 | ||
487cf647 FG |
530 | let notable_traits = |
531 | f.decl.output.as_return().and_then(|output| notable_traits_button(output, cx)); | |
532 | ||
2b03887a | 533 | wrap_into_item_decl(w, |w| { |
3c0e092e XL |
534 | wrap_item(w, "fn", |w| { |
535 | render_attributes_in_pre(w, it, ""); | |
536 | w.reserve(header_len); | |
537 | write!( | |
538 | w, | |
539 | "{vis}{constness}{asyncness}{unsafety}{abi}fn \ | |
540 | {name}{generics}{decl}{notable_traits}{where_clause}", | |
5e7ed085 | 541 | vis = visibility, |
3c0e092e XL |
542 | constness = constness, |
543 | asyncness = asyncness, | |
544 | unsafety = unsafety, | |
545 | abi = abi, | |
546 | name = name, | |
547 | generics = f.generics.print(cx), | |
064997fb | 548 | where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline), |
f2b60f7d | 549 | decl = f.decl.full_print(header_len, 0, cx), |
487cf647 | 550 | notable_traits = notable_traits.unwrap_or_default(), |
3c0e092e XL |
551 | ); |
552 | }); | |
94222f64 | 553 | }); |
487cf647 | 554 | document(w, cx, it, None, HeadingOffset::H2); |
6a06907d XL |
555 | } |
556 | ||
923072b8 | 557 | fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Trait) { |
487cf647 | 558 | let tcx = cx.tcx(); |
cdc7bbd5 | 559 | let bounds = bounds(&t.bounds, false, cx); |
04454e1e FG |
560 | let required_types = t.items.iter().filter(|m| m.is_ty_associated_type()).collect::<Vec<_>>(); |
561 | let provided_types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>(); | |
562 | let required_consts = t.items.iter().filter(|m| m.is_ty_associated_const()).collect::<Vec<_>>(); | |
563 | let provided_consts = t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>(); | |
564 | let required_methods = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>(); | |
565 | let provided_methods = t.items.iter().filter(|m| m.is_method()).collect::<Vec<_>>(); | |
566 | let count_types = required_types.len() + provided_types.len(); | |
567 | let count_consts = required_consts.len() + provided_consts.len(); | |
568 | let count_methods = required_methods.len() + provided_methods.len(); | |
487cf647 | 569 | let must_implement_one_of_functions = tcx.trait_def(t.def_id).must_implement_one_of.clone(); |
6a06907d XL |
570 | |
571 | // Output the trait definition | |
2b03887a | 572 | wrap_into_item_decl(w, |w| { |
94222f64 XL |
573 | wrap_item(w, "trait", |w| { |
574 | render_attributes_in_pre(w, it, ""); | |
575 | write!( | |
576 | w, | |
577 | "{}{}{}trait {}{}{}", | |
487cf647 FG |
578 | visibility_print_with_space(it.visibility(tcx), it.item_id, cx), |
579 | t.unsafety(tcx).print_with_space(), | |
580 | if t.is_auto(tcx) { "auto " } else { "" }, | |
5099ac24 | 581 | it.name.unwrap(), |
94222f64 XL |
582 | t.generics.print(cx), |
583 | bounds | |
584 | ); | |
6a06907d | 585 | |
94222f64 | 586 | if !t.generics.where_predicates.is_empty() { |
064997fb | 587 | write!(w, "{}", print_where_clause(&t.generics, cx, 0, Ending::Newline)); |
94222f64 XL |
588 | } else { |
589 | w.write_str(" "); | |
6a06907d | 590 | } |
6a06907d | 591 | |
94222f64 XL |
592 | if t.items.is_empty() { |
593 | w.write_str("{ }"); | |
594 | } else { | |
595 | // FIXME: we should be using a derived_id for the Anchors here | |
596 | w.write_str("{\n"); | |
597 | let mut toggle = false; | |
598 | ||
599 | // If there are too many associated types, hide _everything_ | |
600 | if should_hide_fields(count_types) { | |
601 | toggle = true; | |
602 | toggle_open( | |
603 | w, | |
604 | format_args!( | |
605 | "{} associated items", | |
606 | count_types + count_consts + count_methods | |
607 | ), | |
608 | ); | |
6a06907d | 609 | } |
04454e1e FG |
610 | for types in [&required_types, &provided_types] { |
611 | for t in types { | |
612 | render_assoc_item( | |
613 | w, | |
614 | t, | |
615 | AssocItemLink::Anchor(None), | |
616 | ItemType::Trait, | |
617 | cx, | |
618 | RenderMode::Normal, | |
619 | ); | |
620 | w.write_str(";\n"); | |
621 | } | |
94222f64 XL |
622 | } |
623 | // If there are too many associated constants, hide everything after them | |
624 | // We also do this if the types + consts is large because otherwise we could | |
625 | // render a bunch of types and _then_ a bunch of consts just because both were | |
626 | // _just_ under the limit | |
627 | if !toggle && should_hide_fields(count_types + count_consts) { | |
628 | toggle = true; | |
629 | toggle_open( | |
630 | w, | |
631 | format_args!( | |
632 | "{} associated constant{} and {} method{}", | |
633 | count_consts, | |
634 | pluralize(count_consts), | |
635 | count_methods, | |
636 | pluralize(count_methods), | |
637 | ), | |
638 | ); | |
639 | } | |
04454e1e | 640 | if count_types != 0 && (count_consts != 0 || count_methods != 0) { |
94222f64 XL |
641 | w.write_str("\n"); |
642 | } | |
04454e1e FG |
643 | for consts in [&required_consts, &provided_consts] { |
644 | for c in consts { | |
645 | render_assoc_item( | |
646 | w, | |
647 | c, | |
648 | AssocItemLink::Anchor(None), | |
649 | ItemType::Trait, | |
650 | cx, | |
651 | RenderMode::Normal, | |
652 | ); | |
653 | w.write_str(";\n"); | |
654 | } | |
94222f64 XL |
655 | } |
656 | if !toggle && should_hide_fields(count_methods) { | |
657 | toggle = true; | |
658 | toggle_open(w, format_args!("{} methods", count_methods)); | |
659 | } | |
04454e1e | 660 | if count_consts != 0 && count_methods != 0 { |
94222f64 XL |
661 | w.write_str("\n"); |
662 | } | |
04454e1e | 663 | for (pos, m) in required_methods.iter().enumerate() { |
a2a8927a XL |
664 | render_assoc_item( |
665 | w, | |
666 | m, | |
667 | AssocItemLink::Anchor(None), | |
668 | ItemType::Trait, | |
669 | cx, | |
670 | RenderMode::Normal, | |
671 | ); | |
94222f64 XL |
672 | w.write_str(";\n"); |
673 | ||
04454e1e | 674 | if pos < required_methods.len() - 1 { |
5e7ed085 | 675 | w.write_str("<span class=\"item-spacer\"></span>"); |
6a06907d | 676 | } |
94222f64 | 677 | } |
04454e1e | 678 | if !required_methods.is_empty() && !provided_methods.is_empty() { |
94222f64 XL |
679 | w.write_str("\n"); |
680 | } | |
04454e1e | 681 | for (pos, m) in provided_methods.iter().enumerate() { |
a2a8927a XL |
682 | render_assoc_item( |
683 | w, | |
684 | m, | |
685 | AssocItemLink::Anchor(None), | |
686 | ItemType::Trait, | |
687 | cx, | |
688 | RenderMode::Normal, | |
689 | ); | |
94222f64 XL |
690 | match *m.kind { |
691 | clean::MethodItem(ref inner, _) | |
692 | if !inner.generics.where_predicates.is_empty() => | |
693 | { | |
694 | w.write_str(",\n { ... }\n"); | |
695 | } | |
696 | _ => { | |
697 | w.write_str(" { ... }\n"); | |
698 | } | |
699 | } | |
04454e1e FG |
700 | |
701 | if pos < provided_methods.len() - 1 { | |
5e7ed085 | 702 | w.write_str("<span class=\"item-spacer\"></span>"); |
6a06907d XL |
703 | } |
704 | } | |
94222f64 XL |
705 | if toggle { |
706 | toggle_close(w); | |
6a06907d | 707 | } |
94222f64 | 708 | w.write_str("}"); |
6a06907d | 709 | } |
94222f64 | 710 | }); |
6a06907d XL |
711 | }); |
712 | ||
713 | // Trait documentation | |
c295e0f8 | 714 | document(w, cx, it, None, HeadingOffset::H2); |
6a06907d XL |
715 | |
716 | fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) { | |
717 | write!( | |
718 | w, | |
719 | "<h2 id=\"{0}\" class=\"small-section-header\">\ | |
487cf647 | 720 | {1}<a href=\"#{0}\" class=\"anchor\">§</a>\ |
6a06907d XL |
721 | </h2>{2}", |
722 | id, title, extra_content | |
723 | ) | |
724 | } | |
725 | ||
923072b8 | 726 | fn trait_item(w: &mut Buffer, cx: &mut Context<'_>, m: &clean::Item, t: &clean::Item) { |
5099ac24 | 727 | let name = m.name.unwrap(); |
6a06907d XL |
728 | info!("Documenting {} on {:?}", name, t.name); |
729 | let item_type = m.type_(); | |
730 | let id = cx.derive_id(format!("{}.{}", item_type, name)); | |
17df50a5 | 731 | let mut content = Buffer::empty_from(w); |
c295e0f8 | 732 | document(&mut content, cx, m, Some(t), HeadingOffset::H5); |
17df50a5 XL |
733 | let toggled = !content.is_empty(); |
734 | if toggled { | |
2b03887a | 735 | write!(w, "<details class=\"rustdoc-toggle method-toggle\" open><summary>"); |
17df50a5 | 736 | } |
2b03887a | 737 | write!(w, "<section id=\"{}\" class=\"method has-srclink\">", id); |
f2b60f7d | 738 | render_rightside(w, cx, m, t, RenderMode::Normal); |
17df50a5 | 739 | write!(w, "<h4 class=\"code-header\">"); |
a2a8927a XL |
740 | render_assoc_item( |
741 | w, | |
742 | m, | |
743 | AssocItemLink::Anchor(Some(&id)), | |
744 | ItemType::Impl, | |
745 | cx, | |
746 | RenderMode::Normal, | |
747 | ); | |
17df50a5 | 748 | w.write_str("</h4>"); |
2b03887a | 749 | w.write_str("</section>"); |
17df50a5 XL |
750 | if toggled { |
751 | write!(w, "</summary>"); | |
752 | w.push_buffer(content); | |
753 | write!(w, "</details>"); | |
754 | } | |
6a06907d XL |
755 | } |
756 | ||
04454e1e FG |
757 | if !required_types.is_empty() { |
758 | write_small_section_header( | |
759 | w, | |
760 | "required-associated-types", | |
761 | "Required Associated Types", | |
762 | "<div class=\"methods\">", | |
763 | ); | |
764 | for t in required_types { | |
765 | trait_item(w, cx, t, it); | |
766 | } | |
767 | w.write_str("</div>"); | |
768 | } | |
769 | if !provided_types.is_empty() { | |
6a06907d XL |
770 | write_small_section_header( |
771 | w, | |
04454e1e FG |
772 | "provided-associated-types", |
773 | "Provided Associated Types", | |
6a06907d XL |
774 | "<div class=\"methods\">", |
775 | ); | |
04454e1e | 776 | for t in provided_types { |
6a06907d XL |
777 | trait_item(w, cx, t, it); |
778 | } | |
17df50a5 | 779 | w.write_str("</div>"); |
6a06907d XL |
780 | } |
781 | ||
04454e1e | 782 | if !required_consts.is_empty() { |
6a06907d XL |
783 | write_small_section_header( |
784 | w, | |
04454e1e FG |
785 | "required-associated-consts", |
786 | "Required Associated Constants", | |
6a06907d XL |
787 | "<div class=\"methods\">", |
788 | ); | |
04454e1e FG |
789 | for t in required_consts { |
790 | trait_item(w, cx, t, it); | |
791 | } | |
792 | w.write_str("</div>"); | |
793 | } | |
794 | if !provided_consts.is_empty() { | |
795 | write_small_section_header( | |
796 | w, | |
797 | "provided-associated-consts", | |
798 | "Provided Associated Constants", | |
799 | "<div class=\"methods\">", | |
800 | ); | |
801 | for t in provided_consts { | |
6a06907d XL |
802 | trait_item(w, cx, t, it); |
803 | } | |
17df50a5 | 804 | w.write_str("</div>"); |
6a06907d XL |
805 | } |
806 | ||
807 | // Output the documentation for each function individually | |
064997fb | 808 | if !required_methods.is_empty() || must_implement_one_of_functions.is_some() { |
6a06907d XL |
809 | write_small_section_header( |
810 | w, | |
811 | "required-methods", | |
04454e1e | 812 | "Required Methods", |
6a06907d XL |
813 | "<div class=\"methods\">", |
814 | ); | |
064997fb FG |
815 | |
816 | if let Some(list) = must_implement_one_of_functions.as_deref() { | |
817 | write!( | |
818 | w, | |
819 | "<div class=\"stab must_implement\">At least one of the `{}` methods is required.</div>", | |
820 | list.iter().join("`, `") | |
821 | ); | |
822 | } | |
823 | ||
04454e1e | 824 | for m in required_methods { |
6a06907d XL |
825 | trait_item(w, cx, m, it); |
826 | } | |
17df50a5 | 827 | w.write_str("</div>"); |
6a06907d | 828 | } |
04454e1e | 829 | if !provided_methods.is_empty() { |
6a06907d XL |
830 | write_small_section_header( |
831 | w, | |
832 | "provided-methods", | |
04454e1e | 833 | "Provided Methods", |
6a06907d XL |
834 | "<div class=\"methods\">", |
835 | ); | |
04454e1e | 836 | for m in provided_methods { |
6a06907d XL |
837 | trait_item(w, cx, m, it); |
838 | } | |
17df50a5 | 839 | w.write_str("</div>"); |
6a06907d XL |
840 | } |
841 | ||
842 | // If there are methods directly on this trait object, render them here. | |
04454e1e | 843 | render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All); |
6a06907d | 844 | |
923072b8 FG |
845 | let cloned_shared = Rc::clone(&cx.shared); |
846 | let cache = &cloned_shared.cache; | |
04454e1e FG |
847 | let mut extern_crates = FxHashSet::default(); |
848 | if let Some(implementors) = cache.implementors.get(&it.item_id.expect_def_id()) { | |
6a06907d XL |
849 | // The DefId is for the first Type found with that name. The bool is |
850 | // if any Types with the same name but different DefId have been found. | |
851 | let mut implementor_dups: FxHashMap<Symbol, (DefId, bool)> = FxHashMap::default(); | |
852 | for implementor in implementors { | |
923072b8 | 853 | if let Some(did) = implementor.inner_impl().for_.without_borrowed_ref().def_id(cache) && |
04454e1e FG |
854 | !did.is_local() { |
855 | extern_crates.insert(did.krate); | |
856 | } | |
857 | match implementor.inner_impl().for_.without_borrowed_ref() { | |
858 | clean::Type::Path { ref path } if !path.is_assoc_ty() => { | |
3c0e092e | 859 | let did = path.def_id(); |
6a06907d XL |
860 | let &mut (prev_did, ref mut has_duplicates) = |
861 | implementor_dups.entry(path.last()).or_insert((did, false)); | |
862 | if prev_did != did { | |
863 | *has_duplicates = true; | |
864 | } | |
865 | } | |
866 | _ => {} | |
867 | } | |
868 | } | |
869 | ||
923072b8 FG |
870 | let (local, foreign) = |
871 | implementors.iter().partition::<Vec<_>, _>(|i| i.is_on_local_type(cx)); | |
6a06907d XL |
872 | |
873 | let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) = | |
3c0e092e | 874 | local.iter().partition(|i| i.inner_impl().kind.is_auto()); |
6a06907d | 875 | |
cdc7bbd5 XL |
876 | synthetic.sort_by(|a, b| compare_impl(a, b, cx)); |
877 | concrete.sort_by(|a, b| compare_impl(a, b, cx)); | |
6a06907d XL |
878 | |
879 | if !foreign.is_empty() { | |
880 | write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", ""); | |
881 | ||
882 | for implementor in foreign { | |
17df50a5 XL |
883 | let provided_methods = implementor.inner_impl().provided_trait_methods(cx.tcx()); |
884 | let assoc_link = | |
04454e1e | 885 | AssocItemLink::GotoSource(implementor.impl_item.item_id, &provided_methods); |
6a06907d XL |
886 | render_impl( |
887 | w, | |
888 | cx, | |
3c0e092e | 889 | implementor, |
6a06907d XL |
890 | it, |
891 | assoc_link, | |
892 | RenderMode::Normal, | |
6a06907d | 893 | None, |
6a06907d | 894 | &[], |
136023e0 XL |
895 | ImplRenderingParameters { |
896 | show_def_docs: false, | |
136023e0 XL |
897 | show_default_items: false, |
898 | show_non_assoc_items: true, | |
899 | toggle_open_by_default: false, | |
900 | }, | |
6a06907d XL |
901 | ); |
902 | } | |
6a06907d XL |
903 | } |
904 | ||
905 | write_small_section_header( | |
906 | w, | |
907 | "implementors", | |
908 | "Implementors", | |
2b03887a | 909 | "<div id=\"implementors-list\">", |
6a06907d XL |
910 | ); |
911 | for implementor in concrete { | |
912 | render_implementor(cx, implementor, it, w, &implementor_dups, &[]); | |
913 | } | |
17df50a5 | 914 | w.write_str("</div>"); |
6a06907d | 915 | |
064997fb | 916 | if t.is_auto(cx.tcx()) { |
6a06907d XL |
917 | write_small_section_header( |
918 | w, | |
919 | "synthetic-implementors", | |
920 | "Auto implementors", | |
2b03887a | 921 | "<div id=\"synthetic-implementors-list\">", |
6a06907d XL |
922 | ); |
923 | for implementor in synthetic { | |
924 | render_implementor( | |
925 | cx, | |
926 | implementor, | |
927 | it, | |
928 | w, | |
929 | &implementor_dups, | |
94222f64 | 930 | &collect_paths_for_type(implementor.inner_impl().for_.clone(), cache), |
6a06907d XL |
931 | ); |
932 | } | |
17df50a5 | 933 | w.write_str("</div>"); |
6a06907d XL |
934 | } |
935 | } else { | |
936 | // even without any implementations to write in, we still want the heading and list, so the | |
937 | // implementors javascript file pulled in below has somewhere to write the impls into | |
938 | write_small_section_header( | |
939 | w, | |
940 | "implementors", | |
941 | "Implementors", | |
2b03887a | 942 | "<div id=\"implementors-list\"></div>", |
6a06907d | 943 | ); |
6a06907d | 944 | |
064997fb | 945 | if t.is_auto(cx.tcx()) { |
6a06907d XL |
946 | write_small_section_header( |
947 | w, | |
948 | "synthetic-implementors", | |
949 | "Auto implementors", | |
2b03887a | 950 | "<div id=\"synthetic-implementors-list\"></div>", |
6a06907d | 951 | ); |
6a06907d XL |
952 | } |
953 | } | |
954 | ||
04454e1e FG |
955 | // Include implementors in crates that depend on the current crate. |
956 | // | |
957 | // This is complicated by the way rustdoc is invoked, which is basically | |
958 | // the same way rustc is invoked: it gets called, one at a time, for each | |
959 | // crate. When building the rustdocs for the current crate, rustdoc can | |
960 | // see crate metadata for its dependencies, but cannot see metadata for its | |
961 | // dependents. | |
962 | // | |
963 | // To make this work, we generate a "hook" at this stage, and our | |
964 | // dependents can "plug in" to it when they build. For simplicity's sake, | |
965 | // it's [JSONP]: a JavaScript file with the data we need (and can parse), | |
966 | // surrounded by a tiny wrapper that the Rust side ignores, but allows the | |
967 | // JavaScript side to include without having to worry about Same Origin | |
968 | // Policy. The code for *that* is in `write_shared.rs`. | |
969 | // | |
970 | // This is further complicated by `#[doc(inline)]`. We want all copies | |
971 | // of an inlined trait to reference the same JS file, to address complex | |
972 | // dependency graphs like this one (lower crates depend on higher crates): | |
973 | // | |
974 | // ```text | |
975 | // -------------------------------------------- | |
976 | // | crate A: trait Foo | | |
977 | // -------------------------------------------- | |
978 | // | | | |
979 | // -------------------------------- | | |
980 | // | crate B: impl A::Foo for Bar | | | |
981 | // -------------------------------- | | |
982 | // | | | |
983 | // --------------------------------------------- | |
984 | // | crate C: #[doc(inline)] use A::Foo as Baz | | |
985 | // | impl Baz for Quux | | |
986 | // --------------------------------------------- | |
987 | // ``` | |
988 | // | |
989 | // Basically, we want `C::Baz` and `A::Foo` to show the same set of | |
990 | // impls, which is easier if they both treat `/implementors/A/trait.Foo.js` | |
991 | // as the Single Source of Truth. | |
992 | // | |
993 | // We also want the `impl Baz for Quux` to be written to | |
994 | // `trait.Foo.js`. However, when we generate plain HTML for `C::Baz`, | |
995 | // we're going to want to generate plain HTML for `impl Baz for Quux` too, | |
996 | // because that'll load faster, and it's better for SEO. And we don't want | |
997 | // the same impl to show up twice on the same page. | |
998 | // | |
999 | // To make this work, the implementors JS file has a structure kinda | |
1000 | // like this: | |
1001 | // | |
1002 | // ```js | |
1003 | // JSONP({ | |
1004 | // "B": {"impl A::Foo for Bar"}, | |
1005 | // "C": {"impl Baz for Quux"}, | |
1006 | // }); | |
1007 | // ``` | |
1008 | // | |
1009 | // First of all, this means we can rebuild a crate, and it'll replace its own | |
1010 | // data if something changes. That is, `rustdoc` is idempotent. The other | |
1011 | // advantage is that we can list the crates that get included in the HTML, | |
1012 | // and ignore them when doing the JavaScript-based part of rendering. | |
1013 | // So C's HTML will have something like this: | |
1014 | // | |
1015 | // ```html | |
f2b60f7d | 1016 | // <script src="/implementors/A/trait.Foo.js" |
04454e1e FG |
1017 | // data-ignore-extern-crates="A,B" async></script> |
1018 | // ``` | |
1019 | // | |
1020 | // And, when the JS runs, anything in data-ignore-extern-crates is known | |
1021 | // to already be in the HTML, and will be ignored. | |
1022 | // | |
1023 | // [JSONP]: https://en.wikipedia.org/wiki/JSONP | |
5099ac24 FG |
1024 | let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..") |
1025 | .take(cx.current.len()) | |
1026 | .chain(std::iter::once("implementors")) | |
1027 | .collect(); | |
04454e1e FG |
1028 | if let Some(did) = it.item_id.as_def_id() && |
1029 | let get_extern = { || cache.external_paths.get(&did).map(|s| s.0.clone()) } && | |
1030 | let Some(fqp) = cache.exact_paths.get(&did).cloned().or_else(get_extern) { | |
1031 | js_src_path.extend(fqp[..fqp.len() - 1].iter().copied()); | |
1032 | js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), fqp.last().unwrap())); | |
5099ac24 | 1033 | } else { |
04454e1e FG |
1034 | js_src_path.extend(cx.current.iter().copied()); |
1035 | js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), it.name.unwrap())); | |
5099ac24 | 1036 | } |
04454e1e FG |
1037 | let extern_crates = extern_crates |
1038 | .into_iter() | |
487cf647 | 1039 | .map(|cnum| tcx.crate_name(cnum).to_string()) |
04454e1e FG |
1040 | .collect::<Vec<_>>() |
1041 | .join(","); | |
f2b60f7d FG |
1042 | let (extern_before, extern_after) = |
1043 | if extern_crates.is_empty() { ("", "") } else { (" data-ignore-extern-crates=\"", "\"") }; | |
6a06907d XL |
1044 | write!( |
1045 | w, | |
f2b60f7d | 1046 | "<script src=\"{src}\"{extern_before}{extern_crates}{extern_after} async></script>", |
5099ac24 | 1047 | src = js_src_path.finish(), |
6a06907d XL |
1048 | ); |
1049 | } | |
1050 | ||
923072b8 | 1051 | fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TraitAlias) { |
2b03887a | 1052 | wrap_into_item_decl(w, |w| { |
3c0e092e XL |
1053 | wrap_item(w, "trait-alias", |w| { |
1054 | render_attributes_in_pre(w, it, ""); | |
1055 | write!( | |
1056 | w, | |
1057 | "trait {}{}{} = {};", | |
5099ac24 | 1058 | it.name.unwrap(), |
3c0e092e | 1059 | t.generics.print(cx), |
064997fb | 1060 | print_where_clause(&t.generics, cx, 0, Ending::Newline), |
3c0e092e XL |
1061 | bounds(&t.bounds, true, cx) |
1062 | ); | |
1063 | }); | |
94222f64 | 1064 | }); |
6a06907d | 1065 | |
c295e0f8 | 1066 | document(w, cx, it, None, HeadingOffset::H2); |
6a06907d XL |
1067 | |
1068 | // Render any items associated directly to this alias, as otherwise they | |
1069 | // won't be visible anywhere in the docs. It would be nice to also show | |
1070 | // associated items from the aliased type (see discussion in #32077), but | |
1071 | // we need #14072 to make sense of the generics. | |
04454e1e | 1072 | render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All) |
6a06907d XL |
1073 | } |
1074 | ||
923072b8 | 1075 | fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { |
2b03887a | 1076 | wrap_into_item_decl(w, |w| { |
3c0e092e XL |
1077 | wrap_item(w, "opaque", |w| { |
1078 | render_attributes_in_pre(w, it, ""); | |
1079 | write!( | |
1080 | w, | |
1081 | "type {}{}{where_clause} = impl {bounds};", | |
5099ac24 | 1082 | it.name.unwrap(), |
3c0e092e | 1083 | t.generics.print(cx), |
064997fb | 1084 | where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), |
3c0e092e XL |
1085 | bounds = bounds(&t.bounds, false, cx), |
1086 | ); | |
1087 | }); | |
94222f64 | 1088 | }); |
6a06907d | 1089 | |
c295e0f8 | 1090 | document(w, cx, it, None, HeadingOffset::H2); |
6a06907d XL |
1091 | |
1092 | // Render any items associated directly to this alias, as otherwise they | |
1093 | // won't be visible anywhere in the docs. It would be nice to also show | |
1094 | // associated items from the aliased type (see discussion in #32077), but | |
1095 | // we need #14072 to make sense of the generics. | |
04454e1e | 1096 | render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All) |
6a06907d XL |
1097 | } |
1098 | ||
923072b8 | 1099 | fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) { |
04454e1e | 1100 | fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) { |
3c0e092e XL |
1101 | wrap_item(w, "typedef", |w| { |
1102 | render_attributes_in_pre(w, it, ""); | |
487cf647 | 1103 | write!(w, "{}", visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx)); |
3c0e092e XL |
1104 | write!( |
1105 | w, | |
1106 | "type {}{}{where_clause} = {type_};", | |
5099ac24 | 1107 | it.name.unwrap(), |
3c0e092e | 1108 | t.generics.print(cx), |
064997fb | 1109 | where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), |
3c0e092e XL |
1110 | type_ = t.type_.print(cx), |
1111 | ); | |
1112 | }); | |
1113 | } | |
1114 | ||
2b03887a | 1115 | wrap_into_item_decl(w, |w| write_content(w, cx, it, t)); |
6a06907d | 1116 | |
c295e0f8 | 1117 | document(w, cx, it, None, HeadingOffset::H2); |
6a06907d | 1118 | |
04454e1e | 1119 | let def_id = it.item_id.expect_def_id(); |
6a06907d XL |
1120 | // Render any items associated directly to this alias, as otherwise they |
1121 | // won't be visible anywhere in the docs. It would be nice to also show | |
1122 | // associated items from the aliased type (see discussion in #32077), but | |
1123 | // we need #14072 to make sense of the generics. | |
17df50a5 | 1124 | render_assoc_items(w, cx, it, def_id, AssocItemRender::All); |
a2a8927a | 1125 | document_type_layout(w, cx, def_id); |
6a06907d XL |
1126 | } |
1127 | ||
923072b8 | 1128 | fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) { |
2b03887a | 1129 | wrap_into_item_decl(w, |w| { |
94222f64 XL |
1130 | wrap_item(w, "union", |w| { |
1131 | render_attributes_in_pre(w, it, ""); | |
1132 | render_union(w, it, Some(&s.generics), &s.fields, "", cx); | |
1133 | }); | |
6a06907d XL |
1134 | }); |
1135 | ||
c295e0f8 | 1136 | document(w, cx, it, None, HeadingOffset::H2); |
17df50a5 | 1137 | |
6a06907d XL |
1138 | let mut fields = s |
1139 | .fields | |
1140 | .iter() | |
1141 | .filter_map(|f| match *f.kind { | |
1142 | clean::StructFieldItem(ref ty) => Some((f, ty)), | |
1143 | _ => None, | |
1144 | }) | |
1145 | .peekable(); | |
1146 | if fields.peek().is_some() { | |
1147 | write!( | |
1148 | w, | |
136023e0 | 1149 | "<h2 id=\"fields\" class=\"fields small-section-header\">\ |
487cf647 | 1150 | Fields<a href=\"#fields\" class=\"anchor\">§</a>\ |
064997fb | 1151 | </h2>" |
6a06907d XL |
1152 | ); |
1153 | for (field, ty) in fields { | |
5099ac24 | 1154 | let name = field.name.expect("union field name"); |
6a06907d XL |
1155 | let id = format!("{}.{}", ItemType::StructField, name); |
1156 | write!( | |
1157 | w, | |
1158 | "<span id=\"{id}\" class=\"{shortty} small-section-header\">\ | |
487cf647 | 1159 | <a href=\"#{id}\" class=\"anchor field\">§</a>\ |
6a06907d XL |
1160 | <code>{name}: {ty}</code>\ |
1161 | </span>", | |
1162 | id = id, | |
1163 | name = name, | |
1164 | shortty = ItemType::StructField, | |
cdc7bbd5 | 1165 | ty = ty.print(cx), |
6a06907d XL |
1166 | ); |
1167 | if let Some(stability_class) = field.stability_class(cx.tcx()) { | |
1168 | write!(w, "<span class=\"stab {stab}\"></span>", stab = stability_class); | |
1169 | } | |
c295e0f8 | 1170 | document(w, cx, field, Some(it), HeadingOffset::H3); |
6a06907d XL |
1171 | } |
1172 | } | |
04454e1e | 1173 | let def_id = it.item_id.expect_def_id(); |
17df50a5 XL |
1174 | render_assoc_items(w, cx, it, def_id, AssocItemRender::All); |
1175 | document_type_layout(w, cx, def_id); | |
6a06907d XL |
1176 | } |
1177 | ||
94222f64 XL |
1178 | fn print_tuple_struct_fields(w: &mut Buffer, cx: &Context<'_>, s: &[clean::Item]) { |
1179 | for (i, ty) in s.iter().enumerate() { | |
1180 | if i > 0 { | |
1181 | w.write_str(", "); | |
1182 | } | |
1183 | match *ty.kind { | |
1184 | clean::StrippedItem(box clean::StructFieldItem(_)) => w.write_str("_"), | |
1185 | clean::StructFieldItem(ref ty) => write!(w, "{}", ty.print(cx)), | |
1186 | _ => unreachable!(), | |
1187 | } | |
1188 | } | |
1189 | } | |
1190 | ||
923072b8 | 1191 | fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) { |
487cf647 | 1192 | let tcx = cx.tcx(); |
923072b8 | 1193 | let count_variants = e.variants().count(); |
2b03887a | 1194 | wrap_into_item_decl(w, |w| { |
94222f64 XL |
1195 | wrap_item(w, "enum", |w| { |
1196 | render_attributes_in_pre(w, it, ""); | |
1197 | write!( | |
1198 | w, | |
064997fb | 1199 | "{}enum {}{}", |
487cf647 | 1200 | visibility_print_with_space(it.visibility(tcx), it.item_id, cx), |
5099ac24 | 1201 | it.name.unwrap(), |
94222f64 | 1202 | e.generics.print(cx), |
94222f64 | 1203 | ); |
064997fb FG |
1204 | if !print_where_clause_and_check(w, &e.generics, cx) { |
1205 | // If there wasn't a `where` clause, we add a whitespace. | |
1206 | w.write_str(" "); | |
1207 | } | |
1208 | ||
923072b8 FG |
1209 | let variants_stripped = e.has_stripped_entries(); |
1210 | if count_variants == 0 && !variants_stripped { | |
064997fb | 1211 | w.write_str("{}"); |
94222f64 | 1212 | } else { |
064997fb | 1213 | w.write_str("{\n"); |
94222f64 XL |
1214 | let toggle = should_hide_fields(count_variants); |
1215 | if toggle { | |
1216 | toggle_open(w, format_args!("{} variants", count_variants)); | |
1217 | } | |
923072b8 | 1218 | for v in e.variants() { |
94222f64 | 1219 | w.write_str(" "); |
5099ac24 | 1220 | let name = v.name.unwrap(); |
94222f64 XL |
1221 | match *v.kind { |
1222 | clean::VariantItem(ref var) => match var { | |
f2b60f7d FG |
1223 | // FIXME(#101337): Show discriminant |
1224 | clean::Variant::CLike(..) => write!(w, "{}", name), | |
94222f64 XL |
1225 | clean::Variant::Tuple(ref s) => { |
1226 | write!(w, "{}(", name); | |
1227 | print_tuple_struct_fields(w, cx, s); | |
1228 | w.write_str(")"); | |
6a06907d | 1229 | } |
94222f64 XL |
1230 | clean::Variant::Struct(ref s) => { |
1231 | render_struct( | |
1232 | w, | |
1233 | v, | |
1234 | None, | |
487cf647 | 1235 | s.ctor_kind, |
94222f64 XL |
1236 | &s.fields, |
1237 | " ", | |
1238 | false, | |
1239 | cx, | |
1240 | ); | |
1241 | } | |
1242 | }, | |
1243 | _ => unreachable!(), | |
1244 | } | |
1245 | w.write_str(",\n"); | |
6a06907d | 1246 | } |
6a06907d | 1247 | |
923072b8 | 1248 | if variants_stripped { |
94222f64 XL |
1249 | w.write_str(" // some variants omitted\n"); |
1250 | } | |
1251 | if toggle { | |
1252 | toggle_close(w); | |
1253 | } | |
1254 | w.write_str("}"); | |
cdc7bbd5 | 1255 | } |
94222f64 | 1256 | }); |
6a06907d XL |
1257 | }); |
1258 | ||
c295e0f8 | 1259 | document(w, cx, it, None, HeadingOffset::H2); |
17df50a5 | 1260 | |
923072b8 | 1261 | if count_variants != 0 { |
6a06907d XL |
1262 | write!( |
1263 | w, | |
136023e0 | 1264 | "<h2 id=\"variants\" class=\"variants small-section-header\">\ |
487cf647 | 1265 | Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\ |
064997fb | 1266 | </h2>", |
6a06907d XL |
1267 | document_non_exhaustive_header(it) |
1268 | ); | |
1269 | document_non_exhaustive(w, it); | |
487cf647 | 1270 | write!(w, "<div class=\"variants\">"); |
923072b8 | 1271 | for variant in e.variants() { |
5099ac24 | 1272 | let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap())); |
6a06907d XL |
1273 | write!( |
1274 | w, | |
487cf647 FG |
1275 | "<section id=\"{id}\" class=\"variant\">\ |
1276 | <a href=\"#{id}\" class=\"anchor\">§</a>", | |
6a06907d | 1277 | id = id, |
6a06907d | 1278 | ); |
487cf647 FG |
1279 | render_stability_since_raw_with_extra( |
1280 | w, | |
1281 | variant.stable_since(tcx), | |
1282 | variant.const_stability(tcx), | |
1283 | it.stable_since(tcx), | |
1284 | it.const_stable_since(tcx), | |
1285 | " rightside", | |
1286 | ); | |
1287 | write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap()); | |
94222f64 | 1288 | if let clean::VariantItem(clean::Variant::Tuple(ref s)) = *variant.kind { |
6a06907d | 1289 | w.write_str("("); |
94222f64 | 1290 | print_tuple_struct_fields(w, cx, s); |
6a06907d XL |
1291 | w.write_str(")"); |
1292 | } | |
487cf647 | 1293 | w.write_str("</h3></section>"); |
6a06907d XL |
1294 | |
1295 | use crate::clean::Variant; | |
5099ac24 FG |
1296 | |
1297 | let heading_and_fields = match &*variant.kind { | |
1298 | clean::VariantItem(Variant::Struct(s)) => Some(("Fields", &s.fields)), | |
1299 | // Documentation on tuple variant fields is rare, so to reduce noise we only emit | |
1300 | // the section if at least one field is documented. | |
1301 | clean::VariantItem(Variant::Tuple(fields)) | |
1302 | if fields.iter().any(|f| f.doc_value().is_some()) => | |
1303 | { | |
1304 | Some(("Tuple Fields", fields)) | |
1305 | } | |
94222f64 | 1306 | _ => None, |
5099ac24 FG |
1307 | }; |
1308 | ||
1309 | if let Some((heading, fields)) = heading_and_fields { | |
1310 | let variant_id = | |
1311 | cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap())); | |
136023e0 | 1312 | write!(w, "<div class=\"sub-variant\" id=\"{id}\">", id = variant_id); |
5099ac24 | 1313 | write!(w, "<h4>{heading}</h4>", heading = heading); |
3c0e092e | 1314 | document_non_exhaustive(w, variant); |
94222f64 XL |
1315 | for field in fields { |
1316 | match *field.kind { | |
1317 | clean::StrippedItem(box clean::StructFieldItem(_)) => {} | |
1318 | clean::StructFieldItem(ref ty) => { | |
1319 | let id = cx.derive_id(format!( | |
1320 | "variant.{}.field.{}", | |
5099ac24 FG |
1321 | variant.name.unwrap(), |
1322 | field.name.unwrap() | |
94222f64 XL |
1323 | )); |
1324 | write!( | |
1325 | w, | |
3c0e092e | 1326 | "<div class=\"sub-variant-field\">\ |
487cf647 FG |
1327 | <span id=\"{id}\" class=\"small-section-header\">\ |
1328 | <a href=\"#{id}\" class=\"anchor field\">§</a>\ | |
064997fb FG |
1329 | <code>{f}: {t}</code>\ |
1330 | </span>", | |
94222f64 | 1331 | id = id, |
5099ac24 | 1332 | f = field.name.unwrap(), |
94222f64 XL |
1333 | t = ty.print(cx) |
1334 | ); | |
3c0e092e XL |
1335 | document(w, cx, field, Some(variant), HeadingOffset::H5); |
1336 | write!(w, "</div>"); | |
94222f64 XL |
1337 | } |
1338 | _ => unreachable!(), | |
6a06907d XL |
1339 | } |
1340 | } | |
3c0e092e | 1341 | w.write_str("</div>"); |
6a06907d | 1342 | } |
3c0e092e XL |
1343 | |
1344 | document(w, cx, variant, Some(it), HeadingOffset::H4); | |
6a06907d | 1345 | } |
487cf647 | 1346 | write!(w, "</div>"); |
6a06907d | 1347 | } |
04454e1e | 1348 | let def_id = it.item_id.expect_def_id(); |
17df50a5 XL |
1349 | render_assoc_items(w, cx, it, def_id, AssocItemRender::All); |
1350 | document_type_layout(w, cx, def_id); | |
6a06907d XL |
1351 | } |
1352 | ||
923072b8 | 1353 | fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) { |
2b03887a | 1354 | wrap_into_item_decl(w, |w| { |
f2b60f7d | 1355 | highlight::render_macro_with_highlighting(&t.source, w); |
6a06907d | 1356 | }); |
c295e0f8 | 1357 | document(w, cx, it, None, HeadingOffset::H2) |
6a06907d XL |
1358 | } |
1359 | ||
923072b8 | 1360 | fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &clean::ProcMacro) { |
2b03887a | 1361 | wrap_into_item_decl(w, |w| { |
5099ac24 | 1362 | let name = it.name.expect("proc-macros always have names"); |
3c0e092e XL |
1363 | match m.kind { |
1364 | MacroKind::Bang => { | |
1365 | wrap_item(w, "macro", |w| { | |
1366 | write!(w, "{}!() {{ /* proc-macro */ }}", name); | |
1367 | }); | |
1368 | } | |
1369 | MacroKind::Attr => { | |
1370 | wrap_item(w, "attr", |w| { | |
1371 | write!(w, "#[{}]", name); | |
1372 | }); | |
1373 | } | |
1374 | MacroKind::Derive => { | |
1375 | wrap_item(w, "derive", |w| { | |
1376 | write!(w, "#[derive({})]", name); | |
1377 | if !m.helpers.is_empty() { | |
1378 | w.push_str("\n{\n"); | |
1379 | w.push_str(" // Attributes available to this derive:\n"); | |
1380 | for attr in &m.helpers { | |
1381 | writeln!(w, " #[{}]", attr); | |
1382 | } | |
1383 | w.push_str("}\n"); | |
94222f64 | 1384 | } |
3c0e092e XL |
1385 | }); |
1386 | } | |
6a06907d | 1387 | } |
3c0e092e | 1388 | }); |
c295e0f8 | 1389 | document(w, cx, it, None, HeadingOffset::H2) |
6a06907d XL |
1390 | } |
1391 | ||
923072b8 | 1392 | fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { |
f2b60f7d | 1393 | let def_id = it.item_id.expect_def_id(); |
c295e0f8 | 1394 | document(w, cx, it, None, HeadingOffset::H2); |
f2b60f7d FG |
1395 | if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { |
1396 | render_assoc_items(w, cx, it, def_id, AssocItemRender::All); | |
1397 | } else { | |
1398 | // We handle the "reference" primitive type on its own because we only want to list | |
1399 | // implementations on generic types. | |
1400 | let shared = Rc::clone(&cx.shared); | |
1401 | let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it); | |
1402 | ||
1403 | render_all_impls(w, cx, it, &concrete, &synthetic, &blanket_impl); | |
1404 | } | |
6a06907d XL |
1405 | } |
1406 | ||
923072b8 | 1407 | fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) { |
2b03887a | 1408 | wrap_into_item_decl(w, |w| { |
3c0e092e | 1409 | wrap_item(w, "const", |w| { |
487cf647 | 1410 | let tcx = cx.tcx(); |
3c0e092e | 1411 | render_attributes_in_code(w, it); |
6a06907d | 1412 | |
3c0e092e XL |
1413 | write!( |
1414 | w, | |
1415 | "{vis}const {name}: {typ}", | |
487cf647 | 1416 | vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx), |
5099ac24 | 1417 | name = it.name.unwrap(), |
3c0e092e XL |
1418 | typ = c.type_.print(cx), |
1419 | ); | |
6a06907d | 1420 | |
064997fb FG |
1421 | // FIXME: The code below now prints |
1422 | // ` = _; // 100i32` | |
1423 | // if the expression is | |
1424 | // `50 + 50` | |
1425 | // which looks just wrong. | |
1426 | // Should we print | |
1427 | // ` = 100i32;` | |
1428 | // instead? | |
1429 | ||
487cf647 FG |
1430 | let value = c.value(tcx); |
1431 | let is_literal = c.is_literal(tcx); | |
1432 | let expr = c.expr(tcx); | |
3c0e092e XL |
1433 | if value.is_some() || is_literal { |
1434 | write!(w, " = {expr};", expr = Escape(&expr)); | |
1435 | } else { | |
1436 | w.write_str(";"); | |
1437 | } | |
6a06907d | 1438 | |
3c0e092e XL |
1439 | if !is_literal { |
1440 | if let Some(value) = &value { | |
1441 | let value_lowercase = value.to_lowercase(); | |
1442 | let expr_lowercase = expr.to_lowercase(); | |
6a06907d | 1443 | |
3c0e092e XL |
1444 | if value_lowercase != expr_lowercase |
1445 | && value_lowercase.trim_end_matches("i32") != expr_lowercase | |
1446 | { | |
1447 | write!(w, " // {value}", value = Escape(value)); | |
1448 | } | |
94222f64 | 1449 | } |
6a06907d | 1450 | } |
3c0e092e | 1451 | }); |
94222f64 | 1452 | }); |
6a06907d | 1453 | |
c295e0f8 | 1454 | document(w, cx, it, None, HeadingOffset::H2) |
6a06907d XL |
1455 | } |
1456 | ||
923072b8 | 1457 | fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) { |
2b03887a | 1458 | wrap_into_item_decl(w, |w| { |
94222f64 XL |
1459 | wrap_item(w, "struct", |w| { |
1460 | render_attributes_in_code(w, it); | |
487cf647 | 1461 | render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx); |
94222f64 | 1462 | }); |
6a06907d XL |
1463 | }); |
1464 | ||
c295e0f8 | 1465 | document(w, cx, it, None, HeadingOffset::H2); |
17df50a5 | 1466 | |
6a06907d XL |
1467 | let mut fields = s |
1468 | .fields | |
1469 | .iter() | |
1470 | .filter_map(|f| match *f.kind { | |
1471 | clean::StructFieldItem(ref ty) => Some((f, ty)), | |
1472 | _ => None, | |
1473 | }) | |
1474 | .peekable(); | |
487cf647 | 1475 | if let None | Some(CtorKind::Fn) = s.ctor_kind { |
6a06907d XL |
1476 | if fields.peek().is_some() { |
1477 | write!( | |
1478 | w, | |
136023e0 | 1479 | "<h2 id=\"fields\" class=\"fields small-section-header\">\ |
487cf647 | 1480 | {}{}<a href=\"#fields\" class=\"anchor\">§</a>\ |
94222f64 | 1481 | </h2>", |
487cf647 | 1482 | if s.ctor_kind.is_none() { "Fields" } else { "Tuple Fields" }, |
6a06907d XL |
1483 | document_non_exhaustive_header(it) |
1484 | ); | |
1485 | document_non_exhaustive(w, it); | |
94222f64 XL |
1486 | for (index, (field, ty)) in fields.enumerate() { |
1487 | let field_name = | |
a2a8927a | 1488 | field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string()); |
94222f64 | 1489 | let id = cx.derive_id(format!("{}.{}", ItemType::StructField, field_name)); |
6a06907d XL |
1490 | write!( |
1491 | w, | |
1492 | "<span id=\"{id}\" class=\"{item_type} small-section-header\">\ | |
487cf647 | 1493 | <a href=\"#{id}\" class=\"anchor field\">§</a>\ |
6a06907d XL |
1494 | <code>{name}: {ty}</code>\ |
1495 | </span>", | |
1496 | item_type = ItemType::StructField, | |
1497 | id = id, | |
94222f64 | 1498 | name = field_name, |
cdc7bbd5 | 1499 | ty = ty.print(cx) |
6a06907d | 1500 | ); |
c295e0f8 | 1501 | document(w, cx, field, Some(it), HeadingOffset::H3); |
6a06907d XL |
1502 | } |
1503 | } | |
1504 | } | |
04454e1e | 1505 | let def_id = it.item_id.expect_def_id(); |
17df50a5 XL |
1506 | render_assoc_items(w, cx, it, def_id, AssocItemRender::All); |
1507 | document_type_layout(w, cx, def_id); | |
6a06907d XL |
1508 | } |
1509 | ||
923072b8 | 1510 | fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) { |
2b03887a | 1511 | wrap_into_item_decl(w, |w| { |
3c0e092e XL |
1512 | wrap_item(w, "static", |w| { |
1513 | render_attributes_in_code(w, it); | |
1514 | write!( | |
1515 | w, | |
1516 | "{vis}static {mutability}{name}: {typ}", | |
487cf647 | 1517 | vis = visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx), |
3c0e092e | 1518 | mutability = s.mutability.print_with_space(), |
5099ac24 | 1519 | name = it.name.unwrap(), |
3c0e092e XL |
1520 | typ = s.type_.print(cx) |
1521 | ); | |
1522 | }); | |
94222f64 | 1523 | }); |
c295e0f8 | 1524 | document(w, cx, it, None, HeadingOffset::H2) |
6a06907d XL |
1525 | } |
1526 | ||
923072b8 | 1527 | fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { |
2b03887a | 1528 | wrap_into_item_decl(w, |w| { |
3c0e092e XL |
1529 | wrap_item(w, "foreigntype", |w| { |
1530 | w.write_str("extern {\n"); | |
1531 | render_attributes_in_code(w, it); | |
1532 | write!( | |
1533 | w, | |
1534 | " {}type {};\n}}", | |
487cf647 | 1535 | visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx), |
5099ac24 | 1536 | it.name.unwrap(), |
3c0e092e XL |
1537 | ); |
1538 | }); | |
94222f64 | 1539 | }); |
6a06907d | 1540 | |
c295e0f8 | 1541 | document(w, cx, it, None, HeadingOffset::H2); |
6a06907d | 1542 | |
04454e1e | 1543 | render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All) |
6a06907d XL |
1544 | } |
1545 | ||
923072b8 | 1546 | fn item_keyword(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { |
c295e0f8 | 1547 | document(w, cx, it, None, HeadingOffset::H2) |
6a06907d XL |
1548 | } |
1549 | ||
1550 | /// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order). | |
923072b8 | 1551 | pub(crate) fn compare_names(mut lhs: &str, mut rhs: &str) -> Ordering { |
6a06907d XL |
1552 | /// Takes a non-numeric and a numeric part from the given &str. |
1553 | fn take_parts<'a>(s: &mut &'a str) -> (&'a str, &'a str) { | |
1554 | let i = s.find(|c: char| c.is_ascii_digit()); | |
1555 | let (a, b) = s.split_at(i.unwrap_or(s.len())); | |
1556 | let i = b.find(|c: char| !c.is_ascii_digit()); | |
1557 | let (b, c) = b.split_at(i.unwrap_or(b.len())); | |
1558 | *s = c; | |
1559 | (a, b) | |
1560 | } | |
1561 | ||
1562 | while !lhs.is_empty() || !rhs.is_empty() { | |
1563 | let (la, lb) = take_parts(&mut lhs); | |
1564 | let (ra, rb) = take_parts(&mut rhs); | |
1565 | // First process the non-numeric part. | |
1566 | match la.cmp(ra) { | |
1567 | Ordering::Equal => (), | |
1568 | x => return x, | |
1569 | } | |
1570 | // Then process the numeric part, if both sides have one (and they fit in a u64). | |
1571 | if let (Ok(ln), Ok(rn)) = (lb.parse::<u64>(), rb.parse::<u64>()) { | |
1572 | match ln.cmp(&rn) { | |
1573 | Ordering::Equal => (), | |
1574 | x => return x, | |
1575 | } | |
1576 | } | |
1577 | // Then process the numeric part again, but this time as strings. | |
1578 | match lb.cmp(rb) { | |
1579 | Ordering::Equal => (), | |
1580 | x => return x, | |
1581 | } | |
1582 | } | |
1583 | ||
1584 | Ordering::Equal | |
1585 | } | |
1586 | ||
1587 | pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String { | |
5099ac24 | 1588 | let mut s = join_with_double_colon(&cx.current); |
6a06907d | 1589 | s.push_str("::"); |
a2a8927a | 1590 | s.push_str(item.name.unwrap().as_str()); |
6a06907d XL |
1591 | s |
1592 | } | |
1593 | ||
1594 | pub(super) fn item_path(ty: ItemType, name: &str) -> String { | |
1595 | match ty { | |
1596 | ItemType::Module => format!("{}index.html", ensure_trailing_slash(name)), | |
1597 | _ => format!("{}.{}.html", ty, name), | |
1598 | } | |
1599 | } | |
1600 | ||
cdc7bbd5 | 1601 | fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) -> String { |
6a06907d XL |
1602 | let mut bounds = String::new(); |
1603 | if !t_bounds.is_empty() { | |
1604 | if !trait_alias { | |
1605 | bounds.push_str(": "); | |
1606 | } | |
1607 | for (i, p) in t_bounds.iter().enumerate() { | |
1608 | if i > 0 { | |
1609 | bounds.push_str(" + "); | |
1610 | } | |
cdc7bbd5 | 1611 | bounds.push_str(&p.print(cx).to_string()); |
6a06907d XL |
1612 | } |
1613 | } | |
1614 | bounds | |
1615 | } | |
1616 | ||
2b03887a | 1617 | fn wrap_into_item_decl<F>(w: &mut Buffer, f: F) |
6a06907d XL |
1618 | where |
1619 | F: FnOnce(&mut Buffer), | |
1620 | { | |
2b03887a | 1621 | w.write_str("<div class=\"item-decl\">"); |
6a06907d XL |
1622 | f(w); |
1623 | w.write_str("</div>") | |
1624 | } | |
1625 | ||
94222f64 XL |
1626 | fn wrap_item<F>(w: &mut Buffer, item_name: &str, f: F) |
1627 | where | |
1628 | F: FnOnce(&mut Buffer), | |
1629 | { | |
1630 | w.write_fmt(format_args!("<pre class=\"rust {}\"><code>", item_name)); | |
1631 | f(w); | |
1632 | w.write_str("</code></pre>"); | |
1633 | } | |
1634 | ||
cdc7bbd5 XL |
1635 | fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cx: &Context<'_>) -> Ordering { |
1636 | let lhss = format!("{}", lhs.inner_impl().print(false, cx)); | |
1637 | let rhss = format!("{}", rhs.inner_impl().print(false, cx)); | |
6a06907d XL |
1638 | |
1639 | // lhs and rhs are formatted as HTML, which may be unnecessary | |
cdc7bbd5 | 1640 | compare_names(&lhss, &rhss) |
6a06907d XL |
1641 | } |
1642 | ||
1643 | fn render_implementor( | |
923072b8 | 1644 | cx: &mut Context<'_>, |
6a06907d XL |
1645 | implementor: &Impl, |
1646 | trait_: &clean::Item, | |
1647 | w: &mut Buffer, | |
1648 | implementor_dups: &FxHashMap<Symbol, (DefId, bool)>, | |
1649 | aliases: &[String], | |
1650 | ) { | |
136023e0 | 1651 | // If there's already another implementor that has the same abridged name, use the |
6a06907d XL |
1652 | // full path, for example in `std::iter::ExactSizeIterator` |
1653 | let use_absolute = match implementor.inner_impl().for_ { | |
3c0e092e XL |
1654 | clean::Type::Path { ref path, .. } |
1655 | | clean::BorrowedRef { type_: box clean::Type::Path { ref path, .. }, .. } | |
c295e0f8 XL |
1656 | if !path.is_assoc_ty() => |
1657 | { | |
1658 | implementor_dups[&path.last()].1 | |
1659 | } | |
6a06907d XL |
1660 | _ => false, |
1661 | }; | |
1662 | render_impl( | |
1663 | w, | |
1664 | cx, | |
1665 | implementor, | |
1666 | trait_, | |
1667 | AssocItemLink::Anchor(None), | |
1668 | RenderMode::Normal, | |
6a06907d | 1669 | Some(use_absolute), |
6a06907d | 1670 | aliases, |
136023e0 XL |
1671 | ImplRenderingParameters { |
1672 | show_def_docs: false, | |
136023e0 XL |
1673 | show_default_items: false, |
1674 | show_non_assoc_items: false, | |
1675 | toggle_open_by_default: false, | |
1676 | }, | |
6a06907d XL |
1677 | ); |
1678 | } | |
1679 | ||
1680 | fn render_union( | |
1681 | w: &mut Buffer, | |
1682 | it: &clean::Item, | |
1683 | g: Option<&clean::Generics>, | |
1684 | fields: &[clean::Item], | |
1685 | tab: &str, | |
6a06907d XL |
1686 | cx: &Context<'_>, |
1687 | ) { | |
487cf647 FG |
1688 | let tcx = cx.tcx(); |
1689 | write!( | |
1690 | w, | |
1691 | "{}union {}", | |
1692 | visibility_print_with_space(it.visibility(tcx), it.item_id, cx), | |
1693 | it.name.unwrap(), | |
1694 | ); | |
064997fb FG |
1695 | |
1696 | let where_displayed = g | |
1697 | .map(|g| { | |
1698 | write!(w, "{}", g.print(cx)); | |
1699 | print_where_clause_and_check(w, g, cx) | |
1700 | }) | |
1701 | .unwrap_or(false); | |
1702 | ||
1703 | // If there wasn't a `where` clause, we add a whitespace. | |
1704 | if !where_displayed { | |
1705 | w.write_str(" "); | |
6a06907d XL |
1706 | } |
1707 | ||
064997fb | 1708 | write!(w, "{{\n{}", tab); |
cdc7bbd5 XL |
1709 | let count_fields = |
1710 | fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count(); | |
1711 | let toggle = should_hide_fields(count_fields); | |
1712 | if toggle { | |
136023e0 | 1713 | toggle_open(w, format_args!("{} fields", count_fields)); |
cdc7bbd5 XL |
1714 | } |
1715 | ||
6a06907d XL |
1716 | for field in fields { |
1717 | if let clean::StructFieldItem(ref ty) = *field.kind { | |
1718 | write!( | |
1719 | w, | |
1720 | " {}{}: {},\n{}", | |
487cf647 | 1721 | visibility_print_with_space(field.visibility(tcx), field.item_id, cx), |
5099ac24 | 1722 | field.name.unwrap(), |
cdc7bbd5 | 1723 | ty.print(cx), |
6a06907d XL |
1724 | tab |
1725 | ); | |
1726 | } | |
1727 | } | |
1728 | ||
923072b8 | 1729 | if it.has_stripped_entries().unwrap() { |
5099ac24 | 1730 | write!(w, " /* private fields */\n{}", tab); |
6a06907d | 1731 | } |
cdc7bbd5 XL |
1732 | if toggle { |
1733 | toggle_close(w); | |
1734 | } | |
6a06907d XL |
1735 | w.write_str("}"); |
1736 | } | |
1737 | ||
1738 | fn render_struct( | |
1739 | w: &mut Buffer, | |
1740 | it: &clean::Item, | |
1741 | g: Option<&clean::Generics>, | |
487cf647 | 1742 | ty: Option<CtorKind>, |
6a06907d XL |
1743 | fields: &[clean::Item], |
1744 | tab: &str, | |
1745 | structhead: bool, | |
1746 | cx: &Context<'_>, | |
1747 | ) { | |
487cf647 | 1748 | let tcx = cx.tcx(); |
6a06907d XL |
1749 | write!( |
1750 | w, | |
1751 | "{}{}{}", | |
487cf647 | 1752 | visibility_print_with_space(it.visibility(tcx), it.item_id, cx), |
6a06907d | 1753 | if structhead { "struct " } else { "" }, |
5099ac24 | 1754 | it.name.unwrap() |
6a06907d XL |
1755 | ); |
1756 | if let Some(g) = g { | |
cdc7bbd5 | 1757 | write!(w, "{}", g.print(cx)) |
6a06907d XL |
1758 | } |
1759 | match ty { | |
487cf647 | 1760 | None => { |
064997fb FG |
1761 | let where_diplayed = g.map(|g| print_where_clause_and_check(w, g, cx)).unwrap_or(false); |
1762 | ||
1763 | // If there wasn't a `where` clause, we add a whitespace. | |
1764 | if !where_diplayed { | |
1765 | w.write_str(" {"); | |
1766 | } else { | |
1767 | w.write_str("{"); | |
6a06907d | 1768 | } |
cdc7bbd5 XL |
1769 | let count_fields = |
1770 | fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count(); | |
1771 | let has_visible_fields = count_fields > 0; | |
1772 | let toggle = should_hide_fields(count_fields); | |
1773 | if toggle { | |
136023e0 | 1774 | toggle_open(w, format_args!("{} fields", count_fields)); |
cdc7bbd5 | 1775 | } |
6a06907d XL |
1776 | for field in fields { |
1777 | if let clean::StructFieldItem(ref ty) = *field.kind { | |
1778 | write!( | |
1779 | w, | |
1780 | "\n{} {}{}: {},", | |
1781 | tab, | |
487cf647 | 1782 | visibility_print_with_space(field.visibility(tcx), field.item_id, cx), |
5099ac24 | 1783 | field.name.unwrap(), |
cdc7bbd5 | 1784 | ty.print(cx), |
6a06907d | 1785 | ); |
6a06907d XL |
1786 | } |
1787 | } | |
1788 | ||
1789 | if has_visible_fields { | |
923072b8 | 1790 | if it.has_stripped_entries().unwrap() { |
5099ac24 | 1791 | write!(w, "\n{} /* private fields */", tab); |
6a06907d XL |
1792 | } |
1793 | write!(w, "\n{}", tab); | |
923072b8 | 1794 | } else if it.has_stripped_entries().unwrap() { |
5099ac24 | 1795 | write!(w, " /* private fields */ "); |
6a06907d | 1796 | } |
cdc7bbd5 XL |
1797 | if toggle { |
1798 | toggle_close(w); | |
1799 | } | |
6a06907d XL |
1800 | w.write_str("}"); |
1801 | } | |
487cf647 | 1802 | Some(CtorKind::Fn) => { |
6a06907d XL |
1803 | w.write_str("("); |
1804 | for (i, field) in fields.iter().enumerate() { | |
1805 | if i > 0 { | |
1806 | w.write_str(", "); | |
1807 | } | |
1808 | match *field.kind { | |
1809 | clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"), | |
1810 | clean::StructFieldItem(ref ty) => { | |
1811 | write!( | |
1812 | w, | |
1813 | "{}{}", | |
487cf647 | 1814 | visibility_print_with_space(field.visibility(tcx), field.item_id, cx), |
cdc7bbd5 | 1815 | ty.print(cx), |
6a06907d XL |
1816 | ) |
1817 | } | |
1818 | _ => unreachable!(), | |
1819 | } | |
1820 | } | |
1821 | w.write_str(")"); | |
1822 | if let Some(g) = g { | |
064997fb | 1823 | write!(w, "{}", print_where_clause(g, cx, 0, Ending::NoNewline)); |
6a06907d | 1824 | } |
94222f64 XL |
1825 | // We only want a ";" when we are displaying a tuple struct, not a variant tuple struct. |
1826 | if structhead { | |
1827 | w.write_str(";"); | |
1828 | } | |
6a06907d | 1829 | } |
487cf647 | 1830 | Some(CtorKind::Const) => { |
6a06907d XL |
1831 | // Needed for PhantomData. |
1832 | if let Some(g) = g { | |
064997fb | 1833 | write!(w, "{}", print_where_clause(g, cx, 0, Ending::NoNewline)); |
6a06907d XL |
1834 | } |
1835 | w.write_str(";"); | |
1836 | } | |
1837 | } | |
1838 | } | |
1839 | ||
1840 | fn document_non_exhaustive_header(item: &clean::Item) -> &str { | |
1841 | if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" } | |
1842 | } | |
1843 | ||
1844 | fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) { | |
1845 | if item.is_non_exhaustive() { | |
17df50a5 XL |
1846 | write!( |
1847 | w, | |
1848 | "<details class=\"rustdoc-toggle non-exhaustive\">\ | |
1849 | <summary class=\"hideme\"><span>{}</span></summary>\ | |
1850 | <div class=\"docblock\">", | |
1851 | { | |
1852 | if item.is_struct() { | |
1853 | "This struct is marked as non-exhaustive" | |
1854 | } else if item.is_enum() { | |
1855 | "This enum is marked as non-exhaustive" | |
1856 | } else if item.is_variant() { | |
1857 | "This variant is marked as non-exhaustive" | |
1858 | } else { | |
1859 | "This type is marked as non-exhaustive" | |
1860 | } | |
6a06907d | 1861 | } |
17df50a5 | 1862 | ); |
6a06907d XL |
1863 | |
1864 | if item.is_struct() { | |
1865 | w.write_str( | |
1866 | "Non-exhaustive structs could have additional fields added in future. \ | |
1867 | Therefore, non-exhaustive structs cannot be constructed in external crates \ | |
17df50a5 | 1868 | using the traditional <code>Struct { .. }</code> syntax; cannot be \ |
6a06907d XL |
1869 | matched against without a wildcard <code>..</code>; and \ |
1870 | struct update syntax will not work.", | |
1871 | ); | |
1872 | } else if item.is_enum() { | |
1873 | w.write_str( | |
1874 | "Non-exhaustive enums could have additional variants added in future. \ | |
1875 | Therefore, when matching against variants of non-exhaustive enums, an \ | |
1876 | extra wildcard arm must be added to account for any future variants.", | |
1877 | ); | |
1878 | } else if item.is_variant() { | |
1879 | w.write_str( | |
1880 | "Non-exhaustive enum variants could have additional fields added in future. \ | |
1881 | Therefore, non-exhaustive enum variants cannot be constructed in external \ | |
1882 | crates and cannot be matched against.", | |
1883 | ); | |
1884 | } else { | |
1885 | w.write_str( | |
1886 | "This type will require a wildcard arm in any match statements or constructors.", | |
1887 | ); | |
1888 | } | |
1889 | ||
17df50a5 XL |
1890 | w.write_str("</div></details>"); |
1891 | } | |
1892 | } | |
1893 | ||
1894 | fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) { | |
487cf647 FG |
1895 | fn write_size_of_layout(w: &mut Buffer, layout: &LayoutS<VariantIdx>, tag_size: u64) { |
1896 | if layout.abi.is_unsized() { | |
c295e0f8 XL |
1897 | write!(w, "(unsized)"); |
1898 | } else { | |
487cf647 | 1899 | let bytes = layout.size.bytes() - tag_size; |
c295e0f8 XL |
1900 | write!(w, "{size} byte{pl}", size = bytes, pl = if bytes == 1 { "" } else { "s" },); |
1901 | } | |
1902 | } | |
1903 | ||
17df50a5 XL |
1904 | if !cx.shared.show_type_layout { |
1905 | return; | |
1906 | } | |
1907 | ||
064997fb FG |
1908 | writeln!( |
1909 | w, | |
1910 | "<h2 id=\"layout\" class=\"small-section-header\"> \ | |
487cf647 | 1911 | Layout<a href=\"#layout\" class=\"anchor\">§</a></h2>" |
064997fb | 1912 | ); |
17df50a5 XL |
1913 | writeln!(w, "<div class=\"docblock\">"); |
1914 | ||
1915 | let tcx = cx.tcx(); | |
1916 | let param_env = tcx.param_env(ty_def_id); | |
1917 | let ty = tcx.type_of(ty_def_id); | |
1918 | match tcx.layout_of(param_env.and(ty)) { | |
1919 | Ok(ty_layout) => { | |
1920 | writeln!( | |
1921 | w, | |
1922 | "<div class=\"warning\"><p><strong>Note:</strong> Most layout information is \ | |
c295e0f8 | 1923 | <strong>completely unstable</strong> and may even differ between compilations. \ |
17df50a5 XL |
1924 | The only exception is types with certain <code>repr(...)</code> attributes. \ |
1925 | Please see the Rust Reference’s \ | |
1926 | <a href=\"https://doc.rust-lang.org/reference/type-layout.html\">“Type Layout”</a> \ | |
1927 | chapter for details on type layout guarantees.</p></div>" | |
1928 | ); | |
c295e0f8 | 1929 | w.write_str("<p><strong>Size:</strong> "); |
487cf647 | 1930 | write_size_of_layout(w, &ty_layout.layout.0, 0); |
c295e0f8 XL |
1931 | writeln!(w, "</p>"); |
1932 | if let Variants::Multiple { variants, tag, tag_encoding, .. } = | |
5e7ed085 | 1933 | &ty_layout.layout.variants() |
c295e0f8 XL |
1934 | { |
1935 | if !variants.is_empty() { | |
1936 | w.write_str( | |
1937 | "<p><strong>Size for each variant:</strong></p>\ | |
1938 | <ul>", | |
1939 | ); | |
1940 | ||
5099ac24 | 1941 | let Adt(adt, _) = ty_layout.ty.kind() else { |
c295e0f8 XL |
1942 | span_bug!(tcx.def_span(ty_def_id), "not an adt") |
1943 | }; | |
1944 | ||
1945 | let tag_size = if let TagEncoding::Niche { .. } = tag_encoding { | |
1946 | 0 | |
04454e1e | 1947 | } else if let Primitive::Int(i, _) = tag.primitive() { |
c295e0f8 XL |
1948 | i.size().bytes() |
1949 | } else { | |
1950 | span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int") | |
1951 | }; | |
1952 | ||
1953 | for (index, layout) in variants.iter_enumerated() { | |
5e7ed085 | 1954 | let name = adt.variant(index).name; |
5099ac24 | 1955 | write!(w, "<li><code>{name}</code>: ", name = name); |
487cf647 | 1956 | write_size_of_layout(w, layout, tag_size); |
c295e0f8 XL |
1957 | writeln!(w, "</li>"); |
1958 | } | |
1959 | w.write_str("</ul>"); | |
1960 | } | |
17df50a5 XL |
1961 | } |
1962 | } | |
1963 | // This kind of layout error can occur with valid code, e.g. if you try to | |
1964 | // get the layout of a generic type such as `Vec<T>`. | |
1965 | Err(LayoutError::Unknown(_)) => { | |
1966 | writeln!( | |
1967 | w, | |
1968 | "<p><strong>Note:</strong> Unable to compute type layout, \ | |
1969 | possibly due to this type having generic parameters. \ | |
1970 | Layout can only be computed for concrete, fully-instantiated types.</p>" | |
1971 | ); | |
1972 | } | |
1973 | // This kind of error probably can't happen with valid code, but we don't | |
1974 | // want to panic and prevent the docs from building, so we just let the | |
1975 | // user know that we couldn't compute the layout. | |
1976 | Err(LayoutError::SizeOverflow(_)) => { | |
1977 | writeln!( | |
1978 | w, | |
1979 | "<p><strong>Note:</strong> Encountered an error during type layout; \ | |
1980 | the type was too big.</p>" | |
1981 | ); | |
1982 | } | |
a2a8927a XL |
1983 | Err(LayoutError::NormalizationFailure(_, _)) => { |
1984 | writeln!( | |
1985 | w, | |
1986 | "<p><strong>Note:</strong> Encountered an error during type layout; \ | |
1987 | the type failed to be normalized.</p>" | |
1988 | ) | |
1989 | } | |
6a06907d | 1990 | } |
17df50a5 XL |
1991 | |
1992 | writeln!(w, "</div>"); | |
6a06907d | 1993 | } |
136023e0 XL |
1994 | |
1995 | fn pluralize(count: usize) -> &'static str { | |
1996 | if count > 1 { "s" } else { "" } | |
1997 | } |