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