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