]>
Commit | Line | Data |
---|---|---|
9fa01778 | 1 | //! Rustdoc's HTML rendering module. |
1a4d82fc JJ |
2 | //! |
3 | //! This modules contains the bulk of the logic necessary for rendering a | |
4 | //! rustdoc `clean::Crate` instance to a set of static HTML pages. This | |
5 | //! rendering process is largely driven by the `format!` syntax extension to | |
6 | //! perform all I/O into files and streams. | |
7 | //! | |
8 | //! The rendering process is largely driven by the `Context` and `Cache` | |
9 | //! structures. The cache is pre-populated by crawling the crate in question, | |
bd371182 | 10 | //! and then it is shared among the various rendering threads. The cache is meant |
1a4d82fc | 11 | //! to be a fairly large structure not implementing `Clone` (because it's shared |
bd371182 AL |
12 | //! among threads). The context, however, should be a lightweight structure. This |
13 | //! is cloned per-thread and contains information about what is currently being | |
1a4d82fc JJ |
14 | //! rendered. |
15 | //! | |
16 | //! In order to speed up rendering (mostly because of markdown rendering), the | |
17 | //! rendering process has been parallelized. This parallelization is only | |
18 | //! exposed through the `crate` method on the context, and then also from the | |
19 | //! fact that the shared cache is stored in TLS (and must be accessed as such). | |
20 | //! | |
21 | //! In addition to rendering the crate itself, this module is also responsible | |
22 | //! for creating the corresponding search index and source file renderings. | |
bd371182 | 23 | //! These threads are not parallelized (they haven't been a bottleneck yet), and |
1a4d82fc | 24 | //! both occur before the crate is rendered. |
0531ce1d | 25 | |
923072b8 | 26 | pub(crate) mod search_index; |
3dfed10e XL |
27 | |
28 | #[cfg(test)] | |
29 | mod tests; | |
30 | ||
6a06907d XL |
31 | mod context; |
32 | mod print_item; | |
94222f64 | 33 | mod span_map; |
6a06907d XL |
34 | mod write_shared; |
35 | ||
923072b8 FG |
36 | pub(crate) use self::context::*; |
37 | pub(crate) use self::span_map::{collect_spans_and_sources, LinkFromSrc}; | |
6a06907d | 38 | |
6a06907d | 39 | use std::collections::VecDeque; |
1a4d82fc | 40 | use std::default::Default; |
6a06907d | 41 | use std::fmt; |
3c0e092e XL |
42 | use std::fs; |
43 | use std::iter::Peekable; | |
cdc7bbd5 | 44 | use std::path::PathBuf; |
923072b8 | 45 | use std::rc::Rc; |
1a4d82fc | 46 | use std::str; |
f9f354fc | 47 | use std::string::ToString; |
1a4d82fc | 48 | |
74b04a01 | 49 | use rustc_ast_pretty::pprust; |
136023e0 | 50 | use rustc_attr::{ConstStability, Deprecation, StabilityLevel}; |
3c0e092e | 51 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
5869c6ff | 52 | use rustc_hir::def::CtorKind; |
6a06907d | 53 | use rustc_hir::def_id::DefId; |
dfeec247 | 54 | use rustc_hir::Mutability; |
ba9703b0 | 55 | use rustc_middle::middle::stability; |
3c0e092e | 56 | use rustc_middle::ty; |
c295e0f8 | 57 | use rustc_middle::ty::TyCtxt; |
3c0e092e | 58 | use rustc_span::{ |
923072b8 | 59 | symbol::{sym, Symbol}, |
3c0e092e XL |
60 | BytePos, FileName, RealFileName, |
61 | }; | |
dfeec247 XL |
62 | use serde::ser::SerializeSeq; |
63 | use serde::{Serialize, Serializer}; | |
1a4d82fc | 64 | |
3c0e092e | 65 | use crate::clean::{self, ItemId, RenderedLink, SelfTy}; |
3dfed10e | 66 | use crate::error::Error; |
5869c6ff | 67 | use crate::formats::cache::Cache; |
3dfed10e | 68 | use crate::formats::item_type::ItemType; |
cdc7bbd5 | 69 | use crate::formats::{AssocItemRender, Impl, RenderMode}; |
9fa01778 | 70 | use crate::html::escape::Escape; |
6a06907d | 71 | use crate::html::format::{ |
5099ac24 | 72 | href, join_with_double_colon, print_abi_with_space, print_constness_with_space, |
064997fb | 73 | print_default_space, print_generic_bounds, print_where_clause, Buffer, Ending, HrefError, |
5099ac24 | 74 | PrintWithSpace, |
fc512014 | 75 | }; |
3c0e092e | 76 | use crate::html::highlight; |
5e7ed085 | 77 | use crate::html::markdown::{HeadingOffset, IdMap, Markdown, MarkdownHtml, MarkdownSummaryLine}; |
3c0e092e | 78 | use crate::html::sources; |
04454e1e | 79 | use crate::html::static_files::SCRAPE_EXAMPLES_HELP_MD; |
3c0e092e XL |
80 | use crate::scrape_examples::{CallData, CallLocation}; |
81 | use crate::try_none; | |
04454e1e | 82 | use crate::DOC_RUST_LANG_ORG_CHANNEL; |
e74abb32 | 83 | |
85aaf69f | 84 | /// A pair of name and its optional document. |
923072b8 | 85 | pub(crate) type NameDoc = (String, Option<String>); |
85aaf69f | 86 | |
923072b8 | 87 | pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { |
e74abb32 | 88 | crate::html::format::display_fn(move |f| { |
5869c6ff | 89 | if !v.ends_with('/') && !v.is_empty() { write!(f, "{}/", v) } else { f.write_str(v) } |
e74abb32 | 90 | }) |
9fa01778 XL |
91 | } |
92 | ||
1a4d82fc JJ |
93 | // Helper structs for rendering items/sidebars and carrying along contextual |
94 | // information | |
95 | ||
1a4d82fc JJ |
96 | /// Struct representing one entry in the JS search index. These are all emitted |
97 | /// by hand to a large JS file at the end of cache-creation. | |
83c7162d | 98 | #[derive(Debug)] |
923072b8 FG |
99 | pub(crate) struct IndexItem { |
100 | pub(crate) ty: ItemType, | |
101 | pub(crate) name: String, | |
102 | pub(crate) path: String, | |
103 | pub(crate) desc: String, | |
104 | pub(crate) parent: Option<DefId>, | |
105 | pub(crate) parent_idx: Option<usize>, | |
106 | pub(crate) search_type: Option<IndexItemFunctionType>, | |
107 | pub(crate) aliases: Box<[Symbol]>, | |
c34b1796 AL |
108 | } |
109 | ||
110 | /// A type used for the search index. | |
83c7162d | 111 | #[derive(Debug)] |
923072b8 | 112 | pub(crate) struct RenderType { |
064997fb FG |
113 | id: Option<RenderTypeId>, |
114 | generics: Option<Vec<RenderType>>, | |
ba9703b0 XL |
115 | } |
116 | ||
064997fb | 117 | impl Serialize for RenderType { |
60c5eb7d XL |
118 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
119 | where | |
120 | S: Serializer, | |
121 | { | |
064997fb FG |
122 | let id = match &self.id { |
123 | // 0 is a sentinel, everything else is one-indexed | |
124 | None => 0, | |
125 | Some(RenderTypeId::Index(idx)) => idx + 1, | |
126 | _ => panic!("must convert render types to indexes before serializing"), | |
127 | }; | |
128 | if let Some(generics) = &self.generics { | |
60c5eb7d | 129 | let mut seq = serializer.serialize_seq(None)?; |
064997fb FG |
130 | seq.serialize_element(&id)?; |
131 | seq.serialize_element(generics)?; | |
60c5eb7d | 132 | seq.end() |
064997fb FG |
133 | } else { |
134 | id.serialize(serializer) | |
c34b1796 | 135 | } |
c34b1796 | 136 | } |
1a4d82fc JJ |
137 | } |
138 | ||
064997fb FG |
139 | #[derive(Clone, Debug)] |
140 | pub(crate) enum RenderTypeId { | |
141 | DefId(DefId), | |
142 | Primitive(clean::PrimitiveType), | |
143 | Index(usize), | |
ba9703b0 XL |
144 | } |
145 | ||
064997fb FG |
146 | /// Full type of functions/methods in the search index. |
147 | #[derive(Debug)] | |
148 | pub(crate) struct IndexItemFunctionType { | |
149 | inputs: Vec<RenderType>, | |
150 | output: Vec<RenderType>, | |
ba9703b0 XL |
151 | } |
152 | ||
064997fb | 153 | impl Serialize for IndexItemFunctionType { |
ba9703b0 XL |
154 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
155 | where | |
156 | S: Serializer, | |
157 | { | |
064997fb FG |
158 | // If we couldn't figure out a type, just write `0`. |
159 | let has_missing = self | |
160 | .inputs | |
161 | .iter() | |
162 | .chain(self.output.iter()) | |
163 | .any(|i| i.id.is_none() && i.generics.is_none()); | |
164 | if has_missing { | |
165 | 0.serialize(serializer) | |
166 | } else { | |
167 | let mut seq = serializer.serialize_seq(None)?; | |
168 | match &self.inputs[..] { | |
169 | [one] if one.generics.is_none() => seq.serialize_element(one)?, | |
170 | _ => seq.serialize_element(&self.inputs)?, | |
171 | } | |
172 | match &self.output[..] { | |
173 | [] => {} | |
174 | [one] if one.generics.is_none() => seq.serialize_element(one)?, | |
175 | _ => seq.serialize_element(&self.output)?, | |
176 | } | |
177 | seq.end() | |
136023e0 | 178 | } |
ba9703b0 XL |
179 | } |
180 | } | |
181 | ||
3dfed10e | 182 | #[derive(Debug, Clone)] |
923072b8 | 183 | pub(crate) struct StylePath { |
3dfed10e | 184 | /// The path to the theme |
923072b8 | 185 | pub(crate) path: PathBuf, |
a2a8927a XL |
186 | } |
187 | ||
188 | impl StylePath { | |
923072b8 | 189 | pub(crate) fn basename(&self) -> Result<String, Error> { |
a2a8927a XL |
190 | Ok(try_none!(try_none!(self.path.file_stem(), &self.path).to_str(), &self.path).to_string()) |
191 | } | |
3dfed10e XL |
192 | } |
193 | ||
83c7162d XL |
194 | #[derive(Debug, Eq, PartialEq, Hash)] |
195 | struct ItemEntry { | |
196 | url: String, | |
197 | name: String, | |
198 | } | |
199 | ||
200 | impl ItemEntry { | |
201 | fn new(mut url: String, name: String) -> ItemEntry { | |
202 | while url.starts_with('/') { | |
203 | url.remove(0); | |
204 | } | |
dfeec247 | 205 | ItemEntry { url, name } |
83c7162d XL |
206 | } |
207 | } | |
208 | ||
e74abb32 | 209 | impl ItemEntry { |
923072b8 | 210 | pub(crate) fn print(&self) -> impl fmt::Display + '_ { |
e74abb32 | 211 | crate::html::format::display_fn(move |f| { |
29967ef6 | 212 | write!(f, "<a href=\"{}\">{}</a>", self.url, Escape(&self.name)) |
e74abb32 | 213 | }) |
83c7162d XL |
214 | } |
215 | } | |
216 | ||
217 | impl PartialOrd for ItemEntry { | |
218 | fn partial_cmp(&self, other: &ItemEntry) -> Option<::std::cmp::Ordering> { | |
219 | Some(self.cmp(other)) | |
220 | } | |
221 | } | |
222 | ||
223 | impl Ord for ItemEntry { | |
224 | fn cmp(&self, other: &ItemEntry) -> ::std::cmp::Ordering { | |
225 | self.name.cmp(&other.name) | |
226 | } | |
227 | } | |
228 | ||
229 | #[derive(Debug)] | |
230 | struct AllTypes { | |
b7449926 XL |
231 | structs: FxHashSet<ItemEntry>, |
232 | enums: FxHashSet<ItemEntry>, | |
233 | unions: FxHashSet<ItemEntry>, | |
234 | primitives: FxHashSet<ItemEntry>, | |
235 | traits: FxHashSet<ItemEntry>, | |
236 | macros: FxHashSet<ItemEntry>, | |
237 | functions: FxHashSet<ItemEntry>, | |
238 | typedefs: FxHashSet<ItemEntry>, | |
416331ca | 239 | opaque_tys: FxHashSet<ItemEntry>, |
b7449926 XL |
240 | statics: FxHashSet<ItemEntry>, |
241 | constants: FxHashSet<ItemEntry>, | |
0bf4aa26 XL |
242 | attributes: FxHashSet<ItemEntry>, |
243 | derives: FxHashSet<ItemEntry>, | |
9fa01778 | 244 | trait_aliases: FxHashSet<ItemEntry>, |
83c7162d XL |
245 | } |
246 | ||
247 | impl AllTypes { | |
248 | fn new() -> AllTypes { | |
b7449926 | 249 | let new_set = |cap| FxHashSet::with_capacity_and_hasher(cap, Default::default()); |
83c7162d | 250 | AllTypes { |
b7449926 XL |
251 | structs: new_set(100), |
252 | enums: new_set(100), | |
253 | unions: new_set(100), | |
254 | primitives: new_set(26), | |
255 | traits: new_set(100), | |
256 | macros: new_set(100), | |
257 | functions: new_set(100), | |
258 | typedefs: new_set(100), | |
416331ca | 259 | opaque_tys: new_set(100), |
b7449926 XL |
260 | statics: new_set(100), |
261 | constants: new_set(100), | |
0bf4aa26 XL |
262 | attributes: new_set(100), |
263 | derives: new_set(100), | |
9fa01778 | 264 | trait_aliases: new_set(100), |
83c7162d XL |
265 | } |
266 | } | |
267 | ||
268 | fn append(&mut self, item_name: String, item_type: &ItemType) { | |
269 | let mut url: Vec<_> = item_name.split("::").skip(1).collect(); | |
270 | if let Some(name) = url.pop() { | |
271 | let new_url = format!("{}/{}.{}.html", url.join("/"), item_type, name); | |
272 | url.push(name); | |
273 | let name = url.join("::"); | |
274 | match *item_type { | |
275 | ItemType::Struct => self.structs.insert(ItemEntry::new(new_url, name)), | |
276 | ItemType::Enum => self.enums.insert(ItemEntry::new(new_url, name)), | |
277 | ItemType::Union => self.unions.insert(ItemEntry::new(new_url, name)), | |
278 | ItemType::Primitive => self.primitives.insert(ItemEntry::new(new_url, name)), | |
279 | ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)), | |
280 | ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)), | |
281 | ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)), | |
282 | ItemType::Typedef => self.typedefs.insert(ItemEntry::new(new_url, name)), | |
416331ca | 283 | ItemType::OpaqueTy => self.opaque_tys.insert(ItemEntry::new(new_url, name)), |
83c7162d XL |
284 | ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)), |
285 | ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)), | |
0bf4aa26 XL |
286 | ItemType::ProcAttribute => self.attributes.insert(ItemEntry::new(new_url, name)), |
287 | ItemType::ProcDerive => self.derives.insert(ItemEntry::new(new_url, name)), | |
9fa01778 | 288 | ItemType::TraitAlias => self.trait_aliases.insert(ItemEntry::new(new_url, name)), |
83c7162d XL |
289 | _ => true, |
290 | }; | |
291 | } | |
292 | } | |
293 | } | |
294 | ||
e1599b0c XL |
295 | impl AllTypes { |
296 | fn print(self, f: &mut Buffer) { | |
5869c6ff XL |
297 | fn print_entries(f: &mut Buffer, e: &FxHashSet<ItemEntry>, title: &str, class: &str) { |
298 | if !e.is_empty() { | |
299 | let mut e: Vec<&ItemEntry> = e.iter().collect(); | |
300 | e.sort(); | |
17df50a5 XL |
301 | write!( |
302 | f, | |
303 | "<h3 id=\"{}\">{}</h3><ul class=\"{} docblock\">", | |
304 | title.replace(' ', "-"), // IDs cannot contain whitespaces. | |
305 | title, | |
306 | class | |
307 | ); | |
5869c6ff XL |
308 | |
309 | for s in e.iter() { | |
310 | write!(f, "<li>{}</li>", s.print()); | |
311 | } | |
312 | ||
313 | f.write_str("</ul>"); | |
314 | } | |
315 | } | |
316 | ||
317 | f.write_str( | |
29967ef6 | 318 | "<h1 class=\"fqn\">\ |
5869c6ff | 319 | <span class=\"in-band\">List of all items</span>\ |
5869c6ff | 320 | </h1>", |
dfeec247 | 321 | ); |
5869c6ff | 322 | // Note: print_entries does not escape the title, because we know the current set of titles |
17df50a5 | 323 | // doesn't require escaping. |
e1599b0c XL |
324 | print_entries(f, &self.structs, "Structs", "structs"); |
325 | print_entries(f, &self.enums, "Enums", "enums"); | |
326 | print_entries(f, &self.unions, "Unions", "unions"); | |
327 | print_entries(f, &self.primitives, "Primitives", "primitives"); | |
328 | print_entries(f, &self.traits, "Traits", "traits"); | |
329 | print_entries(f, &self.macros, "Macros", "macros"); | |
330 | print_entries(f, &self.attributes, "Attribute Macros", "attributes"); | |
331 | print_entries(f, &self.derives, "Derive Macros", "derives"); | |
332 | print_entries(f, &self.functions, "Functions", "functions"); | |
333 | print_entries(f, &self.typedefs, "Typedefs", "typedefs"); | |
334 | print_entries(f, &self.trait_aliases, "Trait Aliases", "trait-aliases"); | |
335 | print_entries(f, &self.opaque_tys, "Opaque Types", "opaque-types"); | |
336 | print_entries(f, &self.statics, "Statics", "statics"); | |
83c7162d XL |
337 | print_entries(f, &self.constants, "Constants", "constants") |
338 | } | |
339 | } | |
340 | ||
04454e1e FG |
341 | fn scrape_examples_help(shared: &SharedContext<'_>) -> String { |
342 | let mut content = SCRAPE_EXAMPLES_HELP_MD.to_owned(); | |
343 | content.push_str(&format!( | |
344 | "## More information\n\n\ | |
345 | If you want more information about this feature, please read the [corresponding chapter in the Rustdoc book]({}/rustdoc/scraped-examples.html).", | |
346 | DOC_RUST_LANG_ORG_CHANNEL)); | |
60c5eb7d | 347 | |
04454e1e FG |
348 | let mut ids = IdMap::default(); |
349 | format!( | |
350 | "<div class=\"main-heading\">\ | |
5099ac24 | 351 | <h1 class=\"fqn\">\ |
04454e1e | 352 | <span class=\"in-band\">About scraped examples</span>\ |
5099ac24 | 353 | </h1>\ |
5099ac24 | 354 | </div>\ |
04454e1e FG |
355 | <div>{}</div>", |
356 | Markdown { | |
357 | content: &content, | |
358 | links: &[], | |
359 | ids: &mut ids, | |
360 | error_codes: shared.codes, | |
361 | edition: shared.edition(), | |
362 | playground: &shared.playground, | |
363 | heading_offset: HeadingOffset::H1 | |
364 | } | |
365 | .into_string() | |
366 | ) | |
1a4d82fc JJ |
367 | } |
368 | ||
c295e0f8 XL |
369 | fn document( |
370 | w: &mut Buffer, | |
923072b8 | 371 | cx: &mut Context<'_>, |
c295e0f8 XL |
372 | item: &clean::Item, |
373 | parent: Option<&clean::Item>, | |
374 | heading_offset: HeadingOffset, | |
375 | ) { | |
6a06907d XL |
376 | if let Some(ref name) = item.name { |
377 | info!("Documenting {}", name); | |
c30ab7b3 | 378 | } |
17df50a5 XL |
379 | document_item_info(w, cx, item, parent); |
380 | if parent.is_none() { | |
c295e0f8 | 381 | document_full_collapsible(w, item, cx, heading_offset); |
17df50a5 | 382 | } else { |
c295e0f8 | 383 | document_full(w, item, cx, heading_offset); |
17df50a5 | 384 | } |
6a06907d | 385 | } |
c30ab7b3 | 386 | |
6a06907d | 387 | /// Render md_text as markdown. |
c295e0f8 XL |
388 | fn render_markdown( |
389 | w: &mut Buffer, | |
923072b8 | 390 | cx: &mut Context<'_>, |
c295e0f8 XL |
391 | md_text: &str, |
392 | links: Vec<RenderedLink>, | |
393 | heading_offset: HeadingOffset, | |
394 | ) { | |
6a06907d XL |
395 | write!( |
396 | w, | |
17df50a5 | 397 | "<div class=\"docblock\">{}</div>", |
c295e0f8 XL |
398 | Markdown { |
399 | content: md_text, | |
400 | links: &links, | |
923072b8 | 401 | ids: &mut cx.id_map, |
c295e0f8 XL |
402 | error_codes: cx.shared.codes, |
403 | edition: cx.shared.edition(), | |
404 | playground: &cx.shared.playground, | |
405 | heading_offset, | |
406 | } | |
6a06907d XL |
407 | .into_string() |
408 | ) | |
409 | } | |
1a4d82fc | 410 | |
6a06907d XL |
411 | /// Writes a documentation block containing only the first paragraph of the documentation. If the |
412 | /// docs are longer, a "Read more" link is appended to the end. | |
413 | fn document_short( | |
414 | w: &mut Buffer, | |
415 | item: &clean::Item, | |
923072b8 | 416 | cx: &mut Context<'_>, |
6a06907d | 417 | link: AssocItemLink<'_>, |
cdc7bbd5 | 418 | parent: &clean::Item, |
6a06907d XL |
419 | show_def_docs: bool, |
420 | ) { | |
17df50a5 | 421 | document_item_info(w, cx, item, Some(parent)); |
6a06907d XL |
422 | if !show_def_docs { |
423 | return; | |
424 | } | |
425 | if let Some(s) = item.doc_value() { | |
cdc7bbd5 | 426 | let mut summary_html = MarkdownSummaryLine(&s, &item.links(cx)).into_string(); |
1a4d82fc | 427 | |
6a06907d | 428 | if s.contains('\n') { |
04454e1e | 429 | let link = format!(r#" <a{}>Read more</a>"#, assoc_href_attr(item, link, cx)); |
1a4d82fc | 430 | |
6a06907d XL |
431 | if let Some(idx) = summary_html.rfind("</p>") { |
432 | summary_html.insert_str(idx, &link); | |
e1599b0c | 433 | } else { |
6a06907d | 434 | summary_html.push_str(&link); |
1b1a35ee XL |
435 | } |
436 | } | |
437 | ||
17df50a5 | 438 | write!(w, "<div class='docblock'>{}</div>", summary_html,); |
a7813a04 | 439 | } |
a7813a04 XL |
440 | } |
441 | ||
c295e0f8 XL |
442 | fn document_full_collapsible( |
443 | w: &mut Buffer, | |
444 | item: &clean::Item, | |
923072b8 | 445 | cx: &mut Context<'_>, |
c295e0f8 XL |
446 | heading_offset: HeadingOffset, |
447 | ) { | |
448 | document_full_inner(w, item, cx, true, heading_offset); | |
17df50a5 XL |
449 | } |
450 | ||
c295e0f8 XL |
451 | fn document_full( |
452 | w: &mut Buffer, | |
453 | item: &clean::Item, | |
923072b8 | 454 | cx: &mut Context<'_>, |
c295e0f8 XL |
455 | heading_offset: HeadingOffset, |
456 | ) { | |
457 | document_full_inner(w, item, cx, false, heading_offset); | |
17df50a5 XL |
458 | } |
459 | ||
c295e0f8 XL |
460 | fn document_full_inner( |
461 | w: &mut Buffer, | |
462 | item: &clean::Item, | |
923072b8 | 463 | cx: &mut Context<'_>, |
c295e0f8 XL |
464 | is_collapsible: bool, |
465 | heading_offset: HeadingOffset, | |
466 | ) { | |
a2a8927a | 467 | if let Some(s) = item.collapsed_doc_value() { |
ff7c6d11 | 468 | debug!("Doc block: =====\n{}\n=====", s); |
17df50a5 XL |
469 | if is_collapsible { |
470 | w.write_str( | |
471 | "<details class=\"rustdoc-toggle top-doc\" open>\ | |
472 | <summary class=\"hideme\">\ | |
473 | <span>Expand description</span>\ | |
474 | </summary>", | |
475 | ); | |
c295e0f8 | 476 | render_markdown(w, cx, &s, item.links(cx), heading_offset); |
17df50a5 XL |
477 | w.write_str("</details>"); |
478 | } else { | |
c295e0f8 | 479 | render_markdown(w, cx, &s, item.links(cx), heading_offset); |
17df50a5 | 480 | } |
3157f602 | 481 | } |
3c0e092e XL |
482 | |
483 | let kind = match &*item.kind { | |
484 | clean::ItemKind::StrippedItem(box kind) | kind => kind, | |
485 | }; | |
486 | ||
487 | if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind { | |
488 | render_call_locations(w, cx, item); | |
489 | } | |
3157f602 XL |
490 | } |
491 | ||
fc512014 XL |
492 | /// Add extra information about an item such as: |
493 | /// | |
494 | /// * Stability | |
495 | /// * Deprecated | |
496 | /// * Required features (through the `doc_cfg` feature) | |
497 | fn document_item_info( | |
29967ef6 | 498 | w: &mut Buffer, |
923072b8 | 499 | cx: &mut Context<'_>, |
29967ef6 | 500 | item: &clean::Item, |
29967ef6 XL |
501 | parent: Option<&clean::Item>, |
502 | ) { | |
fc512014 XL |
503 | let item_infos = short_item_info(item, cx, parent); |
504 | if !item_infos.is_empty() { | |
04454e1e | 505 | w.write_str("<span class=\"item-info\">"); |
fc512014 | 506 | for info in item_infos { |
5869c6ff | 507 | w.write_str(&info); |
32a655c1 | 508 | } |
04454e1e | 509 | w.write_str("</span>"); |
3157f602 | 510 | } |
3157f602 XL |
511 | } |
512 | ||
fc512014 | 513 | fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> { |
cdc7bbd5 | 514 | let cfg = match (&item.cfg, parent.and_then(|p| p.cfg.as_ref())) { |
fc512014 XL |
515 | (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg), |
516 | (cfg, _) => cfg.as_deref().cloned(), | |
517 | }; | |
518 | ||
f2b60f7d FG |
519 | debug!( |
520 | "Portability {:?} {:?} (parent: {:?}) - {:?} = {:?}", | |
521 | item.name, | |
522 | item.cfg, | |
523 | parent, | |
524 | parent.and_then(|p| p.cfg.as_ref()), | |
525 | cfg | |
526 | ); | |
fc512014 XL |
527 | |
528 | Some(format!("<div class=\"stab portability\">{}</div>", cfg?.render_long_html())) | |
529 | } | |
530 | ||
531 | /// Render the stability, deprecation and portability information that is displayed at the top of | |
532 | /// the item's documentation. | |
533 | fn short_item_info( | |
534 | item: &clean::Item, | |
923072b8 | 535 | cx: &mut Context<'_>, |
fc512014 XL |
536 | parent: Option<&clean::Item>, |
537 | ) -> Vec<String> { | |
538 | let mut extra_info = vec![]; | |
e74abb32 | 539 | let error_codes = cx.shared.codes; |
a7813a04 | 540 | |
c295e0f8 | 541 | if let Some(depr @ Deprecation { note, since, is_since_rustc_version: _, suggestion: _ }) = |
fc512014 XL |
542 | item.deprecation(cx.tcx()) |
543 | { | |
04454e1e FG |
544 | // We display deprecation messages for #[deprecated], but only display |
545 | // the future-deprecation messages for rustc versions. | |
0731742a | 546 | let mut message = if let Some(since) = since { |
a2a8927a | 547 | let since = since.as_str(); |
c295e0f8 | 548 | if !stability::deprecation_in_effect(&depr) { |
a2a8927a | 549 | if since == "TBD" { |
5869c6ff | 550 | String::from("Deprecating in a future Rust version") |
fc512014 XL |
551 | } else { |
552 | format!("Deprecating in {}", Escape(since)) | |
553 | } | |
3dfed10e | 554 | } else { |
fc512014 | 555 | format!("Deprecated since {}", Escape(since)) |
3dfed10e | 556 | } |
d9579d0f | 557 | } else { |
0731742a | 558 | String::from("Deprecated") |
d9579d0f | 559 | }; |
0731742a XL |
560 | |
561 | if let Some(note) = note { | |
fc512014 | 562 | let note = note.as_str(); |
e74abb32 | 563 | let html = MarkdownHtml( |
a2a8927a | 564 | note, |
923072b8 | 565 | &mut cx.id_map, |
dfeec247 | 566 | error_codes, |
cdc7bbd5 | 567 | cx.shared.edition(), |
dfeec247 XL |
568 | &cx.shared.playground, |
569 | ); | |
3dfed10e | 570 | message.push_str(&format!(": {}", html.into_string())); |
0731742a | 571 | } |
fc512014 | 572 | extra_info.push(format!( |
f2b60f7d FG |
573 | "<div class=\"stab deprecated\">\ |
574 | <span class=\"emoji\">👎</span>\ | |
575 | <span>{}</span>\ | |
576 | </div>", | |
f9f354fc XL |
577 | message, |
578 | )); | |
0731742a XL |
579 | } |
580 | ||
3dfed10e XL |
581 | // Render unstable items. But don't render "rustc_private" crates (internal compiler crates). |
582 | // Those crates are permanently unstable so it makes no sense to render "unstable" everywhere. | |
3c0e092e | 583 | if let Some((StabilityLevel::Unstable { reason: _, issue, .. }, feature)) = item |
fc512014 | 584 | .stability(cx.tcx()) |
3dfed10e | 585 | .as_ref() |
29967ef6 XL |
586 | .filter(|stab| stab.feature != sym::rustc_private) |
587 | .map(|stab| (stab.level, stab.feature)) | |
3dfed10e | 588 | { |
f2b60f7d FG |
589 | let mut message = "<span class=\"emoji\">🔬</span>\ |
590 | <span>This is a nightly-only experimental API." | |
591 | .to_owned(); | |
3dfed10e | 592 | |
a2a8927a | 593 | let mut feature = format!("<code>{}</code>", Escape(feature.as_str())); |
29967ef6 | 594 | if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) { |
3dfed10e | 595 | feature.push_str(&format!( |
6a06907d XL |
596 | " <a href=\"{url}{issue}\">#{issue}</a>", |
597 | url = url, | |
598 | issue = issue | |
599 | )); | |
1a4d82fc | 600 | } |
0531ce1d | 601 | |
f2b60f7d | 602 | message.push_str(&format!(" ({})</span>", feature)); |
6a06907d | 603 | |
6a06907d | 604 | extra_info.push(format!("<div class=\"stab unstable\">{}</div>", message)); |
1a4d82fc | 605 | } |
dfeec247 | 606 | |
6a06907d XL |
607 | if let Some(portability) = portability(item, parent) { |
608 | extra_info.push(portability); | |
609 | } | |
610 | ||
611 | extra_info | |
612 | } | |
613 | ||
136023e0 XL |
614 | // Render the list of items inside one of the sections "Trait Implementations", |
615 | // "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages). | |
f2b60f7d | 616 | pub(crate) fn render_impls( |
923072b8 | 617 | cx: &mut Context<'_>, |
5e7ed085 | 618 | w: &mut Buffer, |
f2b60f7d | 619 | impls: &[&Impl], |
5e7ed085 FG |
620 | containing_item: &clean::Item, |
621 | toggle_open_by_default: bool, | |
622 | ) { | |
cdc7bbd5 | 623 | let tcx = cx.tcx(); |
c295e0f8 | 624 | let mut rendered_impls = impls |
6a06907d XL |
625 | .iter() |
626 | .map(|i| { | |
c295e0f8 | 627 | let did = i.trait_did().unwrap(); |
17df50a5 XL |
628 | let provided_trait_methods = i.inner_impl().provided_trait_methods(tcx); |
629 | let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods); | |
6a06907d XL |
630 | let mut buffer = if w.is_for_html() { Buffer::html() } else { Buffer::new() }; |
631 | render_impl( | |
632 | &mut buffer, | |
633 | cx, | |
634 | i, | |
635 | containing_item, | |
636 | assoc_link, | |
637 | RenderMode::Normal, | |
6a06907d | 638 | None, |
6a06907d | 639 | &[], |
136023e0 XL |
640 | ImplRenderingParameters { |
641 | show_def_docs: true, | |
136023e0 XL |
642 | show_default_items: true, |
643 | show_non_assoc_items: true, | |
5e7ed085 | 644 | toggle_open_by_default, |
136023e0 | 645 | }, |
6a06907d XL |
646 | ); |
647 | buffer.into_inner() | |
648 | }) | |
649 | .collect::<Vec<_>>(); | |
c295e0f8 XL |
650 | rendered_impls.sort(); |
651 | w.write_str(&rendered_impls.join("")); | |
1a4d82fc JJ |
652 | } |
653 | ||
04454e1e FG |
654 | /// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item. |
655 | fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) -> String { | |
656 | let name = it.name.unwrap(); | |
657 | let item_type = it.type_(); | |
54a0048b | 658 | |
04454e1e FG |
659 | let href = match link { |
660 | AssocItemLink::Anchor(Some(ref id)) => Some(format!("#{}", id)), | |
661 | AssocItemLink::Anchor(None) => Some(format!("#{}.{}", item_type, name)), | |
662 | AssocItemLink::GotoSource(did, provided_methods) => { | |
663 | // We're creating a link from the implementation of an associated item to its | |
664 | // declaration in the trait declaration. | |
665 | let item_type = match item_type { | |
666 | // For historical but not technical reasons, the item type of methods in | |
667 | // trait declarations depends on whether the method is required (`TyMethod`) or | |
668 | // provided (`Method`). | |
669 | ItemType::Method | ItemType::TyMethod => { | |
670 | if provided_methods.contains(&name) { | |
671 | ItemType::Method | |
672 | } else { | |
673 | ItemType::TyMethod | |
674 | } | |
675 | } | |
676 | // For associated types and constants, no such distinction exists. | |
677 | item_type => item_type, | |
678 | }; | |
54a0048b | 679 | |
04454e1e FG |
680 | match href(did.expect_def_id(), cx) { |
681 | Ok((url, ..)) => Some(format!("{}#{}.{}", url, item_type, name)), | |
682 | // The link is broken since it points to an external crate that wasn't documented. | |
683 | // Do not create any link in such case. This is better than falling back to a | |
684 | // dummy anchor like `#{item_type}.{name}` representing the `id` of *this* impl item | |
685 | // (that used to happen in older versions). Indeed, in most cases this dummy would | |
686 | // coincide with the `id`. However, it would not always do so. | |
687 | // In general, this dummy would be incorrect: | |
688 | // If the type with the trait impl also had an inherent impl with an assoc. item of | |
689 | // the *same* name as this impl item, the dummy would link to that one even though | |
690 | // those two items are distinct! | |
691 | // In this scenario, the actual `id` of this impl item would be | |
692 | // `#{item_type}.{name}-{n}` for some number `n` (a disambiguator). | |
693 | Err(HrefError::DocumentationNotBuilt) => None, | |
694 | Err(_) => Some(format!("#{}.{}", item_type, name)), | |
695 | } | |
54a0048b | 696 | } |
04454e1e FG |
697 | }; |
698 | ||
699 | // If there is no `href` for the reason explained above, simply do not render it which is valid: | |
700 | // https://html.spec.whatwg.org/multipage/links.html#links-created-by-a-and-area-elements | |
701 | href.map(|href| format!(" href=\"{}\"", href)).unwrap_or_default() | |
54a0048b SL |
702 | } |
703 | ||
dfeec247 XL |
704 | fn assoc_const( |
705 | w: &mut Buffer, | |
706 | it: &clean::Item, | |
707 | ty: &clean::Type, | |
04454e1e | 708 | default: Option<&clean::ConstantKind>, |
dfeec247 XL |
709 | link: AssocItemLink<'_>, |
710 | extra: &str, | |
fc512014 | 711 | cx: &Context<'_>, |
dfeec247 XL |
712 | ) { |
713 | write!( | |
714 | w, | |
04454e1e FG |
715 | "{extra}{vis}const <a{href} class=\"constant\">{name}</a>: {ty}", |
716 | extra = extra, | |
717 | vis = it.visibility.print_with_space(it.item_id, cx), | |
718 | href = assoc_href_attr(it, link, cx), | |
719 | name = it.name.as_ref().unwrap(), | |
720 | ty = ty.print(cx), | |
dfeec247 | 721 | ); |
04454e1e | 722 | if let Some(default) = default { |
064997fb FG |
723 | write!(w, " = "); |
724 | ||
04454e1e FG |
725 | // FIXME: `.value()` uses `clean::utils::format_integer_with_underscore_sep` under the |
726 | // hood which adds noisy underscores and a type suffix to number literals. | |
727 | // This hurts readability in this context especially when more complex expressions | |
728 | // are involved and it doesn't add much of value. | |
729 | // Find a way to print constants here without all that jazz. | |
064997fb | 730 | write!(w, "{}", Escape(&default.value(cx.tcx()).unwrap_or_else(|| default.expr(cx.tcx())))); |
04454e1e | 731 | } |
d9579d0f AL |
732 | } |
733 | ||
dfeec247 XL |
734 | fn assoc_type( |
735 | w: &mut Buffer, | |
736 | it: &clean::Item, | |
5e7ed085 | 737 | generics: &clean::Generics, |
dfeec247 XL |
738 | bounds: &[clean::GenericBound], |
739 | default: Option<&clean::Type>, | |
740 | link: AssocItemLink<'_>, | |
5e7ed085 | 741 | indent: usize, |
cdc7bbd5 | 742 | cx: &Context<'_>, |
dfeec247 XL |
743 | ) { |
744 | write!( | |
745 | w, | |
04454e1e | 746 | "{indent}type <a{href} class=\"associatedtype\">{name}</a>{generics}", |
5e7ed085 | 747 | indent = " ".repeat(indent), |
04454e1e | 748 | href = assoc_href_attr(it, link, cx), |
5e7ed085 FG |
749 | name = it.name.as_ref().unwrap(), |
750 | generics = generics.print(cx), | |
dfeec247 | 751 | ); |
9346a6ac | 752 | if !bounds.is_empty() { |
cdc7bbd5 | 753 | write!(w, ": {}", print_generic_bounds(bounds, cx)) |
1a4d82fc | 754 | } |
064997fb | 755 | write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline)); |
54a0048b | 756 | if let Some(default) = default { |
cdc7bbd5 | 757 | write!(w, " = {}", default.print(cx)) |
1a4d82fc | 758 | } |
1a4d82fc JJ |
759 | } |
760 | ||
5e7ed085 FG |
761 | fn assoc_method( |
762 | w: &mut Buffer, | |
763 | meth: &clean::Item, | |
764 | g: &clean::Generics, | |
765 | d: &clean::FnDecl, | |
766 | link: AssocItemLink<'_>, | |
767 | parent: ItemType, | |
768 | cx: &Context<'_>, | |
769 | render_mode: RenderMode, | |
770 | ) { | |
771 | let header = meth.fn_header(cx.tcx()).expect("Trying to get header from a non-function item"); | |
772 | let name = meth.name.as_ref().unwrap(); | |
04454e1e | 773 | let vis = meth.visibility.print_with_space(meth.item_id, cx).to_string(); |
5e7ed085 FG |
774 | // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove |
775 | // this condition. | |
776 | let constness = match render_mode { | |
777 | RenderMode::Normal => { | |
778 | print_constness_with_space(&header.constness, meth.const_stability(cx.tcx())) | |
779 | } | |
780 | RenderMode::ForDeref { .. } => "", | |
781 | }; | |
782 | let asyncness = header.asyncness.print_with_space(); | |
783 | let unsafety = header.unsafety.print_with_space(); | |
784 | let defaultness = print_default_space(meth.is_default()); | |
785 | let abi = print_abi_with_space(header.abi).to_string(); | |
04454e1e | 786 | let href = assoc_href_attr(meth, link, cx); |
5e7ed085 FG |
787 | |
788 | // NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`. | |
789 | let generics_len = format!("{:#}", g.print(cx)).len(); | |
790 | let mut header_len = "fn ".len() | |
791 | + vis.len() | |
792 | + constness.len() | |
793 | + asyncness.len() | |
794 | + unsafety.len() | |
795 | + defaultness.len() | |
796 | + abi.len() | |
797 | + name.as_str().len() | |
798 | + generics_len; | |
799 | ||
800 | let (indent, indent_str, end_newline) = if parent == ItemType::Trait { | |
801 | header_len += 4; | |
802 | let indent_str = " "; | |
803 | render_attributes_in_pre(w, meth, indent_str); | |
064997fb | 804 | (4, indent_str, Ending::NoNewline) |
5e7ed085 FG |
805 | } else { |
806 | render_attributes_in_code(w, meth); | |
064997fb | 807 | (0, "", Ending::Newline) |
5e7ed085 FG |
808 | }; |
809 | w.reserve(header_len + "<a href=\"\" class=\"fnname\">{".len() + "</a>".len()); | |
810 | write!( | |
811 | w, | |
04454e1e | 812 | "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn <a{href} class=\"fnname\">{name}</a>\ |
5e7ed085 FG |
813 | {generics}{decl}{notable_traits}{where_clause}", |
814 | indent = indent_str, | |
815 | vis = vis, | |
816 | constness = constness, | |
817 | asyncness = asyncness, | |
818 | unsafety = unsafety, | |
819 | defaultness = defaultness, | |
820 | abi = abi, | |
04454e1e | 821 | href = href, |
5e7ed085 FG |
822 | name = name, |
823 | generics = g.print(cx), | |
f2b60f7d | 824 | decl = d.full_print(header_len, indent, cx), |
5e7ed085 FG |
825 | notable_traits = notable_traits_decl(d, cx), |
826 | where_clause = print_where_clause(g, cx, indent, end_newline), | |
827 | ) | |
828 | } | |
829 | ||
5099ac24 FG |
830 | /// Writes a span containing the versions at which an item became stable and/or const-stable. For |
831 | /// example, if the item became stable at 1.0.0, and const-stable at 1.45.0, this function would | |
832 | /// write a span containing "1.0.0 (const: 1.45.0)". | |
833 | /// | |
834 | /// Returns `true` if a stability annotation was rendered. | |
835 | /// | |
836 | /// Stability and const-stability are considered separately. If the item is unstable, no version | |
837 | /// will be written. If the item is const-unstable, "const: unstable" will be appended to the | |
838 | /// span, with a link to the tracking issue if present. If an item's stability or const-stability | |
839 | /// version matches the version of its enclosing item, that version will be omitted. | |
840 | /// | |
841 | /// Note that it is possible for an unstable function to be const-stable. In that case, the span | |
842 | /// will include the const-stable version, but no stable version will be emitted, as a natural | |
843 | /// consequence of the above rules. | |
f2b60f7d | 844 | fn render_stability_since_raw_with_extra( |
fc512014 | 845 | w: &mut Buffer, |
a2a8927a XL |
846 | ver: Option<Symbol>, |
847 | const_stability: Option<ConstStability>, | |
848 | containing_ver: Option<Symbol>, | |
849 | containing_const_ver: Option<Symbol>, | |
f2b60f7d | 850 | extra_class: &str, |
5099ac24 FG |
851 | ) -> bool { |
852 | let stable_version = ver.filter(|inner| !inner.is_empty() && Some(*inner) != containing_ver); | |
853 | ||
854 | let mut title = String::new(); | |
855 | let mut stability = String::new(); | |
fc512014 | 856 | |
5099ac24 | 857 | if let Some(ver) = stable_version { |
923072b8 | 858 | stability.push_str(ver.as_str()); |
5099ac24 FG |
859 | title.push_str(&format!("Stable since Rust version {}", ver)); |
860 | } | |
861 | ||
862 | let const_title_and_stability = match const_stability { | |
064997fb | 863 | Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) |
a2a8927a | 864 | if Some(since) != containing_const_ver => |
136023e0 | 865 | { |
5099ac24 | 866 | Some((format!("const since {}", since), format!("const: {}", since))) |
136023e0 | 867 | } |
5099ac24 FG |
868 | Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => { |
869 | let unstable = if let Some(n) = issue { | |
870 | format!( | |
871 | r#"<a href="https://github.com/rust-lang/rust/issues/{}" title="Tracking issue for {}">unstable</a>"#, | |
136023e0 | 872 | n, feature |
5099ac24 | 873 | ) |
136023e0 | 874 | } else { |
5099ac24 FG |
875 | String::from("unstable") |
876 | }; | |
877 | ||
878 | Some((String::from("const unstable"), format!("const: {}", unstable))) | |
5869c6ff | 879 | } |
5099ac24 FG |
880 | _ => None, |
881 | }; | |
882 | ||
883 | if let Some((const_title, const_stability)) = const_title_and_stability { | |
884 | if !title.is_empty() { | |
885 | title.push_str(&format!(", {}", const_title)); | |
886 | } else { | |
887 | title.push_str(&const_title); | |
888 | } | |
889 | ||
890 | if !stability.is_empty() { | |
891 | stability.push_str(&format!(" ({})", const_stability)); | |
892 | } else { | |
893 | stability.push_str(&const_stability); | |
7453a54e SL |
894 | } |
895 | } | |
5099ac24 FG |
896 | |
897 | if !stability.is_empty() { | |
f2b60f7d | 898 | write!(w, r#"<span class="since{extra_class}" title="{title}">{stability}</span>"#); |
5099ac24 FG |
899 | } |
900 | ||
901 | !stability.is_empty() | |
7453a54e SL |
902 | } |
903 | ||
f2b60f7d FG |
904 | #[inline] |
905 | fn render_stability_since_raw( | |
906 | w: &mut Buffer, | |
907 | ver: Option<Symbol>, | |
908 | const_stability: Option<ConstStability>, | |
909 | containing_ver: Option<Symbol>, | |
910 | containing_const_ver: Option<Symbol>, | |
911 | ) -> bool { | |
912 | render_stability_since_raw_with_extra( | |
913 | w, | |
914 | ver, | |
915 | const_stability, | |
916 | containing_ver, | |
917 | containing_const_ver, | |
918 | "", | |
919 | ) | |
920 | } | |
921 | ||
dfeec247 XL |
922 | fn render_assoc_item( |
923 | w: &mut Buffer, | |
924 | item: &clean::Item, | |
925 | link: AssocItemLink<'_>, | |
926 | parent: ItemType, | |
fc512014 | 927 | cx: &Context<'_>, |
a2a8927a | 928 | render_mode: RenderMode, |
dfeec247 | 929 | ) { |
04454e1e | 930 | match &*item.kind { |
dfeec247 | 931 | clean::StrippedItem(..) => {} |
04454e1e | 932 | clean::TyMethodItem(m) => { |
5e7ed085 | 933 | assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode) |
fc512014 | 934 | } |
04454e1e | 935 | clean::MethodItem(m, _) => { |
5e7ed085 | 936 | assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode) |
a2a8927a | 937 | } |
04454e1e FG |
938 | kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => assoc_const( |
939 | w, | |
940 | item, | |
941 | ty, | |
942 | match kind { | |
943 | clean::TyAssocConstItem(_) => None, | |
944 | clean::AssocConstItem(_, default) => Some(default), | |
945 | _ => unreachable!(), | |
946 | }, | |
947 | link, | |
948 | if parent == ItemType::Trait { " " } else { "" }, | |
949 | cx, | |
950 | ), | |
951 | clean::TyAssocTypeItem(ref generics, ref bounds) => assoc_type( | |
dfeec247 XL |
952 | w, |
953 | item, | |
5e7ed085 | 954 | generics, |
dfeec247 | 955 | bounds, |
04454e1e FG |
956 | None, |
957 | link, | |
958 | if parent == ItemType::Trait { 4 } else { 0 }, | |
959 | cx, | |
960 | ), | |
961 | clean::AssocTypeItem(ref ty, ref bounds) => assoc_type( | |
962 | w, | |
963 | item, | |
964 | &ty.generics, | |
965 | bounds, | |
966 | Some(ty.item_type.as_ref().unwrap_or(&ty.type_)), | |
dfeec247 | 967 | link, |
5e7ed085 | 968 | if parent == ItemType::Trait { 4 } else { 0 }, |
cdc7bbd5 | 969 | cx, |
dfeec247 XL |
970 | ), |
971 | _ => panic!("render_assoc_item called on non-associated-item"), | |
1a4d82fc JJ |
972 | } |
973 | } | |
974 | ||
136023e0 XL |
975 | const ALLOWED_ATTRIBUTES: &[Symbol] = |
976 | &[sym::export_name, sym::link_section, sym::no_mangle, sym::repr, sym::non_exhaustive]; | |
476ff2be | 977 | |
cdc7bbd5 XL |
978 | fn attributes(it: &clean::Item) -> Vec<String> { |
979 | it.attrs | |
f035d41b XL |
980 | .other_attrs |
981 | .iter() | |
982 | .filter_map(|attr| { | |
983 | if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) { | |
5e7ed085 FG |
984 | Some( |
985 | pprust::attribute_to_string(attr) | |
986 | .replace("\\\n", "") | |
987 | .replace('\n', "") | |
988 | .replace(" ", " "), | |
989 | ) | |
f035d41b XL |
990 | } else { |
991 | None | |
992 | } | |
993 | }) | |
cdc7bbd5 XL |
994 | .collect() |
995 | } | |
ba9703b0 | 996 | |
cdc7bbd5 XL |
997 | // When an attribute is rendered inside a `<pre>` tag, it is formatted using |
998 | // a whitespace prefix and newline. | |
999 | fn render_attributes_in_pre(w: &mut Buffer, it: &clean::Item, prefix: &str) { | |
1000 | for a in attributes(it) { | |
17df50a5 | 1001 | writeln!(w, "{}{}", prefix, a); |
cdc7bbd5 XL |
1002 | } |
1003 | } | |
1004 | ||
1005 | // When an attribute is rendered inside a <code> tag, it is formatted using | |
1006 | // a div to produce a newline after it. | |
1007 | fn render_attributes_in_code(w: &mut Buffer, it: &clean::Item) { | |
1008 | for a in attributes(it) { | |
1009 | write!(w, "<div class=\"code-attribute\">{}</div>", a); | |
85aaf69f | 1010 | } |
85aaf69f SL |
1011 | } |
1012 | ||
9346a6ac | 1013 | #[derive(Copy, Clone)] |
54a0048b | 1014 | enum AssocItemLink<'a> { |
a7813a04 | 1015 | Anchor(Option<&'a str>), |
136023e0 | 1016 | GotoSource(ItemId, &'a FxHashSet<Symbol>), |
9346a6ac AL |
1017 | } |
1018 | ||
a7813a04 | 1019 | impl<'a> AssocItemLink<'a> { |
5869c6ff | 1020 | fn anchor(&self, id: &'a str) -> Self { |
a7813a04 | 1021 | match *self { |
3c0e092e | 1022 | AssocItemLink::Anchor(_) => AssocItemLink::Anchor(Some(id)), |
a7813a04 XL |
1023 | ref other => *other, |
1024 | } | |
1025 | } | |
1026 | } | |
1027 | ||
f2b60f7d FG |
1028 | fn write_impl_section_heading(w: &mut Buffer, title: &str, id: &str) { |
1029 | write!( | |
1030 | w, | |
1031 | "<h2 id=\"{id}\" class=\"small-section-header\">\ | |
1032 | {title}\ | |
1033 | <a href=\"#{id}\" class=\"anchor\"></a>\ | |
1034 | </h2>" | |
1035 | ); | |
1036 | } | |
1037 | ||
1038 | pub(crate) fn render_all_impls( | |
1039 | w: &mut Buffer, | |
1040 | cx: &mut Context<'_>, | |
1041 | containing_item: &clean::Item, | |
1042 | concrete: &[&Impl], | |
1043 | synthetic: &[&Impl], | |
1044 | blanket_impl: &[&Impl], | |
1045 | ) { | |
1046 | let mut impls = Buffer::empty_from(w); | |
1047 | render_impls(cx, &mut impls, concrete, containing_item, true); | |
1048 | let impls = impls.into_inner(); | |
1049 | if !impls.is_empty() { | |
1050 | write_impl_section_heading(w, "Trait Implementations", "trait-implementations"); | |
1051 | write!(w, "<div id=\"trait-implementations-list\">{}</div>", impls); | |
1052 | } | |
1053 | ||
1054 | if !synthetic.is_empty() { | |
1055 | write_impl_section_heading(w, "Auto Trait Implementations", "synthetic-implementations"); | |
1056 | w.write_str("<div id=\"synthetic-implementations-list\">"); | |
1057 | render_impls(cx, w, synthetic, containing_item, false); | |
1058 | w.write_str("</div>"); | |
1059 | } | |
1060 | ||
1061 | if !blanket_impl.is_empty() { | |
1062 | write_impl_section_heading(w, "Blanket Implementations", "blanket-implementations"); | |
1063 | w.write_str("<div id=\"blanket-implementations-list\">"); | |
1064 | render_impls(cx, w, blanket_impl, containing_item, false); | |
1065 | w.write_str("</div>"); | |
1066 | } | |
1067 | } | |
1068 | ||
dfeec247 XL |
1069 | fn render_assoc_items( |
1070 | w: &mut Buffer, | |
923072b8 | 1071 | cx: &mut Context<'_>, |
dfeec247 XL |
1072 | containing_item: &clean::Item, |
1073 | it: DefId, | |
1074 | what: AssocItemRender<'_>, | |
3c0e092e XL |
1075 | ) { |
1076 | let mut derefs = FxHashSet::default(); | |
1077 | derefs.insert(it); | |
1078 | render_assoc_items_inner(w, cx, containing_item, it, what, &mut derefs) | |
1079 | } | |
1080 | ||
1081 | fn render_assoc_items_inner( | |
1082 | w: &mut Buffer, | |
923072b8 | 1083 | cx: &mut Context<'_>, |
3c0e092e XL |
1084 | containing_item: &clean::Item, |
1085 | it: DefId, | |
1086 | what: AssocItemRender<'_>, | |
1087 | derefs: &mut FxHashSet<DefId>, | |
dfeec247 | 1088 | ) { |
fc512014 | 1089 | info!("Documenting associated items of {:?}", containing_item.name); |
923072b8 FG |
1090 | let shared = Rc::clone(&cx.shared); |
1091 | let cache = &shared.cache; | |
5e7ed085 | 1092 | let Some(v) = cache.impls.get(&it) else { return }; |
dfeec247 | 1093 | let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none()); |
9346a6ac | 1094 | if !non_trait.is_empty() { |
3c0e092e | 1095 | let mut tmp_buf = Buffer::empty_from(w); |
5e7ed085 | 1096 | let (render_mode, id) = match what { |
d9579d0f | 1097 | AssocItemRender::All => { |
f2b60f7d | 1098 | write_impl_section_heading(&mut tmp_buf, "Implementations", "implementations"); |
5e7ed085 | 1099 | (RenderMode::Normal, "implementations-list".to_owned()) |
d9579d0f | 1100 | } |
9e0c209e | 1101 | AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { |
3c0e092e XL |
1102 | let id = |
1103 | cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx)))); | |
1104 | if let Some(def_id) = type_.def_id(cx.cache()) { | |
923072b8 | 1105 | cx.deref_id_map.insert(def_id, id.clone()); |
3c0e092e | 1106 | } |
f2b60f7d FG |
1107 | write_impl_section_heading( |
1108 | &mut tmp_buf, | |
1109 | &format!( | |
1110 | "<span>Methods from {trait_}<Target = {type_}></span>", | |
1111 | trait_ = trait_.print(cx), | |
1112 | type_ = type_.print(cx), | |
1113 | ), | |
1114 | &id, | |
dfeec247 | 1115 | ); |
5e7ed085 | 1116 | (RenderMode::ForDeref { mut_: deref_mut_ }, cx.derive_id(id)) |
d9579d0f AL |
1117 | } |
1118 | }; | |
3c0e092e | 1119 | let mut impls_buf = Buffer::empty_from(w); |
9346a6ac | 1120 | for i in &non_trait { |
dfeec247 | 1121 | render_impl( |
3c0e092e | 1122 | &mut impls_buf, |
dfeec247 XL |
1123 | cx, |
1124 | i, | |
fc512014 | 1125 | containing_item, |
dfeec247 XL |
1126 | AssocItemLink::Anchor(None), |
1127 | render_mode, | |
dfeec247 | 1128 | None, |
dfeec247 | 1129 | &[], |
136023e0 XL |
1130 | ImplRenderingParameters { |
1131 | show_def_docs: true, | |
136023e0 XL |
1132 | show_default_items: true, |
1133 | show_non_assoc_items: true, | |
1134 | toggle_open_by_default: true, | |
1135 | }, | |
dfeec247 | 1136 | ); |
9346a6ac | 1137 | } |
3c0e092e XL |
1138 | if !impls_buf.is_empty() { |
1139 | w.push_buffer(tmp_buf); | |
5e7ed085 | 1140 | write!(w, "<div id=\"{}\">", id); |
3c0e092e | 1141 | w.push_buffer(impls_buf); |
5e7ed085 | 1142 | w.write_str("</div>"); |
3c0e092e | 1143 | } |
9346a6ac | 1144 | } |
3c0e092e | 1145 | |
9346a6ac | 1146 | if !traits.is_empty() { |
c295e0f8 XL |
1147 | let deref_impl = |
1148 | traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait()); | |
d9579d0f | 1149 | if let Some(impl_) = deref_impl { |
c295e0f8 XL |
1150 | let has_deref_mut = |
1151 | traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait()); | |
3c0e092e XL |
1152 | render_deref_methods(w, cx, impl_, containing_item, has_deref_mut, derefs); |
1153 | } | |
1154 | ||
1155 | // If we were already one level into rendering deref methods, we don't want to render | |
1156 | // anything after recursing into any further deref methods above. | |
1157 | if let AssocItemRender::DerefFor { .. } = what { | |
1158 | return; | |
5869c6ff | 1159 | } |
3c0e092e | 1160 | |
f2b60f7d FG |
1161 | let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = |
1162 | traits.into_iter().partition(|t| t.inner_impl().kind.is_auto()); | |
1163 | let (blanket_impl, concrete): (Vec<&Impl>, _) = | |
3c0e092e | 1164 | concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket()); |
0531ce1d | 1165 | |
f2b60f7d | 1166 | render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl); |
1a4d82fc | 1167 | } |
1a4d82fc JJ |
1168 | } |
1169 | ||
dfeec247 XL |
1170 | fn render_deref_methods( |
1171 | w: &mut Buffer, | |
923072b8 | 1172 | cx: &mut Context<'_>, |
dfeec247 XL |
1173 | impl_: &Impl, |
1174 | container_item: &clean::Item, | |
1175 | deref_mut: bool, | |
3c0e092e | 1176 | derefs: &mut FxHashSet<DefId>, |
dfeec247 | 1177 | ) { |
94222f64 | 1178 | let cache = cx.cache(); |
a7813a04 | 1179 | let deref_type = impl_.inner_impl().trait_.as_ref().unwrap(); |
dfeec247 XL |
1180 | let (target, real_target) = impl_ |
1181 | .inner_impl() | |
1182 | .items | |
1183 | .iter() | |
5869c6ff | 1184 | .find_map(|item| match *item.kind { |
064997fb | 1185 | clean::AssocTypeItem(box ref t, _) => Some(match *t { |
dfeec247 XL |
1186 | clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), |
1187 | _ => (&t.type_, &t.type_), | |
1188 | }), | |
d9579d0f | 1189 | _ => None, |
dfeec247 | 1190 | }) |
dfeec247 | 1191 | .expect("Expected associated type binding"); |
5869c6ff | 1192 | debug!("Render deref methods for {:#?}, target {:#?}", impl_.inner_impl().for_, target); |
dfeec247 XL |
1193 | let what = |
1194 | AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut }; | |
3c0e092e XL |
1195 | if let Some(did) = target.def_id(cache) { |
1196 | if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) { | |
5869c6ff | 1197 | // `impl Deref<Target = S> for S` |
3c0e092e | 1198 | if did == type_did || !derefs.insert(did) { |
5869c6ff XL |
1199 | // Avoid infinite cycles |
1200 | return; | |
1201 | } | |
1202 | } | |
3c0e092e | 1203 | render_assoc_items_inner(w, cx, container_item, did, what, derefs); |
5099ac24 FG |
1204 | } else if let Some(prim) = target.primitive_type() { |
1205 | if let Some(&did) = cache.primitive_locations.get(&prim) { | |
1206 | render_assoc_items_inner(w, cx, container_item, did, what, derefs); | |
d9579d0f | 1207 | } |
1a4d82fc | 1208 | } |
d9579d0f AL |
1209 | } |
1210 | ||
c295e0f8 | 1211 | fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool { |
5869c6ff | 1212 | let self_type_opt = match *item.kind { |
fc512014 | 1213 | clean::MethodItem(ref method, _) => method.decl.self_type(), |
abe05a73 | 1214 | clean::TyMethodItem(ref method) => method.decl.self_type(), |
dfeec247 | 1215 | _ => None, |
abe05a73 XL |
1216 | }; |
1217 | ||
1218 | if let Some(self_ty) = self_type_opt { | |
1219 | let (by_mut_ref, by_box, by_value) = match self_ty { | |
dfeec247 XL |
1220 | SelfTy::SelfBorrowed(_, mutability) |
1221 | | SelfTy::SelfExplicit(clean::BorrowedRef { mutability, .. }) => { | |
1222 | (mutability == Mutability::Mut, false, false) | |
1223 | } | |
3c0e092e XL |
1224 | SelfTy::SelfExplicit(clean::Type::Path { path }) => { |
1225 | (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false) | |
dfeec247 | 1226 | } |
abe05a73 XL |
1227 | SelfTy::SelfValue => (false, false, true), |
1228 | _ => (false, false, false), | |
1229 | }; | |
1230 | ||
1231 | (deref_mut_ || !by_mut_ref) && !by_box && !by_value | |
1232 | } else { | |
1233 | false | |
1234 | } | |
1235 | } | |
1236 | ||
cdc7bbd5 | 1237 | fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String { |
3dfed10e | 1238 | let mut out = Buffer::html(); |
3dfed10e | 1239 | |
a2a8927a XL |
1240 | if let Some((did, ty)) = decl.output.as_return().and_then(|t| Some((t.def_id(cx.cache())?, t))) |
1241 | { | |
cdc7bbd5 | 1242 | if let Some(impls) = cx.cache().impls.get(&did) { |
3dfed10e XL |
1243 | for i in impls { |
1244 | let impl_ = i.inner_impl(); | |
a2a8927a XL |
1245 | if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache()) |
1246 | { | |
1247 | // Two different types might have the same did, | |
1248 | // without actually being the same. | |
1249 | continue; | |
1250 | } | |
c295e0f8 XL |
1251 | if let Some(trait_) = &impl_.trait_ { |
1252 | let trait_did = trait_.def_id(); | |
3dfed10e | 1253 | |
c295e0f8 XL |
1254 | if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable) { |
1255 | if out.is_empty() { | |
1256 | write!( | |
3dfed10e | 1257 | &mut out, |
5099ac24 | 1258 | "<span class=\"notable\">Notable traits for {}</span>\ |
c295e0f8 XL |
1259 | <code class=\"content\">", |
1260 | impl_.for_.print(cx) | |
3dfed10e | 1261 | ); |
c295e0f8 XL |
1262 | } |
1263 | ||
1264 | //use the "where" class here to make it small | |
1265 | write!( | |
1266 | &mut out, | |
1267 | "<span class=\"where fmt-newline\">{}</span>", | |
1268 | impl_.print(false, cx) | |
1269 | ); | |
1270 | for it in &impl_.items { | |
04454e1e | 1271 | if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind { |
c295e0f8 XL |
1272 | out.push_str("<span class=\"where fmt-newline\"> "); |
1273 | let empty_set = FxHashSet::default(); | |
1274 | let src_link = | |
1275 | AssocItemLink::GotoSource(trait_did.into(), &empty_set); | |
5e7ed085 FG |
1276 | assoc_type( |
1277 | &mut out, | |
1278 | it, | |
1279 | &tydef.generics, | |
04454e1e | 1280 | &[], // intentionally leaving out bounds |
5e7ed085 FG |
1281 | Some(&tydef.type_), |
1282 | src_link, | |
1283 | 0, | |
1284 | cx, | |
1285 | ); | |
c295e0f8 XL |
1286 | out.push_str(";</span>"); |
1287 | } | |
3dfed10e XL |
1288 | } |
1289 | } | |
1290 | } | |
1291 | } | |
1292 | } | |
1293 | } | |
1294 | ||
1295 | if !out.is_empty() { | |
1296 | out.insert_str( | |
1297 | 0, | |
29967ef6 | 1298 | "<span class=\"notable-traits\"><span class=\"notable-traits-tooltip\">ⓘ\ |
5099ac24 | 1299 | <span class=\"notable-traits-tooltiptext\"><span class=\"docblock\">", |
3dfed10e | 1300 | ); |
5099ac24 | 1301 | out.push_str("</code></span></span></span></span>"); |
3dfed10e XL |
1302 | } |
1303 | ||
1304 | out.into_inner() | |
1305 | } | |
1306 | ||
136023e0 XL |
1307 | #[derive(Clone, Copy, Debug)] |
1308 | struct ImplRenderingParameters { | |
1309 | show_def_docs: bool, | |
136023e0 XL |
1310 | show_default_items: bool, |
1311 | /// Whether or not to show methods. | |
1312 | show_non_assoc_items: bool, | |
1313 | toggle_open_by_default: bool, | |
1314 | } | |
1315 | ||
dfeec247 XL |
1316 | fn render_impl( |
1317 | w: &mut Buffer, | |
923072b8 | 1318 | cx: &mut Context<'_>, |
dfeec247 | 1319 | i: &Impl, |
fc512014 | 1320 | parent: &clean::Item, |
dfeec247 XL |
1321 | link: AssocItemLink<'_>, |
1322 | render_mode: RenderMode, | |
dfeec247 | 1323 | use_absolute: Option<bool>, |
dfeec247 | 1324 | aliases: &[String], |
136023e0 | 1325 | rendering_params: ImplRenderingParameters, |
dfeec247 | 1326 | ) { |
923072b8 FG |
1327 | let shared = Rc::clone(&cx.shared); |
1328 | let cache = &shared.cache; | |
cdc7bbd5 | 1329 | let traits = &cache.traits; |
c295e0f8 | 1330 | let trait_ = i.trait_did().map(|did| &traits[&did]); |
cdc7bbd5 | 1331 | let mut close_tags = String::new(); |
1a4d82fc | 1332 | |
17df50a5 XL |
1333 | // For trait implementations, the `interesting` output contains all methods that have doc |
1334 | // comments, and the `boring` output contains all methods that do not. The distinction is | |
1335 | // used to allow hiding the boring methods. | |
136023e0 XL |
1336 | // `containing_item` is used for rendering stability info. If the parent is a trait impl, |
1337 | // `containing_item` will the grandparent, since trait impls can't have stability attached. | |
dfeec247 | 1338 | fn doc_impl_item( |
17df50a5 XL |
1339 | boring: &mut Buffer, |
1340 | interesting: &mut Buffer, | |
923072b8 | 1341 | cx: &mut Context<'_>, |
dfeec247 | 1342 | item: &clean::Item, |
fc512014 | 1343 | parent: &clean::Item, |
136023e0 | 1344 | containing_item: &clean::Item, |
dfeec247 XL |
1345 | link: AssocItemLink<'_>, |
1346 | render_mode: RenderMode, | |
1347 | is_default_item: bool, | |
dfeec247 | 1348 | trait_: Option<&clean::Trait>, |
136023e0 | 1349 | rendering_params: ImplRenderingParameters, |
dfeec247 | 1350 | ) { |
c30ab7b3 | 1351 | let item_type = item.type_(); |
92a42be0 | 1352 | let name = item.name.as_ref().unwrap(); |
54a0048b | 1353 | |
136023e0 XL |
1354 | let render_method_item = rendering_params.show_non_assoc_items |
1355 | && match render_mode { | |
1356 | RenderMode::Normal => true, | |
1357 | RenderMode::ForDeref { mut_: deref_mut_ } => { | |
3c0e092e | 1358 | should_render_item(item, deref_mut_, cx.tcx()) |
136023e0 XL |
1359 | } |
1360 | }; | |
54a0048b | 1361 | |
cdc7bbd5 | 1362 | let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" }; |
17df50a5 XL |
1363 | |
1364 | let mut doc_buffer = Buffer::empty_from(boring); | |
1365 | let mut info_buffer = Buffer::empty_from(boring); | |
1366 | let mut short_documented = true; | |
1367 | ||
1368 | if render_method_item { | |
1369 | if !is_default_item { | |
1370 | if let Some(t) = trait_ { | |
1371 | // The trait item may have been stripped so we might not | |
1372 | // find any documentation or stability for it. | |
1373 | if let Some(it) = t.items.iter().find(|i| i.name == item.name) { | |
1374 | // We need the stability of the item from the trait | |
1375 | // because impls can't have a stability. | |
1376 | if item.doc_value().is_some() { | |
1377 | document_item_info(&mut info_buffer, cx, it, Some(parent)); | |
c295e0f8 | 1378 | document_full(&mut doc_buffer, item, cx, HeadingOffset::H5); |
17df50a5 XL |
1379 | short_documented = false; |
1380 | } else { | |
1381 | // In case the item isn't documented, | |
1382 | // provide short documentation from the trait. | |
136023e0 XL |
1383 | document_short( |
1384 | &mut doc_buffer, | |
1385 | it, | |
1386 | cx, | |
1387 | link, | |
1388 | parent, | |
1389 | rendering_params.show_def_docs, | |
1390 | ); | |
17df50a5 XL |
1391 | } |
1392 | } | |
1393 | } else { | |
1394 | document_item_info(&mut info_buffer, cx, item, Some(parent)); | |
136023e0 | 1395 | if rendering_params.show_def_docs { |
c295e0f8 | 1396 | document_full(&mut doc_buffer, item, cx, HeadingOffset::H5); |
17df50a5 XL |
1397 | short_documented = false; |
1398 | } | |
1399 | } | |
1400 | } else { | |
136023e0 XL |
1401 | document_short( |
1402 | &mut doc_buffer, | |
1403 | item, | |
1404 | cx, | |
1405 | link, | |
1406 | parent, | |
1407 | rendering_params.show_def_docs, | |
1408 | ); | |
17df50a5 XL |
1409 | } |
1410 | } | |
1411 | let w = if short_documented && trait_.is_some() { interesting } else { boring }; | |
1412 | ||
1413 | let toggled = !doc_buffer.is_empty(); | |
1414 | if toggled { | |
1415 | let method_toggle_class = | |
1416 | if item_type == ItemType::Method { " method-toggle" } else { "" }; | |
1417 | write!(w, "<details class=\"rustdoc-toggle{}\" open><summary>", method_toggle_class); | |
1418 | } | |
04454e1e | 1419 | match &*item.kind { |
fc512014 | 1420 | clean::MethodItem(..) | clean::TyMethodItem(_) => { |
62682a34 | 1421 | // Only render when the method is not static or we allow static methods |
9e0c209e | 1422 | if render_method_item { |
b7449926 | 1423 | let id = cx.derive_id(format!("{}.{}", item_type, name)); |
cdc7bbd5 XL |
1424 | let source_id = trait_ |
1425 | .and_then(|trait_| { | |
1426 | trait_.items.iter().find(|item| { | |
a2a8927a | 1427 | item.name.map(|n| n.as_str().eq(name.as_str())).unwrap_or(false) |
cdc7bbd5 XL |
1428 | }) |
1429 | }) | |
1430 | .map(|item| format!("{}.{}", item.type_(), name)); | |
1431 | write!( | |
1432 | w, | |
5099ac24 | 1433 | "<section id=\"{}\" class=\"{}{} has-srclink\">", |
17df50a5 | 1434 | id, item_type, in_trait_class, |
cdc7bbd5 | 1435 | ); |
a2a8927a | 1436 | render_rightside(w, cx, item, containing_item, render_mode); |
064997fb FG |
1437 | if trait_.is_some() { |
1438 | // Anchors are only used on trait impls. | |
1439 | write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id); | |
1440 | } | |
17df50a5 | 1441 | w.write_str("<h4 class=\"code-header\">"); |
cdc7bbd5 XL |
1442 | render_assoc_item( |
1443 | w, | |
1444 | item, | |
1445 | link.anchor(source_id.as_ref().unwrap_or(&id)), | |
1446 | ItemType::Impl, | |
1447 | cx, | |
a2a8927a | 1448 | render_mode, |
cdc7bbd5 | 1449 | ); |
17df50a5 | 1450 | w.write_str("</h4>"); |
5099ac24 | 1451 | w.write_str("</section>"); |
62682a34 | 1452 | } |
1a4d82fc | 1453 | } |
04454e1e FG |
1454 | kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => { |
1455 | let source_id = format!("{}.{}", item_type, name); | |
cdc7bbd5 XL |
1456 | let id = cx.derive_id(source_id.clone()); |
1457 | write!( | |
1458 | w, | |
5099ac24 | 1459 | "<section id=\"{}\" class=\"{}{} has-srclink\">", |
17df50a5 | 1460 | id, item_type, in_trait_class |
cdc7bbd5 | 1461 | ); |
04454e1e | 1462 | render_rightside(w, cx, item, containing_item, render_mode); |
064997fb FG |
1463 | if trait_.is_some() { |
1464 | // Anchors are only used on trait impls. | |
1465 | write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id); | |
1466 | } | |
17df50a5 | 1467 | w.write_str("<h4 class=\"code-header\">"); |
04454e1e | 1468 | assoc_const( |
5869c6ff XL |
1469 | w, |
1470 | item, | |
04454e1e FG |
1471 | ty, |
1472 | match kind { | |
1473 | clean::TyAssocConstItem(_) => None, | |
1474 | clean::AssocConstItem(_, default) => Some(default), | |
1475 | _ => unreachable!(), | |
1476 | }, | |
cdc7bbd5 | 1477 | link.anchor(if trait_.is_some() { &source_id } else { &id }), |
04454e1e | 1478 | "", |
cdc7bbd5 | 1479 | cx, |
5869c6ff | 1480 | ); |
cdc7bbd5 | 1481 | w.write_str("</h4>"); |
5099ac24 | 1482 | w.write_str("</section>"); |
1a4d82fc | 1483 | } |
04454e1e | 1484 | clean::TyAssocTypeItem(generics, bounds) => { |
cdc7bbd5 XL |
1485 | let source_id = format!("{}.{}", item_type, name); |
1486 | let id = cx.derive_id(source_id.clone()); | |
04454e1e | 1487 | write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class); |
064997fb FG |
1488 | if trait_.is_some() { |
1489 | // Anchors are only used on trait impls. | |
1490 | write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id); | |
1491 | } | |
17df50a5 | 1492 | w.write_str("<h4 class=\"code-header\">"); |
04454e1e | 1493 | assoc_type( |
cdc7bbd5 XL |
1494 | w, |
1495 | item, | |
04454e1e FG |
1496 | generics, |
1497 | bounds, | |
1498 | None, | |
cdc7bbd5 | 1499 | link.anchor(if trait_.is_some() { &source_id } else { &id }), |
04454e1e | 1500 | 0, |
cdc7bbd5 XL |
1501 | cx, |
1502 | ); | |
17df50a5 | 1503 | w.write_str("</h4>"); |
5099ac24 | 1504 | w.write_str("</section>"); |
d9579d0f | 1505 | } |
04454e1e | 1506 | clean::AssocTypeItem(tydef, _bounds) => { |
cdc7bbd5 XL |
1507 | let source_id = format!("{}.{}", item_type, name); |
1508 | let id = cx.derive_id(source_id.clone()); | |
04454e1e FG |
1509 | write!( |
1510 | w, | |
1511 | "<section id=\"{}\" class=\"{}{} has-srclink\">", | |
1512 | id, item_type, in_trait_class | |
1513 | ); | |
064997fb FG |
1514 | if trait_.is_some() { |
1515 | // Anchors are only used on trait impls. | |
1516 | write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id); | |
1517 | } | |
17df50a5 | 1518 | w.write_str("<h4 class=\"code-header\">"); |
cdc7bbd5 XL |
1519 | assoc_type( |
1520 | w, | |
1521 | item, | |
04454e1e FG |
1522 | &tydef.generics, |
1523 | &[], // intentionally leaving out bounds | |
1524 | Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)), | |
cdc7bbd5 | 1525 | link.anchor(if trait_.is_some() { &source_id } else { &id }), |
5e7ed085 | 1526 | 0, |
cdc7bbd5 XL |
1527 | cx, |
1528 | ); | |
cdc7bbd5 | 1529 | w.write_str("</h4>"); |
5099ac24 | 1530 | w.write_str("</section>"); |
1a4d82fc | 1531 | } |
e1599b0c | 1532 | clean::StrippedItem(..) => return, |
dfeec247 | 1533 | _ => panic!("can't make docs for trait item with name {:?}", item.name), |
1a4d82fc | 1534 | } |
62682a34 | 1535 | |
17df50a5 XL |
1536 | w.push_buffer(info_buffer); |
1537 | if toggled { | |
1538 | w.write_str("</summary>"); | |
1539 | w.push_buffer(doc_buffer); | |
1540 | w.push_str("</details>"); | |
1a4d82fc JJ |
1541 | } |
1542 | } | |
1543 | ||
cdc7bbd5 | 1544 | let mut impl_items = Buffer::empty_from(w); |
17df50a5 XL |
1545 | let mut default_impl_items = Buffer::empty_from(w); |
1546 | ||
a7813a04 | 1547 | for trait_item in &i.inner_impl().items { |
dfeec247 | 1548 | doc_impl_item( |
17df50a5 | 1549 | &mut default_impl_items, |
cdc7bbd5 | 1550 | &mut impl_items, |
dfeec247 XL |
1551 | cx, |
1552 | trait_item, | |
fc512014 | 1553 | if trait_.is_some() { &i.impl_item } else { parent }, |
136023e0 | 1554 | parent, |
dfeec247 XL |
1555 | link, |
1556 | render_mode, | |
1557 | false, | |
6a06907d | 1558 | trait_.map(|t| &t.trait_), |
136023e0 | 1559 | rendering_params, |
dfeec247 | 1560 | ); |
1a4d82fc JJ |
1561 | } |
1562 | ||
dfeec247 | 1563 | fn render_default_items( |
17df50a5 XL |
1564 | boring: &mut Buffer, |
1565 | interesting: &mut Buffer, | |
923072b8 | 1566 | cx: &mut Context<'_>, |
dfeec247 XL |
1567 | t: &clean::Trait, |
1568 | i: &clean::Impl, | |
fc512014 | 1569 | parent: &clean::Item, |
136023e0 | 1570 | containing_item: &clean::Item, |
dfeec247 | 1571 | render_mode: RenderMode, |
136023e0 | 1572 | rendering_params: ImplRenderingParameters, |
dfeec247 | 1573 | ) { |
85aaf69f | 1574 | for trait_item in &t.items { |
f2b60f7d FG |
1575 | // Skip over any default trait items that are impossible to call |
1576 | // (e.g. if it has a `Self: Sized` bound on an unsized type). | |
1577 | if let Some(impl_def_id) = parent.item_id.as_def_id() | |
1578 | && let Some(trait_item_def_id) = trait_item.item_id.as_def_id() | |
1579 | && cx.tcx().is_impossible_method((impl_def_id, trait_item_def_id)) | |
1580 | { | |
1581 | continue; | |
1582 | } | |
1583 | ||
5869c6ff | 1584 | let n = trait_item.name; |
ba9703b0 | 1585 | if i.items.iter().any(|m| m.name == n) { |
54a0048b | 1586 | continue; |
1a4d82fc | 1587 | } |
c295e0f8 | 1588 | let did = i.trait_.as_ref().unwrap().def_id(); |
17df50a5 XL |
1589 | let provided_methods = i.provided_trait_methods(cx.tcx()); |
1590 | let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods); | |
1a4d82fc | 1591 | |
dfeec247 | 1592 | doc_impl_item( |
17df50a5 XL |
1593 | boring, |
1594 | interesting, | |
dfeec247 XL |
1595 | cx, |
1596 | trait_item, | |
29967ef6 | 1597 | parent, |
136023e0 | 1598 | containing_item, |
dfeec247 XL |
1599 | assoc_link, |
1600 | render_mode, | |
1601 | true, | |
cdc7bbd5 | 1602 | Some(t), |
136023e0 | 1603 | rendering_params, |
dfeec247 | 1604 | ); |
1a4d82fc | 1605 | } |
1a4d82fc JJ |
1606 | } |
1607 | ||
1608 | // If we've implemented a trait, then also emit documentation for all | |
54a0048b | 1609 | // default items which weren't overridden in the implementation block. |
dc9dc135 XL |
1610 | // We don't emit documentation for default items if they appear in the |
1611 | // Implementations on Foreign Types or Implementors sections. | |
136023e0 | 1612 | if rendering_params.show_default_items { |
dc9dc135 | 1613 | if let Some(t) = trait_ { |
dfeec247 | 1614 | render_default_items( |
17df50a5 | 1615 | &mut default_impl_items, |
cdc7bbd5 | 1616 | &mut impl_items, |
dfeec247 | 1617 | cx, |
6a06907d | 1618 | &t.trait_, |
3c0e092e | 1619 | i.inner_impl(), |
fc512014 | 1620 | &i.impl_item, |
136023e0 | 1621 | parent, |
dfeec247 | 1622 | render_mode, |
136023e0 | 1623 | rendering_params, |
dfeec247 | 1624 | ); |
dc9dc135 | 1625 | } |
1a4d82fc | 1626 | } |
cdc7bbd5 | 1627 | if render_mode == RenderMode::Normal { |
136023e0 | 1628 | let toggled = !(impl_items.is_empty() && default_impl_items.is_empty()); |
17df50a5 XL |
1629 | if toggled { |
1630 | close_tags.insert_str(0, "</details>"); | |
136023e0 XL |
1631 | write!( |
1632 | w, | |
1633 | "<details class=\"rustdoc-toggle implementors-toggle\"{}>", | |
1634 | if rendering_params.toggle_open_by_default { " open" } else { "" } | |
1635 | ); | |
17df50a5 XL |
1636 | write!(w, "<summary>") |
1637 | } | |
1638 | render_impl_summary( | |
cdc7bbd5 | 1639 | w, |
17df50a5 XL |
1640 | cx, |
1641 | i, | |
136023e0 XL |
1642 | parent, |
1643 | parent, | |
1644 | rendering_params.show_def_docs, | |
17df50a5 | 1645 | use_absolute, |
17df50a5 | 1646 | aliases, |
cdc7bbd5 | 1647 | ); |
17df50a5 XL |
1648 | if toggled { |
1649 | write!(w, "</summary>") | |
cdc7bbd5 | 1650 | } |
cdc7bbd5 | 1651 | |
a2a8927a | 1652 | if let Some(ref dox) = i.impl_item.collapsed_doc_value() { |
923072b8 FG |
1653 | if trait_.is_none() && i.inner_impl().items.is_empty() { |
1654 | w.write_str( | |
1655 | "<div class=\"item-info\">\ | |
1656 | <div class=\"stab empty-impl\">This impl block contains no items.</div> | |
1657 | </div>", | |
1658 | ); | |
1659 | } | |
cdc7bbd5 XL |
1660 | write!( |
1661 | w, | |
1662 | "<div class=\"docblock\">{}</div>", | |
c295e0f8 XL |
1663 | Markdown { |
1664 | content: &*dox, | |
1665 | links: &i.impl_item.links(cx), | |
923072b8 | 1666 | ids: &mut cx.id_map, |
c295e0f8 XL |
1667 | error_codes: cx.shared.codes, |
1668 | edition: cx.shared.edition(), | |
1669 | playground: &cx.shared.playground, | |
1670 | heading_offset: HeadingOffset::H4 | |
1671 | } | |
cdc7bbd5 XL |
1672 | .into_string() |
1673 | ); | |
1674 | } | |
1675 | } | |
17df50a5 | 1676 | if !default_impl_items.is_empty() || !impl_items.is_empty() { |
cdc7bbd5 | 1677 | w.write_str("<div class=\"impl-items\">"); |
17df50a5 | 1678 | w.push_buffer(default_impl_items); |
cdc7bbd5 XL |
1679 | w.push_buffer(impl_items); |
1680 | close_tags.insert_str(0, "</div>"); | |
1681 | } | |
1682 | w.write_str(&close_tags); | |
1a4d82fc JJ |
1683 | } |
1684 | ||
136023e0 | 1685 | // Render the items that appear on the right side of methods, impls, and |
5099ac24 | 1686 | // associated types. For example "1.0.0 (const: 1.39.0) · source". |
136023e0 XL |
1687 | fn render_rightside( |
1688 | w: &mut Buffer, | |
1689 | cx: &Context<'_>, | |
1690 | item: &clean::Item, | |
1691 | containing_item: &clean::Item, | |
a2a8927a | 1692 | render_mode: RenderMode, |
136023e0 XL |
1693 | ) { |
1694 | let tcx = cx.tcx(); | |
1695 | ||
a2a8927a XL |
1696 | // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove |
1697 | // this condition. | |
1698 | let (const_stability, const_stable_since) = match render_mode { | |
1699 | RenderMode::Normal => (item.const_stability(tcx), containing_item.const_stable_since(tcx)), | |
1700 | RenderMode::ForDeref { .. } => (None, None), | |
1701 | }; | |
f2b60f7d FG |
1702 | let src_href = cx.src_href(item); |
1703 | let has_src_ref = src_href.is_some(); | |
a2a8927a | 1704 | |
5099ac24 | 1705 | let mut rightside = Buffer::new(); |
f2b60f7d | 1706 | let has_stability = render_stability_since_raw_with_extra( |
5099ac24 | 1707 | &mut rightside, |
a2a8927a XL |
1708 | item.stable_since(tcx), |
1709 | const_stability, | |
1710 | containing_item.stable_since(tcx), | |
1711 | const_stable_since, | |
f2b60f7d | 1712 | if has_src_ref { "" } else { " rightside" }, |
136023e0 | 1713 | ); |
f2b60f7d FG |
1714 | if let Some(l) = src_href { |
1715 | if has_stability { | |
1716 | write!(rightside, " · <a class=\"srclink\" href=\"{}\">source</a>", l) | |
1717 | } else { | |
1718 | write!(rightside, "<a class=\"srclink rightside\" href=\"{}\">source</a>", l) | |
1719 | } | |
5099ac24 | 1720 | } |
f2b60f7d | 1721 | if has_stability && has_src_ref { |
5099ac24 | 1722 | write!(w, "<span class=\"rightside\">{}</span>", rightside.into_inner()); |
f2b60f7d FG |
1723 | } else { |
1724 | w.push_buffer(rightside); | |
5099ac24 | 1725 | } |
136023e0 XL |
1726 | } |
1727 | ||
1728 | pub(crate) fn render_impl_summary( | |
17df50a5 | 1729 | w: &mut Buffer, |
923072b8 | 1730 | cx: &mut Context<'_>, |
17df50a5 | 1731 | i: &Impl, |
136023e0 XL |
1732 | parent: &clean::Item, |
1733 | containing_item: &clean::Item, | |
17df50a5 XL |
1734 | show_def_docs: bool, |
1735 | use_absolute: Option<bool>, | |
17df50a5 XL |
1736 | // This argument is used to reference same type with different paths to avoid duplication |
1737 | // in documentation pages for trait with automatic implementations like "Send" and "Sync". | |
1738 | aliases: &[String], | |
1739 | ) { | |
f2b60f7d FG |
1740 | let inner_impl = i.inner_impl(); |
1741 | let id = cx.derive_id(get_id_for_impl(&inner_impl.for_, inner_impl.trait_.as_ref(), cx)); | |
17df50a5 XL |
1742 | let aliases = if aliases.is_empty() { |
1743 | String::new() | |
1744 | } else { | |
1745 | format!(" data-aliases=\"{}\"", aliases.join(",")) | |
1746 | }; | |
5099ac24 | 1747 | write!(w, "<section id=\"{}\" class=\"impl has-srclink\"{}>", id, aliases); |
a2a8927a | 1748 | render_rightside(w, cx, &i.impl_item, containing_item, RenderMode::Normal); |
136023e0 XL |
1749 | write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id); |
1750 | write!(w, "<h3 class=\"code-header in-band\">"); | |
1751 | ||
17df50a5 | 1752 | if let Some(use_absolute) = use_absolute { |
f2b60f7d | 1753 | write!(w, "{}", inner_impl.print(use_absolute, cx)); |
17df50a5 | 1754 | if show_def_docs { |
f2b60f7d | 1755 | for it in &inner_impl.items { |
04454e1e | 1756 | if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind { |
17df50a5 | 1757 | w.write_str("<span class=\"where fmt-newline\"> "); |
5e7ed085 FG |
1758 | assoc_type( |
1759 | w, | |
1760 | it, | |
1761 | &tydef.generics, | |
04454e1e | 1762 | &[], // intentionally leaving out bounds |
5e7ed085 FG |
1763 | Some(&tydef.type_), |
1764 | AssocItemLink::Anchor(None), | |
1765 | 0, | |
1766 | cx, | |
1767 | ); | |
17df50a5 XL |
1768 | w.write_str(";</span>"); |
1769 | } | |
1770 | } | |
1771 | } | |
17df50a5 | 1772 | } else { |
f2b60f7d | 1773 | write!(w, "{}", inner_impl.print(false, cx)); |
17df50a5 | 1774 | } |
136023e0 XL |
1775 | write!(w, "</h3>"); |
1776 | ||
f2b60f7d | 1777 | let is_trait = inner_impl.trait_.is_some(); |
136023e0 XL |
1778 | if is_trait { |
1779 | if let Some(portability) = portability(&i.impl_item, Some(parent)) { | |
5099ac24 | 1780 | write!(w, "<span class=\"item-info\">{}</span>", portability); |
136023e0 XL |
1781 | } |
1782 | } | |
1783 | ||
5099ac24 | 1784 | w.write_str("</section>"); |
17df50a5 XL |
1785 | } |
1786 | ||
5869c6ff | 1787 | fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { |
dfeec247 XL |
1788 | if it.is_struct() |
1789 | || it.is_trait() | |
1790 | || it.is_primitive() | |
1791 | || it.is_union() | |
1792 | || it.is_enum() | |
1793 | || it.is_mod() | |
1794 | || it.is_typedef() | |
1795 | { | |
1796 | write!( | |
1797 | buffer, | |
5099ac24 | 1798 | "<h2 class=\"location\"><a href=\"#\">{}{}</a></h2>", |
5869c6ff | 1799 | match *it.kind { |
dfeec247 XL |
1800 | clean::ModuleItem(..) => |
1801 | if it.is_crate() { | |
1802 | "Crate " | |
1803 | } else { | |
1804 | "Module " | |
1805 | }, | |
e1599b0c XL |
1806 | _ => "", |
1807 | }, | |
dfeec247 XL |
1808 | it.name.as_ref().unwrap() |
1809 | ); | |
e1599b0c XL |
1810 | } |
1811 | ||
5099ac24 | 1812 | buffer.write_str("<div class=\"sidebar-elems\">"); |
e1599b0c | 1813 | if it.is_crate() { |
5099ac24 | 1814 | write!(buffer, "<div class=\"block\"><ul>"); |
94222f64 | 1815 | if let Some(ref version) = cx.cache().crate_version { |
5099ac24 | 1816 | write!(buffer, "<li class=\"version\">Version {}</li>", Escape(version)); |
e1599b0c | 1817 | } |
5099ac24 | 1818 | write!(buffer, "<li><a id=\"all-types\" href=\"all.html\">All Items</a></li>"); |
923072b8 | 1819 | buffer.write_str("</ul></div>"); |
e1599b0c | 1820 | } |
17df50a5 | 1821 | |
5869c6ff XL |
1822 | match *it.kind { |
1823 | clean::StructItem(ref s) => sidebar_struct(cx, buffer, it, s), | |
1824 | clean::TraitItem(ref t) => sidebar_trait(cx, buffer, it, t), | |
1825 | clean::PrimitiveItem(_) => sidebar_primitive(cx, buffer, it), | |
1826 | clean::UnionItem(ref u) => sidebar_union(cx, buffer, it, u), | |
1827 | clean::EnumItem(ref e) => sidebar_enum(cx, buffer, it, e), | |
04454e1e | 1828 | clean::TypedefItem(_) => sidebar_typedef(cx, buffer, it), |
e74abb32 | 1829 | clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items), |
5869c6ff | 1830 | clean::ForeignTypeItem => sidebar_foreign_type(cx, buffer, it), |
17df50a5 | 1831 | _ => {} |
e1599b0c XL |
1832 | } |
1833 | ||
1834 | // The sidebar is designed to display sibling functions, modules and | |
1835 | // other miscellaneous information. since there are lots of sibling | |
1836 | // items (and that causes quadratic growth in large modules), | |
1837 | // we refactor common parts into a shared JavaScript file per module. | |
1838 | // still, we don't move everything into JS because we want to preserve | |
1839 | // as much HTML as possible in order to allow non-JS-enabled browsers | |
1840 | // to navigate the documentation (though slightly inefficiently). | |
1841 | ||
17df50a5 | 1842 | if !it.is_mod() { |
5099ac24 FG |
1843 | let path: String = cx.current.iter().map(|s| s.as_str()).intersperse("::").collect(); |
1844 | ||
1845 | write!(buffer, "<h2 class=\"location\"><a href=\"index.html\">In {}</a></h2>", path); | |
e1599b0c | 1846 | } |
e1599b0c | 1847 | |
e1599b0c | 1848 | // Closes sidebar-elems div. |
5869c6ff | 1849 | buffer.write_str("</div>"); |
1a4d82fc JJ |
1850 | } |
1851 | ||
a1dfa0c6 XL |
1852 | fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String { |
1853 | if used_links.insert(url.clone()) { | |
1854 | return url; | |
1855 | } | |
1856 | let mut add = 1; | |
74b04a01 | 1857 | while !used_links.insert(format!("{}-{}", url, add)) { |
a1dfa0c6 XL |
1858 | add += 1; |
1859 | } | |
1860 | format!("{}-{}", url, add) | |
1861 | } | |
1862 | ||
c295e0f8 XL |
1863 | struct SidebarLink { |
1864 | name: Symbol, | |
1865 | url: String, | |
1866 | } | |
1867 | ||
1868 | impl fmt::Display for SidebarLink { | |
1869 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
1870 | write!(f, "<a href=\"#{}\">{}</a>", self.url, self.name) | |
1871 | } | |
1872 | } | |
1873 | ||
1874 | impl PartialEq for SidebarLink { | |
1875 | fn eq(&self, other: &Self) -> bool { | |
1876 | self.url == other.url | |
1877 | } | |
1878 | } | |
1879 | ||
1880 | impl Eq for SidebarLink {} | |
1881 | ||
1882 | impl PartialOrd for SidebarLink { | |
1883 | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { | |
1884 | Some(self.cmp(other)) | |
1885 | } | |
1886 | } | |
1887 | ||
1888 | impl Ord for SidebarLink { | |
1889 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { | |
1890 | self.url.cmp(&other.url) | |
1891 | } | |
1892 | } | |
1893 | ||
a1dfa0c6 XL |
1894 | fn get_methods( |
1895 | i: &clean::Impl, | |
1896 | for_deref: bool, | |
1897 | used_links: &mut FxHashSet<String>, | |
416331ca | 1898 | deref_mut: bool, |
c295e0f8 XL |
1899 | tcx: TyCtxt<'_>, |
1900 | ) -> Vec<SidebarLink> { | |
dfeec247 XL |
1901 | i.items |
1902 | .iter() | |
1903 | .filter_map(|item| match item.name { | |
c295e0f8 XL |
1904 | Some(name) if !name.is_empty() && item.is_method() => { |
1905 | if !for_deref || should_render_item(item, deref_mut, tcx) { | |
1906 | Some(SidebarLink { | |
1907 | name, | |
04454e1e | 1908 | url: get_next_url(used_links, format!("{}.{}", ItemType::Method, name)), |
c295e0f8 | 1909 | }) |
abe05a73 XL |
1910 | } else { |
1911 | None | |
1912 | } | |
1913 | } | |
1914 | _ => None, | |
dfeec247 XL |
1915 | }) |
1916 | .collect::<Vec<_>>() | |
abe05a73 XL |
1917 | } |
1918 | ||
c295e0f8 XL |
1919 | fn get_associated_constants( |
1920 | i: &clean::Impl, | |
1921 | used_links: &mut FxHashSet<String>, | |
1922 | ) -> Vec<SidebarLink> { | |
1923 | i.items | |
1924 | .iter() | |
1925 | .filter_map(|item| match item.name { | |
1926 | Some(name) if !name.is_empty() && item.is_associated_const() => Some(SidebarLink { | |
1927 | name, | |
04454e1e | 1928 | url: get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)), |
c295e0f8 XL |
1929 | }), |
1930 | _ => None, | |
1931 | }) | |
1932 | .collect::<Vec<_>>() | |
1933 | } | |
1934 | ||
abe05a73 | 1935 | // The point is to url encode any potential character from a type with genericity. |
5869c6ff XL |
1936 | fn small_url_encode(s: String) -> String { |
1937 | let mut st = String::new(); | |
1938 | let mut last_match = 0; | |
1939 | for (idx, c) in s.char_indices() { | |
1940 | let escaped = match c { | |
1941 | '<' => "%3C", | |
1942 | '>' => "%3E", | |
1943 | ' ' => "%20", | |
1944 | '?' => "%3F", | |
1945 | '\'' => "%27", | |
1946 | '&' => "%26", | |
1947 | ',' => "%2C", | |
1948 | ':' => "%3A", | |
1949 | ';' => "%3B", | |
1950 | '[' => "%5B", | |
1951 | ']' => "%5D", | |
1952 | '"' => "%22", | |
1953 | _ => continue, | |
1954 | }; | |
1955 | ||
1956 | st += &s[last_match..idx]; | |
1957 | st += escaped; | |
1958 | // NOTE: we only expect single byte characters here - which is fine as long as we | |
1959 | // only match single byte characters | |
1960 | last_match = idx + 1; | |
1961 | } | |
1962 | ||
1963 | if last_match != 0 { | |
1964 | st += &s[last_match..]; | |
1965 | st | |
1966 | } else { | |
1967 | s | |
1968 | } | |
abe05a73 XL |
1969 | } |
1970 | ||
f2b60f7d FG |
1971 | pub(crate) fn sidebar_render_assoc_items( |
1972 | cx: &Context<'_>, | |
1973 | out: &mut Buffer, | |
1974 | id_map: &mut IdMap, | |
1975 | concrete: Vec<&Impl>, | |
1976 | synthetic: Vec<&Impl>, | |
1977 | blanket_impl: Vec<&Impl>, | |
1978 | ) { | |
1979 | let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| { | |
1980 | let mut links = FxHashSet::default(); | |
1981 | ||
1982 | let mut ret = impls | |
1983 | .iter() | |
1984 | .filter_map(|it| { | |
1985 | let trait_ = it.inner_impl().trait_.as_ref()?; | |
1986 | let encoded = | |
1987 | id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx)); | |
1988 | ||
1989 | let i_display = format!("{:#}", trait_.print(cx)); | |
1990 | let out = Escape(&i_display); | |
1991 | let prefix = match it.inner_impl().polarity { | |
1992 | ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "", | |
1993 | ty::ImplPolarity::Negative => "!", | |
1994 | }; | |
1995 | let generated = format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out); | |
1996 | if links.insert(generated.clone()) { Some(generated) } else { None } | |
1997 | }) | |
1998 | .collect::<Vec<String>>(); | |
1999 | ret.sort(); | |
2000 | ret | |
2001 | }; | |
2002 | ||
2003 | let concrete_format = format_impls(concrete, id_map); | |
2004 | let synthetic_format = format_impls(synthetic, id_map); | |
2005 | let blanket_format = format_impls(blanket_impl, id_map); | |
2006 | ||
2007 | if !concrete_format.is_empty() { | |
2008 | print_sidebar_block( | |
2009 | out, | |
2010 | "trait-implementations", | |
2011 | "Trait Implementations", | |
2012 | concrete_format.iter(), | |
2013 | ); | |
2014 | } | |
2015 | ||
2016 | if !synthetic_format.is_empty() { | |
2017 | print_sidebar_block( | |
2018 | out, | |
2019 | "synthetic-implementations", | |
2020 | "Auto Trait Implementations", | |
2021 | synthetic_format.iter(), | |
2022 | ); | |
2023 | } | |
2024 | ||
2025 | if !blanket_format.is_empty() { | |
2026 | print_sidebar_block( | |
2027 | out, | |
2028 | "blanket-implementations", | |
2029 | "Blanket Implementations", | |
2030 | blanket_format.iter(), | |
2031 | ); | |
2032 | } | |
2033 | } | |
2034 | ||
5869c6ff | 2035 | fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { |
04454e1e | 2036 | let did = it.item_id.expect_def_id(); |
94222f64 | 2037 | let cache = cx.cache(); |
5e7ed085 | 2038 | |
94222f64 | 2039 | if let Some(v) = cache.impls.get(&did) { |
a1dfa0c6 | 2040 | let mut used_links = FxHashSet::default(); |
5e7ed085 | 2041 | let mut id_map = IdMap::new(); |
a1dfa0c6 XL |
2042 | |
2043 | { | |
e74abb32 | 2044 | let used_links_bor = &mut used_links; |
c295e0f8 XL |
2045 | let mut assoc_consts = v |
2046 | .iter() | |
5e7ed085 | 2047 | .filter(|i| i.inner_impl().trait_.is_none()) |
c295e0f8 XL |
2048 | .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor)) |
2049 | .collect::<Vec<_>>(); | |
2050 | if !assoc_consts.is_empty() { | |
2051 | // We want links' order to be reproducible so we don't use unstable sort. | |
2052 | assoc_consts.sort(); | |
2053 | ||
5099ac24 FG |
2054 | print_sidebar_block( |
2055 | out, | |
2056 | "implementations", | |
2057 | "Associated Constants", | |
2058 | assoc_consts.iter(), | |
c295e0f8 | 2059 | ); |
c295e0f8 XL |
2060 | } |
2061 | let mut methods = v | |
dfeec247 XL |
2062 | .iter() |
2063 | .filter(|i| i.inner_impl().trait_.is_none()) | |
c295e0f8 | 2064 | .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx())) |
dfeec247 | 2065 | .collect::<Vec<_>>(); |
c295e0f8 | 2066 | if !methods.is_empty() { |
f9f354fc | 2067 | // We want links' order to be reproducible so we don't use unstable sort. |
c295e0f8 | 2068 | methods.sort(); |
5869c6ff | 2069 | |
5099ac24 | 2070 | print_sidebar_block(out, "implementations", "Methods", methods.iter()); |
a1dfa0c6 | 2071 | } |
cc61c64b XL |
2072 | } |
2073 | ||
2074 | if v.iter().any(|i| i.inner_impl().trait_.is_some()) { | |
c295e0f8 XL |
2075 | if let Some(impl_) = |
2076 | v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait()) | |
17df50a5 | 2077 | { |
3c0e092e XL |
2078 | let mut derefs = FxHashSet::default(); |
2079 | derefs.insert(did); | |
f2b60f7d | 2080 | sidebar_deref_methods(cx, out, impl_, v, &mut derefs, &mut used_links); |
17df50a5 XL |
2081 | } |
2082 | ||
dfeec247 | 2083 | let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = |
3c0e092e XL |
2084 | v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto()); |
2085 | let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) = | |
2086 | concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket()); | |
0531ce1d | 2087 | |
f2b60f7d | 2088 | sidebar_render_assoc_items(cx, out, &mut id_map, concrete, synthetic, blanket_impl); |
cc61c64b XL |
2089 | } |
2090 | } | |
5869c6ff | 2091 | } |
cc61c64b | 2092 | |
3c0e092e XL |
2093 | fn sidebar_deref_methods( |
2094 | cx: &Context<'_>, | |
2095 | out: &mut Buffer, | |
2096 | impl_: &Impl, | |
2097 | v: &[Impl], | |
2098 | derefs: &mut FxHashSet<DefId>, | |
f2b60f7d | 2099 | used_links: &mut FxHashSet<String>, |
3c0e092e | 2100 | ) { |
5869c6ff XL |
2101 | let c = cx.cache(); |
2102 | ||
2103 | debug!("found Deref: {:?}", impl_); | |
2104 | if let Some((target, real_target)) = | |
2105 | impl_.inner_impl().items.iter().find_map(|item| match *item.kind { | |
064997fb | 2106 | clean::AssocTypeItem(box ref t, _) => Some(match *t { |
5869c6ff XL |
2107 | clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), |
2108 | _ => (&t.type_, &t.type_), | |
2109 | }), | |
2110 | _ => None, | |
2111 | }) | |
2112 | { | |
2113 | debug!("found target, real_target: {:?} {:?}", target, real_target); | |
3c0e092e XL |
2114 | if let Some(did) = target.def_id(c) { |
2115 | if let Some(type_did) = impl_.inner_impl().for_.def_id(c) { | |
5869c6ff | 2116 | // `impl Deref<Target = S> for S` |
3c0e092e | 2117 | if did == type_did || !derefs.insert(did) { |
5869c6ff XL |
2118 | // Avoid infinite cycles |
2119 | return; | |
2120 | } | |
2121 | } | |
2122 | } | |
c295e0f8 | 2123 | let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait()); |
5869c6ff | 2124 | let inner_impl = target |
3c0e092e | 2125 | .def_id(c) |
5869c6ff XL |
2126 | .or_else(|| { |
2127 | target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned()) | |
2128 | }) | |
2129 | .and_then(|did| c.impls.get(&did)); | |
2130 | if let Some(impls) = inner_impl { | |
2131 | debug!("found inner_impl: {:?}", impls); | |
5869c6ff XL |
2132 | let mut ret = impls |
2133 | .iter() | |
2134 | .filter(|i| i.inner_impl().trait_.is_none()) | |
f2b60f7d | 2135 | .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx())) |
5869c6ff XL |
2136 | .collect::<Vec<_>>(); |
2137 | if !ret.is_empty() { | |
3c0e092e | 2138 | let id = if let Some(target_def_id) = real_target.def_id(c) { |
923072b8 | 2139 | cx.deref_id_map.get(&target_def_id).expect("Deref section without derived id") |
3c0e092e XL |
2140 | } else { |
2141 | "deref-methods" | |
2142 | }; | |
5099ac24 FG |
2143 | let title = format!( |
2144 | "Methods from {}<Target={}>", | |
cdc7bbd5 XL |
2145 | Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))), |
2146 | Escape(&format!("{:#}", real_target.print(cx))), | |
5869c6ff XL |
2147 | ); |
2148 | // We want links' order to be reproducible so we don't use unstable sort. | |
2149 | ret.sort(); | |
5099ac24 | 2150 | print_sidebar_block(out, id, &title, ret.iter()); |
5869c6ff XL |
2151 | } |
2152 | } | |
3c0e092e XL |
2153 | |
2154 | // Recurse into any further impls that might exist for `target` | |
2155 | if let Some(target_did) = target.def_id(c) { | |
2156 | if let Some(target_impls) = c.impls.get(&target_did) { | |
2157 | if let Some(target_deref_impl) = target_impls.iter().find(|i| { | |
2158 | i.inner_impl() | |
2159 | .trait_ | |
2160 | .as_ref() | |
2161 | .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait()) | |
2162 | .unwrap_or(false) | |
2163 | }) { | |
f2b60f7d FG |
2164 | sidebar_deref_methods( |
2165 | cx, | |
2166 | out, | |
2167 | target_deref_impl, | |
2168 | target_impls, | |
2169 | derefs, | |
2170 | used_links, | |
2171 | ); | |
3c0e092e XL |
2172 | } |
2173 | } | |
2174 | } | |
5869c6ff | 2175 | } |
cc61c64b XL |
2176 | } |
2177 | ||
5869c6ff XL |
2178 | fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) { |
2179 | let mut sidebar = Buffer::new(); | |
abe05a73 | 2180 | let fields = get_struct_fields_name(&s.fields); |
cc61c64b | 2181 | |
abe05a73 | 2182 | if !fields.is_empty() { |
5099ac24 FG |
2183 | match s.struct_type { |
2184 | CtorKind::Fictive => { | |
2185 | print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter()); | |
5869c6ff | 2186 | } |
5099ac24 FG |
2187 | CtorKind::Fn => print_sidebar_title(&mut sidebar, "fields", "Tuple Fields"), |
2188 | CtorKind::Const => {} | |
cc61c64b XL |
2189 | } |
2190 | } | |
2191 | ||
5869c6ff | 2192 | sidebar_assoc_items(cx, &mut sidebar, it); |
cc61c64b XL |
2193 | |
2194 | if !sidebar.is_empty() { | |
5099ac24 | 2195 | write!(buf, "<section>{}</section>", sidebar.into_inner()); |
cc61c64b | 2196 | } |
cc61c64b XL |
2197 | } |
2198 | ||
064997fb FG |
2199 | fn get_id_for_impl(for_: &clean::Type, trait_: Option<&clean::Path>, cx: &Context<'_>) -> String { |
2200 | match trait_ { | |
2201 | Some(t) => small_url_encode(format!("impl-{:#}-for-{:#}", t.print(cx), for_.print(cx))), | |
2202 | None => small_url_encode(format!("impl-{:#}", for_.print(cx))), | |
2203 | } | |
48663c56 XL |
2204 | } |
2205 | ||
cdc7bbd5 | 2206 | fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> { |
5869c6ff | 2207 | match *item.kind { |
fc512014 | 2208 | clean::ItemKind::ImplItem(ref i) => { |
3c0e092e | 2209 | i.trait_.as_ref().map(|trait_| { |
cdc7bbd5 XL |
2210 | // Alternative format produces no URLs, |
2211 | // so this parameter does nothing. | |
064997fb | 2212 | (format!("{:#}", i.for_.print(cx)), get_id_for_impl(&i.for_, Some(trait_), cx)) |
3c0e092e | 2213 | }) |
dfeec247 | 2214 | } |
abe05a73 XL |
2215 | _ => None, |
2216 | } | |
2217 | } | |
2218 | ||
5099ac24 FG |
2219 | /// Don't call this function directly!!! Use `print_sidebar_title` or `print_sidebar_block` instead! |
2220 | fn print_sidebar_title_inner(buf: &mut Buffer, id: &str, title: &str) { | |
2221 | write!( | |
2222 | buf, | |
2223 | "<h3 class=\"sidebar-title\">\ | |
2224 | <a href=\"#{}\">{}</a>\ | |
2225 | </h3>", | |
2226 | id, title | |
2227 | ); | |
2228 | } | |
2229 | ||
2230 | fn print_sidebar_title(buf: &mut Buffer, id: &str, title: &str) { | |
2231 | buf.push_str("<div class=\"block\">"); | |
2232 | print_sidebar_title_inner(buf, id, title); | |
2233 | buf.push_str("</div>"); | |
2234 | } | |
2235 | ||
2236 | fn print_sidebar_block( | |
2237 | buf: &mut Buffer, | |
2238 | id: &str, | |
2239 | title: &str, | |
2240 | items: impl Iterator<Item = impl fmt::Display>, | |
2241 | ) { | |
2242 | buf.push_str("<div class=\"block\">"); | |
2243 | print_sidebar_title_inner(buf, id, title); | |
2244 | buf.push_str("<ul>"); | |
2245 | for item in items { | |
2246 | write!(buf, "<li>{}</li>", item); | |
2247 | } | |
2248 | buf.push_str("</ul></div>"); | |
2249 | } | |
2250 | ||
5869c6ff | 2251 | fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { |
5099ac24 | 2252 | buf.write_str("<section>"); |
ff7c6d11 | 2253 | |
5869c6ff XL |
2254 | fn print_sidebar_section( |
2255 | out: &mut Buffer, | |
2256 | items: &[clean::Item], | |
5099ac24 FG |
2257 | id: &str, |
2258 | title: &str, | |
5869c6ff | 2259 | filter: impl Fn(&clean::Item) -> bool, |
5099ac24 | 2260 | mapper: impl Fn(&str) -> String, |
5869c6ff | 2261 | ) { |
5099ac24 | 2262 | let mut items: Vec<&str> = items |
5869c6ff XL |
2263 | .iter() |
2264 | .filter_map(|m| match m.name { | |
cdc7bbd5 | 2265 | Some(ref name) if filter(m) => Some(name.as_str()), |
5869c6ff XL |
2266 | _ => None, |
2267 | }) | |
2268 | .collect::<Vec<_>>(); | |
cc61c64b | 2269 | |
5869c6ff | 2270 | if !items.is_empty() { |
cdc7bbd5 | 2271 | items.sort_unstable(); |
5099ac24 | 2272 | print_sidebar_block(out, id, title, items.into_iter().map(mapper)); |
5869c6ff | 2273 | } |
cc61c64b XL |
2274 | } |
2275 | ||
5869c6ff XL |
2276 | print_sidebar_section( |
2277 | buf, | |
2278 | &t.items, | |
04454e1e FG |
2279 | "required-associated-types", |
2280 | "Required Associated Types", | |
2281 | |m| m.is_ty_associated_type(), | |
2282 | |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocType), | |
2283 | ); | |
2284 | ||
2285 | print_sidebar_section( | |
2286 | buf, | |
2287 | &t.items, | |
2288 | "provided-associated-types", | |
2289 | "Provided Associated Types", | |
5869c6ff | 2290 | |m| m.is_associated_type(), |
04454e1e FG |
2291 | |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocType), |
2292 | ); | |
2293 | ||
2294 | print_sidebar_section( | |
2295 | buf, | |
2296 | &t.items, | |
2297 | "required-associated-consts", | |
2298 | "Required Associated Constants", | |
2299 | |m| m.is_ty_associated_const(), | |
2300 | |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocConst), | |
5869c6ff | 2301 | ); |
ea8adc8c | 2302 | |
5869c6ff XL |
2303 | print_sidebar_section( |
2304 | buf, | |
2305 | &t.items, | |
04454e1e FG |
2306 | "provided-associated-consts", |
2307 | "Provided Associated Constants", | |
5869c6ff | 2308 | |m| m.is_associated_const(), |
04454e1e | 2309 | |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocConst), |
5869c6ff XL |
2310 | ); |
2311 | ||
2312 | print_sidebar_section( | |
2313 | buf, | |
2314 | &t.items, | |
5099ac24 FG |
2315 | "required-methods", |
2316 | "Required Methods", | |
5869c6ff | 2317 | |m| m.is_ty_method(), |
04454e1e | 2318 | |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::TyMethod), |
5869c6ff XL |
2319 | ); |
2320 | ||
2321 | print_sidebar_section( | |
2322 | buf, | |
2323 | &t.items, | |
5099ac24 FG |
2324 | "provided-methods", |
2325 | "Provided Methods", | |
5869c6ff | 2326 | |m| m.is_method(), |
04454e1e | 2327 | |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::Method), |
5869c6ff XL |
2328 | ); |
2329 | ||
923072b8 | 2330 | if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) { |
dfeec247 XL |
2331 | let mut res = implementors |
2332 | .iter() | |
923072b8 | 2333 | .filter(|i| !i.is_on_local_type(cx)) |
cdc7bbd5 | 2334 | .filter_map(|i| extract_for_impl_name(&i.impl_item, cx)) |
f9f354fc XL |
2335 | .collect::<Vec<_>>(); |
2336 | ||
abe05a73 | 2337 | if !res.is_empty() { |
9fa01778 | 2338 | res.sort(); |
5099ac24 FG |
2339 | print_sidebar_block( |
2340 | buf, | |
2341 | "foreign-impls", | |
2342 | "Implementations on Foreign Types", | |
923072b8 | 2343 | res.iter().map(|(name, id)| format!("<a href=\"#{}\">{}</a>", id, Escape(name))), |
5869c6ff | 2344 | ); |
abe05a73 XL |
2345 | } |
2346 | } | |
2347 | ||
5869c6ff | 2348 | sidebar_assoc_items(cx, buf, it); |
f035d41b | 2349 | |
5099ac24 | 2350 | print_sidebar_title(buf, "implementors", "Implementors"); |
064997fb | 2351 | if t.is_auto(cx.tcx()) { |
5099ac24 | 2352 | print_sidebar_title(buf, "synthetic-implementors", "Auto Implementors"); |
0531ce1d | 2353 | } |
ea8adc8c | 2354 | |
5099ac24 | 2355 | buf.push_str("</section>") |
cc61c64b XL |
2356 | } |
2357 | ||
f2b60f7d FG |
2358 | /// Returns the list of implementations for the primitive reference type, filtering out any |
2359 | /// implementations that are on concrete or partially generic types, only keeping implementations | |
2360 | /// of the form `impl<T> Trait for &T`. | |
2361 | pub(crate) fn get_filtered_impls_for_reference<'a>( | |
2362 | shared: &'a Rc<SharedContext<'_>>, | |
2363 | it: &clean::Item, | |
2364 | ) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) { | |
2365 | let def_id = it.item_id.expect_def_id(); | |
2366 | // If the reference primitive is somehow not defined, exit early. | |
2367 | let Some(v) = shared.cache.impls.get(&def_id) else { return (Vec::new(), Vec::new(), Vec::new()) }; | |
2368 | // Since there is no "direct implementation" on the reference primitive type, we filter out | |
2369 | // every implementation which isn't a trait implementation. | |
2370 | let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some()); | |
2371 | let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = | |
2372 | traits.partition(|t| t.inner_impl().kind.is_auto()); | |
2373 | ||
2374 | let (blanket_impl, concrete): (Vec<&Impl>, _) = | |
2375 | concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket()); | |
2376 | // Now we keep only references over full generic types. | |
2377 | let concrete: Vec<_> = concrete | |
2378 | .into_iter() | |
2379 | .filter(|t| match t.inner_impl().for_ { | |
2380 | clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(), | |
2381 | _ => false, | |
2382 | }) | |
2383 | .collect(); | |
2384 | ||
2385 | (concrete, synthetic, blanket_impl) | |
2386 | } | |
2387 | ||
5869c6ff XL |
2388 | fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { |
2389 | let mut sidebar = Buffer::new(); | |
f2b60f7d FG |
2390 | |
2391 | if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { | |
2392 | sidebar_assoc_items(cx, &mut sidebar, it); | |
2393 | } else { | |
2394 | let shared = Rc::clone(&cx.shared); | |
2395 | let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it); | |
2396 | ||
2397 | sidebar_render_assoc_items( | |
2398 | cx, | |
2399 | &mut sidebar, | |
2400 | &mut IdMap::new(), | |
2401 | concrete, | |
2402 | synthetic, | |
2403 | blanket_impl, | |
2404 | ); | |
2405 | } | |
cc61c64b XL |
2406 | |
2407 | if !sidebar.is_empty() { | |
5099ac24 | 2408 | write!(buf, "<section>{}</section>", sidebar.into_inner()); |
cc61c64b | 2409 | } |
cc61c64b XL |
2410 | } |
2411 | ||
5869c6ff XL |
2412 | fn sidebar_typedef(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { |
2413 | let mut sidebar = Buffer::new(); | |
2414 | sidebar_assoc_items(cx, &mut sidebar, it); | |
041b39d2 XL |
2415 | |
2416 | if !sidebar.is_empty() { | |
5099ac24 | 2417 | write!(buf, "<section>{}</section>", sidebar.into_inner()); |
041b39d2 | 2418 | } |
041b39d2 XL |
2419 | } |
2420 | ||
5869c6ff | 2421 | fn get_struct_fields_name(fields: &[clean::Item]) -> Vec<String> { |
f9f354fc | 2422 | let mut fields = fields |
dfeec247 | 2423 | .iter() |
5869c6ff XL |
2424 | .filter(|f| matches!(*f.kind, clean::StructFieldItem(..))) |
2425 | .filter_map(|f| { | |
2426 | f.name.map(|name| format!("<a href=\"#structfield.{name}\">{name}</a>", name = name)) | |
dfeec247 | 2427 | }) |
f9f354fc XL |
2428 | .collect::<Vec<_>>(); |
2429 | fields.sort(); | |
5869c6ff | 2430 | fields |
abe05a73 XL |
2431 | } |
2432 | ||
5869c6ff XL |
2433 | fn sidebar_union(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, u: &clean::Union) { |
2434 | let mut sidebar = Buffer::new(); | |
abe05a73 | 2435 | let fields = get_struct_fields_name(&u.fields); |
cc61c64b | 2436 | |
abe05a73 | 2437 | if !fields.is_empty() { |
5099ac24 | 2438 | print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter()); |
cc61c64b XL |
2439 | } |
2440 | ||
5869c6ff | 2441 | sidebar_assoc_items(cx, &mut sidebar, it); |
cc61c64b XL |
2442 | |
2443 | if !sidebar.is_empty() { | |
5099ac24 | 2444 | write!(buf, "<section>{}</section>", sidebar.into_inner()); |
cc61c64b | 2445 | } |
cc61c64b XL |
2446 | } |
2447 | ||
5869c6ff XL |
2448 | fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) { |
2449 | let mut sidebar = Buffer::new(); | |
cc61c64b | 2450 | |
f9f354fc | 2451 | let mut variants = e |
923072b8 | 2452 | .variants() |
3c0e092e XL |
2453 | .filter_map(|v| { |
2454 | v.name | |
2455 | .as_ref() | |
2456 | .map(|name| format!("<a href=\"#variant.{name}\">{name}</a>", name = name)) | |
dfeec247 | 2457 | }) |
f9f354fc | 2458 | .collect::<Vec<_>>(); |
abe05a73 | 2459 | if !variants.is_empty() { |
f9f354fc | 2460 | variants.sort_unstable(); |
5099ac24 | 2461 | print_sidebar_block(&mut sidebar, "variants", "Variants", variants.iter()); |
cc61c64b XL |
2462 | } |
2463 | ||
5869c6ff | 2464 | sidebar_assoc_items(cx, &mut sidebar, it); |
cc61c64b XL |
2465 | |
2466 | if !sidebar.is_empty() { | |
5099ac24 FG |
2467 | write!(buf, "<section>{}</section>", sidebar.into_inner()); |
2468 | } | |
2469 | } | |
2470 | ||
2471 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | |
2472 | enum ItemSection { | |
2473 | Reexports, | |
2474 | PrimitiveTypes, | |
2475 | Modules, | |
2476 | Macros, | |
2477 | Structs, | |
2478 | Enums, | |
2479 | Constants, | |
2480 | Statics, | |
2481 | Traits, | |
2482 | Functions, | |
2483 | TypeDefinitions, | |
2484 | Unions, | |
2485 | Implementations, | |
2486 | TypeMethods, | |
2487 | Methods, | |
2488 | StructFields, | |
2489 | Variants, | |
2490 | AssociatedTypes, | |
2491 | AssociatedConstants, | |
2492 | ForeignTypes, | |
2493 | Keywords, | |
2494 | OpaqueTypes, | |
2495 | AttributeMacros, | |
2496 | DeriveMacros, | |
2497 | TraitAliases, | |
2498 | } | |
2499 | ||
2500 | impl ItemSection { | |
2501 | const ALL: &'static [Self] = { | |
2502 | use ItemSection::*; | |
2503 | // NOTE: The order here affects the order in the UI. | |
2504 | &[ | |
2505 | Reexports, | |
2506 | PrimitiveTypes, | |
2507 | Modules, | |
2508 | Macros, | |
2509 | Structs, | |
2510 | Enums, | |
2511 | Constants, | |
2512 | Statics, | |
2513 | Traits, | |
2514 | Functions, | |
2515 | TypeDefinitions, | |
2516 | Unions, | |
2517 | Implementations, | |
2518 | TypeMethods, | |
2519 | Methods, | |
2520 | StructFields, | |
2521 | Variants, | |
2522 | AssociatedTypes, | |
2523 | AssociatedConstants, | |
2524 | ForeignTypes, | |
2525 | Keywords, | |
2526 | OpaqueTypes, | |
2527 | AttributeMacros, | |
2528 | DeriveMacros, | |
2529 | TraitAliases, | |
2530 | ] | |
2531 | }; | |
2532 | ||
2533 | fn id(self) -> &'static str { | |
2534 | match self { | |
2535 | Self::Reexports => "reexports", | |
2536 | Self::Modules => "modules", | |
2537 | Self::Structs => "structs", | |
2538 | Self::Unions => "unions", | |
2539 | Self::Enums => "enums", | |
2540 | Self::Functions => "functions", | |
2541 | Self::TypeDefinitions => "types", | |
2542 | Self::Statics => "statics", | |
2543 | Self::Constants => "constants", | |
2544 | Self::Traits => "traits", | |
2545 | Self::Implementations => "impls", | |
2546 | Self::TypeMethods => "tymethods", | |
2547 | Self::Methods => "methods", | |
2548 | Self::StructFields => "fields", | |
2549 | Self::Variants => "variants", | |
2550 | Self::Macros => "macros", | |
2551 | Self::PrimitiveTypes => "primitives", | |
2552 | Self::AssociatedTypes => "associated-types", | |
2553 | Self::AssociatedConstants => "associated-consts", | |
2554 | Self::ForeignTypes => "foreign-types", | |
2555 | Self::Keywords => "keywords", | |
2556 | Self::OpaqueTypes => "opaque-types", | |
2557 | Self::AttributeMacros => "attributes", | |
2558 | Self::DeriveMacros => "derives", | |
2559 | Self::TraitAliases => "trait-aliases", | |
2560 | } | |
2561 | } | |
2562 | ||
2563 | fn name(self) -> &'static str { | |
2564 | match self { | |
2565 | Self::Reexports => "Re-exports", | |
2566 | Self::Modules => "Modules", | |
2567 | Self::Structs => "Structs", | |
2568 | Self::Unions => "Unions", | |
2569 | Self::Enums => "Enums", | |
2570 | Self::Functions => "Functions", | |
2571 | Self::TypeDefinitions => "Type Definitions", | |
2572 | Self::Statics => "Statics", | |
2573 | Self::Constants => "Constants", | |
2574 | Self::Traits => "Traits", | |
2575 | Self::Implementations => "Implementations", | |
2576 | Self::TypeMethods => "Type Methods", | |
2577 | Self::Methods => "Methods", | |
2578 | Self::StructFields => "Struct Fields", | |
2579 | Self::Variants => "Variants", | |
2580 | Self::Macros => "Macros", | |
2581 | Self::PrimitiveTypes => "Primitive Types", | |
2582 | Self::AssociatedTypes => "Associated Types", | |
2583 | Self::AssociatedConstants => "Associated Constants", | |
2584 | Self::ForeignTypes => "Foreign Types", | |
2585 | Self::Keywords => "Keywords", | |
2586 | Self::OpaqueTypes => "Opaque Types", | |
2587 | Self::AttributeMacros => "Attribute Macros", | |
2588 | Self::DeriveMacros => "Derive Macros", | |
2589 | Self::TraitAliases => "Trait Aliases", | |
2590 | } | |
cc61c64b | 2591 | } |
cc61c64b XL |
2592 | } |
2593 | ||
5099ac24 | 2594 | fn item_ty_to_section(ty: ItemType) -> ItemSection { |
17df50a5 | 2595 | match ty { |
5099ac24 FG |
2596 | ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports, |
2597 | ItemType::Module => ItemSection::Modules, | |
2598 | ItemType::Struct => ItemSection::Structs, | |
2599 | ItemType::Union => ItemSection::Unions, | |
2600 | ItemType::Enum => ItemSection::Enums, | |
2601 | ItemType::Function => ItemSection::Functions, | |
2602 | ItemType::Typedef => ItemSection::TypeDefinitions, | |
2603 | ItemType::Static => ItemSection::Statics, | |
2604 | ItemType::Constant => ItemSection::Constants, | |
2605 | ItemType::Trait => ItemSection::Traits, | |
2606 | ItemType::Impl => ItemSection::Implementations, | |
2607 | ItemType::TyMethod => ItemSection::TypeMethods, | |
2608 | ItemType::Method => ItemSection::Methods, | |
2609 | ItemType::StructField => ItemSection::StructFields, | |
2610 | ItemType::Variant => ItemSection::Variants, | |
2611 | ItemType::Macro => ItemSection::Macros, | |
2612 | ItemType::Primitive => ItemSection::PrimitiveTypes, | |
2613 | ItemType::AssocType => ItemSection::AssociatedTypes, | |
2614 | ItemType::AssocConst => ItemSection::AssociatedConstants, | |
2615 | ItemType::ForeignType => ItemSection::ForeignTypes, | |
2616 | ItemType::Keyword => ItemSection::Keywords, | |
2617 | ItemType::OpaqueTy => ItemSection::OpaqueTypes, | |
2618 | ItemType::ProcAttribute => ItemSection::AttributeMacros, | |
2619 | ItemType::ProcDerive => ItemSection::DeriveMacros, | |
2620 | ItemType::TraitAlias => ItemSection::TraitAliases, | |
94b46f34 XL |
2621 | } |
2622 | } | |
2623 | ||
e74abb32 | 2624 | fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { |
923072b8 FG |
2625 | use std::fmt::Write as _; |
2626 | ||
cc61c64b XL |
2627 | let mut sidebar = String::new(); |
2628 | ||
5099ac24 FG |
2629 | let item_sections_in_use: FxHashSet<_> = items |
2630 | .iter() | |
04454e1e FG |
2631 | .filter(|it| { |
2632 | !it.is_stripped() | |
2633 | && it | |
2634 | .name | |
2635 | .or_else(|| { | |
2636 | if let clean::ImportItem(ref i) = *it.kind && | |
2637 | let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None } | |
2638 | }) | |
2639 | .is_some() | |
2640 | }) | |
5099ac24 FG |
2641 | .map(|it| item_ty_to_section(it.type_())) |
2642 | .collect(); | |
2643 | for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) { | |
923072b8 | 2644 | let _ = write!(sidebar, "<li><a href=\"#{}\">{}</a></li>", sec.id(), sec.name()); |
cc61c64b XL |
2645 | } |
2646 | ||
2647 | if !sidebar.is_empty() { | |
5099ac24 FG |
2648 | write!( |
2649 | buf, | |
2650 | "<section>\ | |
2651 | <div class=\"block\">\ | |
2652 | <ul>{}</ul>\ | |
2653 | </div>\ | |
2654 | </section>", | |
2655 | sidebar | |
2656 | ); | |
cc61c64b | 2657 | } |
cc61c64b XL |
2658 | } |
2659 | ||
5869c6ff XL |
2660 | fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { |
2661 | let mut sidebar = Buffer::new(); | |
2662 | sidebar_assoc_items(cx, &mut sidebar, it); | |
2663 | ||
abe05a73 | 2664 | if !sidebar.is_empty() { |
5099ac24 | 2665 | write!(buf, "<section>{}</section>", sidebar.into_inner()); |
abe05a73 | 2666 | } |
abe05a73 XL |
2667 | } |
2668 | ||
923072b8 | 2669 | pub(crate) const BASIC_KEYWORDS: &str = "rust, rustlang, rust-lang"; |
1a4d82fc | 2670 | |
0531ce1d XL |
2671 | /// Returns a list of all paths used in the type. |
2672 | /// This is used to help deduplicate imported impls | |
2673 | /// for reexported types. If any of the contained | |
2674 | /// types are re-exported, we don't use the corresponding | |
2675 | /// entry from the js file, as inlining will have already | |
2676 | /// picked up the impl | |
5869c6ff | 2677 | fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> { |
0531ce1d | 2678 | let mut out = Vec::new(); |
0bf4aa26 | 2679 | let mut visited = FxHashSet::default(); |
0531ce1d | 2680 | let mut work = VecDeque::new(); |
0531ce1d | 2681 | |
c295e0f8 XL |
2682 | let mut process_path = |did: DefId| { |
2683 | let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone()); | |
2684 | let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern); | |
2685 | ||
2686 | if let Some(path) = fqp { | |
5099ac24 | 2687 | out.push(join_with_double_colon(&path)); |
c295e0f8 XL |
2688 | } |
2689 | }; | |
2690 | ||
0531ce1d XL |
2691 | work.push_back(first_ty); |
2692 | ||
2693 | while let Some(ty) = work.pop_front() { | |
2694 | if !visited.insert(ty.clone()) { | |
2695 | continue; | |
2696 | } | |
2697 | ||
2698 | match ty { | |
3c0e092e | 2699 | clean::Type::Path { path } => process_path(path.def_id()), |
0531ce1d XL |
2700 | clean::Type::Tuple(tys) => { |
2701 | work.extend(tys.into_iter()); | |
dfeec247 | 2702 | } |
0531ce1d XL |
2703 | clean::Type::Slice(ty) => { |
2704 | work.push_back(*ty); | |
2705 | } | |
2706 | clean::Type::Array(ty, _) => { | |
2707 | work.push_back(*ty); | |
dfeec247 | 2708 | } |
0531ce1d XL |
2709 | clean::Type::RawPointer(_, ty) => { |
2710 | work.push_back(*ty); | |
dfeec247 | 2711 | } |
0531ce1d XL |
2712 | clean::Type::BorrowedRef { type_, .. } => { |
2713 | work.push_back(*type_); | |
dfeec247 | 2714 | } |
f2b60f7d FG |
2715 | clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => { |
2716 | work.push_back(self_type); | |
c295e0f8 | 2717 | process_path(trait_.def_id()); |
dfeec247 | 2718 | } |
0531ce1d XL |
2719 | _ => {} |
2720 | } | |
dfeec247 | 2721 | } |
0531ce1d XL |
2722 | out |
2723 | } | |
3c0e092e XL |
2724 | |
2725 | const MAX_FULL_EXAMPLES: usize = 5; | |
2726 | const NUM_VISIBLE_LINES: usize = 10; | |
2727 | ||
2728 | /// Generates the HTML for example call locations generated via the --scrape-examples flag. | |
923072b8 | 2729 | fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item) { |
3c0e092e | 2730 | let tcx = cx.tcx(); |
04454e1e | 2731 | let def_id = item.item_id.expect_def_id(); |
3c0e092e | 2732 | let key = tcx.def_path_hash(def_id); |
5e7ed085 | 2733 | let Some(call_locations) = cx.shared.call_locations.get(&key) else { return }; |
3c0e092e XL |
2734 | |
2735 | // Generate a unique ID so users can link to this section for a given method | |
923072b8 | 2736 | let id = cx.id_map.derive("scraped-examples"); |
3c0e092e XL |
2737 | write!( |
2738 | w, | |
2739 | "<div class=\"docblock scraped-example-list\">\ | |
2740 | <span></span>\ | |
5099ac24 | 2741 | <h5 id=\"{id}\">\ |
3c0e092e | 2742 | <a href=\"#{id}\">Examples found in repository</a>\ |
04454e1e | 2743 | <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\ |
3c0e092e | 2744 | </h5>", |
04454e1e | 2745 | root_path = cx.root_path(), |
3c0e092e XL |
2746 | id = id |
2747 | ); | |
2748 | ||
2749 | // Create a URL to a particular location in a reverse-dependency's source file | |
2750 | let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) { | |
2751 | let (line_lo, line_hi) = loc.call_expr.line_span; | |
2752 | let (anchor, title) = if line_lo == line_hi { | |
2753 | ((line_lo + 1).to_string(), format!("line {}", line_lo + 1)) | |
2754 | } else { | |
2755 | ( | |
2756 | format!("{}-{}", line_lo + 1, line_hi + 1), | |
2757 | format!("lines {}-{}", line_lo + 1, line_hi + 1), | |
2758 | ) | |
2759 | }; | |
2760 | let url = format!("{}{}#{}", cx.root_path(), call_data.url, anchor); | |
2761 | (url, title) | |
2762 | }; | |
2763 | ||
2764 | // Generate the HTML for a single example, being the title and code block | |
2765 | let write_example = |w: &mut Buffer, (path, call_data): (&PathBuf, &CallData)| -> bool { | |
2766 | let contents = match fs::read_to_string(&path) { | |
2767 | Ok(contents) => contents, | |
2768 | Err(err) => { | |
f2b60f7d | 2769 | let span = item.span(tcx).map_or(rustc_span::DUMMY_SP, |span| span.inner()); |
3c0e092e XL |
2770 | tcx.sess |
2771 | .span_err(span, &format!("failed to read file {}: {}", path.display(), err)); | |
2772 | return false; | |
2773 | } | |
2774 | }; | |
2775 | ||
2776 | // To reduce file sizes, we only want to embed the source code needed to understand the example, not | |
2777 | // the entire file. So we find the smallest byte range that covers all items enclosing examples. | |
2778 | assert!(!call_data.locations.is_empty()); | |
2779 | let min_loc = | |
2780 | call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap(); | |
2781 | let byte_min = min_loc.enclosing_item.byte_span.0; | |
2782 | let line_min = min_loc.enclosing_item.line_span.0; | |
2783 | let max_loc = | |
2784 | call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap(); | |
2785 | let byte_max = max_loc.enclosing_item.byte_span.1; | |
2786 | let line_max = max_loc.enclosing_item.line_span.1; | |
2787 | ||
2788 | // The output code is limited to that byte range. | |
2789 | let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)]; | |
2790 | ||
2791 | // The call locations need to be updated to reflect that the size of the program has changed. | |
2792 | // Specifically, the ranges are all subtracted by `byte_min` since that's the new zero point. | |
2793 | let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data | |
2794 | .locations | |
2795 | .iter() | |
2796 | .map(|loc| { | |
04454e1e | 2797 | let (byte_lo, byte_hi) = loc.call_ident.byte_span; |
3c0e092e XL |
2798 | let (line_lo, line_hi) = loc.call_expr.line_span; |
2799 | let byte_range = (byte_lo - byte_min, byte_hi - byte_min); | |
04454e1e | 2800 | |
3c0e092e XL |
2801 | let line_range = (line_lo - line_min, line_hi - line_min); |
2802 | let (line_url, line_title) = link_to_loc(call_data, loc); | |
2803 | ||
2804 | (byte_range, (line_range, line_url, line_title)) | |
2805 | }) | |
2806 | .unzip(); | |
2807 | ||
2808 | let (_, init_url, init_title) = &line_ranges[0]; | |
2809 | let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES; | |
2810 | let locations_encoded = serde_json::to_string(&line_ranges).unwrap(); | |
2811 | ||
2812 | write!( | |
2813 | w, | |
2814 | "<div class=\"scraped-example {expanded_cls}\" data-locs=\"{locations}\">\ | |
2815 | <div class=\"scraped-example-title\">\ | |
2816 | {name} (<a href=\"{url}\">{title}</a>)\ | |
2817 | </div>\ | |
2818 | <div class=\"code-wrapper\">", | |
2819 | expanded_cls = if needs_expansion { "" } else { "expanded" }, | |
2820 | name = call_data.display_name, | |
2821 | url = init_url, | |
2822 | title = init_title, | |
2823 | // The locations are encoded as a data attribute, so they can be read | |
2824 | // later by the JS for interactions. | |
2825 | locations = Escape(&locations_encoded) | |
2826 | ); | |
2827 | ||
2828 | if line_ranges.len() > 1 { | |
2829 | write!(w, r#"<span class="prev">≺</span> <span class="next">≻</span>"#); | |
2830 | } | |
2831 | ||
2832 | if needs_expansion { | |
2833 | write!(w, r#"<span class="expand">↕</span>"#); | |
2834 | } | |
2835 | ||
2836 | // Look for the example file in the source map if it exists, otherwise return a dummy span | |
2837 | let file_span = (|| { | |
2838 | let source_map = tcx.sess.source_map(); | |
2839 | let crate_src = tcx.sess.local_crate_source_file.as_ref()?; | |
2840 | let abs_crate_src = crate_src.canonicalize().ok()?; | |
2841 | let crate_root = abs_crate_src.parent()?.parent()?; | |
2842 | let rel_path = path.strip_prefix(crate_root).ok()?; | |
2843 | let files = source_map.files(); | |
2844 | let file = files.iter().find(|file| match &file.name { | |
2845 | FileName::Real(RealFileName::LocalPath(other_path)) => rel_path == other_path, | |
2846 | _ => false, | |
2847 | })?; | |
2848 | Some(rustc_span::Span::with_root_ctxt( | |
2849 | file.start_pos + BytePos(byte_min), | |
2850 | file.start_pos + BytePos(byte_max), | |
2851 | )) | |
2852 | })() | |
2853 | .unwrap_or(rustc_span::DUMMY_SP); | |
2854 | ||
2855 | // The root path is the inverse of Context::current | |
2856 | let root_path = vec!["../"; cx.current.len() - 1].join(""); | |
2857 | ||
2858 | let mut decoration_info = FxHashMap::default(); | |
2859 | decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]); | |
2860 | decoration_info.insert("highlight", byte_ranges); | |
2861 | ||
2862 | sources::print_src( | |
2863 | w, | |
2864 | contents_subset, | |
3c0e092e XL |
2865 | file_span, |
2866 | cx, | |
2867 | &root_path, | |
f2b60f7d | 2868 | highlight::DecorationInfo(decoration_info), |
3c0e092e XL |
2869 | sources::SourceContext::Embedded { offset: line_min }, |
2870 | ); | |
2871 | write!(w, "</div></div>"); | |
2872 | ||
2873 | true | |
2874 | }; | |
2875 | ||
2876 | // The call locations are output in sequence, so that sequence needs to be determined. | |
2877 | // Ideally the most "relevant" examples would be shown first, but there's no general algorithm | |
2878 | // for determining relevance. Instead, we prefer the smallest examples being likely the easiest to | |
2879 | // understand at a glance. | |
2880 | let ordered_locations = { | |
2881 | let sort_criterion = |(_, call_data): &(_, &CallData)| { | |
2882 | // Use the first location because that's what the user will see initially | |
2883 | let (lo, hi) = call_data.locations[0].enclosing_item.byte_span; | |
2884 | hi - lo | |
2885 | }; | |
2886 | ||
923072b8 | 2887 | let mut locs = call_locations.iter().collect::<Vec<_>>(); |
3c0e092e XL |
2888 | locs.sort_by_key(sort_criterion); |
2889 | locs | |
2890 | }; | |
2891 | ||
2892 | let mut it = ordered_locations.into_iter().peekable(); | |
2893 | ||
2894 | // An example may fail to write if its source can't be read for some reason, so this method | |
5e7ed085 | 2895 | // continues iterating until a write succeeds |
3c0e092e XL |
2896 | let write_and_skip_failure = |w: &mut Buffer, it: &mut Peekable<_>| { |
2897 | while let Some(example) = it.next() { | |
2898 | if write_example(&mut *w, example) { | |
2899 | break; | |
2900 | } | |
2901 | } | |
2902 | }; | |
2903 | ||
2904 | // Write just one example that's visible by default in the method's description. | |
2905 | write_and_skip_failure(w, &mut it); | |
2906 | ||
2907 | // Then add the remaining examples in a hidden section. | |
2908 | if it.peek().is_some() { | |
2909 | write!( | |
2910 | w, | |
2911 | "<details class=\"rustdoc-toggle more-examples-toggle\">\ | |
2912 | <summary class=\"hideme\">\ | |
2913 | <span>More examples</span>\ | |
2914 | </summary>\ | |
04454e1e | 2915 | <div class=\"hide-more\">Hide additional examples</div>\ |
3c0e092e XL |
2916 | <div class=\"more-scraped-examples\">\ |
2917 | <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>\ | |
2918 | <div class=\"more-scraped-examples-inner\">" | |
2919 | ); | |
2920 | ||
2921 | // Only generate inline code for MAX_FULL_EXAMPLES number of examples. Otherwise we could | |
2922 | // make the page arbitrarily huge! | |
2923 | for _ in 0..MAX_FULL_EXAMPLES { | |
2924 | write_and_skip_failure(w, &mut it); | |
2925 | } | |
2926 | ||
2927 | // For the remaining examples, generate a <ul> containing links to the source files. | |
2928 | if it.peek().is_some() { | |
2929 | write!(w, r#"<div class="example-links">Additional examples can be found in:<br><ul>"#); | |
2930 | it.for_each(|(_, call_data)| { | |
923072b8 | 2931 | let (url, _) = link_to_loc(call_data, &call_data.locations[0]); |
3c0e092e XL |
2932 | write!( |
2933 | w, | |
2934 | r#"<li><a href="{url}">{name}</a></li>"#, | |
2935 | url = url, | |
2936 | name = call_data.display_name | |
2937 | ); | |
2938 | }); | |
2939 | write!(w, "</ul></div>"); | |
2940 | } | |
2941 | ||
2942 | write!(w, "</div></div></details>"); | |
2943 | } | |
2944 | ||
2945 | write!(w, "</div>"); | |
2946 | } |