]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/render/mod.rs
29b10fb8457b0e40e5e9dad216207c031c7e76c8
[rust.git] / src / librustdoc / html / render / mod.rs
1 //! Rustdoc's HTML rendering module.
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,
10 //! and then it is shared among the various rendering threads. The cache is meant
11 //! to be a fairly large structure not implementing `Clone` (because it's shared
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
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.
23 //! These threads are not parallelized (they haven't been a bottleneck yet), and
24 //! both occur before the crate is rendered.
25
26 crate mod cache;
27
28 #[cfg(test)]
29 mod tests;
30
31 mod context;
32 mod print_item;
33 mod write_shared;
34
35 crate use context::*;
36 crate use write_shared::FILES_UNVERSIONED;
37
38 use std::collections::VecDeque;
39 use std::default::Default;
40 use std::fmt;
41 use std::path::PathBuf;
42 use std::str;
43 use std::string::ToString;
44
45 use rustc_ast_pretty::pprust;
46 use rustc_attr::{Deprecation, StabilityLevel};
47 use rustc_data_structures::fx::FxHashSet;
48 use rustc_hir as hir;
49 use rustc_hir::def::CtorKind;
50 use rustc_hir::def_id::DefId;
51 use rustc_hir::Mutability;
52 use rustc_middle::middle::stability;
53 use rustc_span::symbol::{kw, sym, Symbol};
54 use serde::ser::SerializeSeq;
55 use serde::{Serialize, Serializer};
56
57 use crate::clean::{self, FakeDefId, GetDefId, RenderedLink, SelfTy};
58 use crate::docfs::PathError;
59 use crate::error::Error;
60 use crate::formats::cache::Cache;
61 use crate::formats::item_type::ItemType;
62 use crate::formats::{AssocItemRender, Impl, RenderMode};
63 use crate::html::escape::Escape;
64 use crate::html::format::{
65     href, print_abi_with_space, print_default_space, print_generic_bounds, print_where_clause,
66     Buffer, PrintWithSpace,
67 };
68 use crate::html::markdown::{Markdown, MarkdownHtml, MarkdownSummaryLine};
69
70 /// A pair of name and its optional document.
71 crate type NameDoc = (String, Option<String>);
72
73 crate fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ {
74     crate::html::format::display_fn(move |f| {
75         if !v.ends_with('/') && !v.is_empty() { write!(f, "{}/", v) } else { f.write_str(v) }
76     })
77 }
78
79 // Helper structs for rendering items/sidebars and carrying along contextual
80 // information
81
82 /// Struct representing one entry in the JS search index. These are all emitted
83 /// by hand to a large JS file at the end of cache-creation.
84 #[derive(Debug)]
85 crate struct IndexItem {
86     crate ty: ItemType,
87     crate name: String,
88     crate path: String,
89     crate desc: String,
90     crate parent: Option<DefId>,
91     crate parent_idx: Option<usize>,
92     crate search_type: Option<IndexItemFunctionType>,
93     crate aliases: Box<[String]>,
94 }
95
96 /// A type used for the search index.
97 #[derive(Debug)]
98 crate struct RenderType {
99     ty: Option<DefId>,
100     idx: Option<usize>,
101     name: Option<String>,
102     generics: Option<Vec<Generic>>,
103 }
104
105 impl Serialize for RenderType {
106     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
107     where
108         S: Serializer,
109     {
110         if let Some(name) = &self.name {
111             let mut seq = serializer.serialize_seq(None)?;
112             if let Some(id) = self.idx {
113                 seq.serialize_element(&id)?;
114             } else {
115                 seq.serialize_element(&name)?;
116             }
117             if let Some(generics) = &self.generics {
118                 seq.serialize_element(&generics)?;
119             }
120             seq.end()
121         } else {
122             serializer.serialize_none()
123         }
124     }
125 }
126
127 /// A type used for the search index.
128 #[derive(Debug)]
129 crate struct Generic {
130     name: String,
131     defid: Option<DefId>,
132     idx: Option<usize>,
133 }
134
135 impl Serialize for Generic {
136     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
137     where
138         S: Serializer,
139     {
140         if let Some(id) = self.idx {
141             serializer.serialize_some(&id)
142         } else {
143             serializer.serialize_some(&self.name)
144         }
145     }
146 }
147
148 /// Full type of functions/methods in the search index.
149 #[derive(Debug)]
150 crate struct IndexItemFunctionType {
151     inputs: Vec<TypeWithKind>,
152     output: Option<Vec<TypeWithKind>>,
153 }
154
155 impl Serialize for IndexItemFunctionType {
156     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
157     where
158         S: Serializer,
159     {
160         // If we couldn't figure out a type, just write `null`.
161         let mut iter = self.inputs.iter();
162         if match self.output {
163             Some(ref output) => iter.chain(output.iter()).any(|ref i| i.ty.name.is_none()),
164             None => iter.any(|ref i| i.ty.name.is_none()),
165         } {
166             serializer.serialize_none()
167         } else {
168             let mut seq = serializer.serialize_seq(None)?;
169             seq.serialize_element(&self.inputs)?;
170             if let Some(output) = &self.output {
171                 if output.len() > 1 {
172                     seq.serialize_element(&output)?;
173                 } else {
174                     seq.serialize_element(&output[0])?;
175                 }
176             }
177             seq.end()
178         }
179     }
180 }
181
182 #[derive(Debug)]
183 crate struct TypeWithKind {
184     ty: RenderType,
185     kind: ItemType,
186 }
187
188 impl From<(RenderType, ItemType)> for TypeWithKind {
189     fn from(x: (RenderType, ItemType)) -> TypeWithKind {
190         TypeWithKind { ty: x.0, kind: x.1 }
191     }
192 }
193
194 impl Serialize for TypeWithKind {
195     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
196     where
197         S: Serializer,
198     {
199         (&self.ty.name, self.kind).serialize(serializer)
200     }
201 }
202
203 #[derive(Debug, Clone)]
204 crate struct StylePath {
205     /// The path to the theme
206     crate path: PathBuf,
207     /// What the `disabled` attribute should be set to in the HTML tag
208     crate disabled: bool,
209 }
210
211 fn write_srclink(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
212     if let Some(l) = cx.src_href(item) {
213         write!(buf, "<a class=\"srclink\" href=\"{}\" title=\"goto source code\">[src]</a>", l)
214     }
215 }
216
217 #[derive(Debug, Eq, PartialEq, Hash)]
218 struct ItemEntry {
219     url: String,
220     name: String,
221 }
222
223 impl ItemEntry {
224     fn new(mut url: String, name: String) -> ItemEntry {
225         while url.starts_with('/') {
226             url.remove(0);
227         }
228         ItemEntry { url, name }
229     }
230 }
231
232 impl ItemEntry {
233     crate fn print(&self) -> impl fmt::Display + '_ {
234         crate::html::format::display_fn(move |f| {
235             write!(f, "<a href=\"{}\">{}</a>", self.url, Escape(&self.name))
236         })
237     }
238 }
239
240 impl PartialOrd for ItemEntry {
241     fn partial_cmp(&self, other: &ItemEntry) -> Option<::std::cmp::Ordering> {
242         Some(self.cmp(other))
243     }
244 }
245
246 impl Ord for ItemEntry {
247     fn cmp(&self, other: &ItemEntry) -> ::std::cmp::Ordering {
248         self.name.cmp(&other.name)
249     }
250 }
251
252 #[derive(Debug)]
253 struct AllTypes {
254     structs: FxHashSet<ItemEntry>,
255     enums: FxHashSet<ItemEntry>,
256     unions: FxHashSet<ItemEntry>,
257     primitives: FxHashSet<ItemEntry>,
258     traits: FxHashSet<ItemEntry>,
259     macros: FxHashSet<ItemEntry>,
260     functions: FxHashSet<ItemEntry>,
261     typedefs: FxHashSet<ItemEntry>,
262     opaque_tys: FxHashSet<ItemEntry>,
263     statics: FxHashSet<ItemEntry>,
264     constants: FxHashSet<ItemEntry>,
265     keywords: FxHashSet<ItemEntry>,
266     attributes: FxHashSet<ItemEntry>,
267     derives: FxHashSet<ItemEntry>,
268     trait_aliases: FxHashSet<ItemEntry>,
269 }
270
271 impl AllTypes {
272     fn new() -> AllTypes {
273         let new_set = |cap| FxHashSet::with_capacity_and_hasher(cap, Default::default());
274         AllTypes {
275             structs: new_set(100),
276             enums: new_set(100),
277             unions: new_set(100),
278             primitives: new_set(26),
279             traits: new_set(100),
280             macros: new_set(100),
281             functions: new_set(100),
282             typedefs: new_set(100),
283             opaque_tys: new_set(100),
284             statics: new_set(100),
285             constants: new_set(100),
286             keywords: new_set(100),
287             attributes: new_set(100),
288             derives: new_set(100),
289             trait_aliases: new_set(100),
290         }
291     }
292
293     fn append(&mut self, item_name: String, item_type: &ItemType) {
294         let mut url: Vec<_> = item_name.split("::").skip(1).collect();
295         if let Some(name) = url.pop() {
296             let new_url = format!("{}/{}.{}.html", url.join("/"), item_type, name);
297             url.push(name);
298             let name = url.join("::");
299             match *item_type {
300                 ItemType::Struct => self.structs.insert(ItemEntry::new(new_url, name)),
301                 ItemType::Enum => self.enums.insert(ItemEntry::new(new_url, name)),
302                 ItemType::Union => self.unions.insert(ItemEntry::new(new_url, name)),
303                 ItemType::Primitive => self.primitives.insert(ItemEntry::new(new_url, name)),
304                 ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)),
305                 ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)),
306                 ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)),
307                 ItemType::Typedef => self.typedefs.insert(ItemEntry::new(new_url, name)),
308                 ItemType::OpaqueTy => self.opaque_tys.insert(ItemEntry::new(new_url, name)),
309                 ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)),
310                 ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)),
311                 ItemType::ProcAttribute => self.attributes.insert(ItemEntry::new(new_url, name)),
312                 ItemType::ProcDerive => self.derives.insert(ItemEntry::new(new_url, name)),
313                 ItemType::TraitAlias => self.trait_aliases.insert(ItemEntry::new(new_url, name)),
314                 _ => true,
315             };
316         }
317     }
318 }
319
320 impl AllTypes {
321     fn print(self, f: &mut Buffer) {
322         fn print_entries(f: &mut Buffer, e: &FxHashSet<ItemEntry>, title: &str, class: &str) {
323             if !e.is_empty() {
324                 let mut e: Vec<&ItemEntry> = e.iter().collect();
325                 e.sort();
326                 write!(f, "<h3 id=\"{}\">{}</h3><ul class=\"{} docblock\">", title, title, class);
327
328                 for s in e.iter() {
329                     write!(f, "<li>{}</li>", s.print());
330                 }
331
332                 f.write_str("</ul>");
333             }
334         }
335
336         f.write_str(
337             "<h1 class=\"fqn\">\
338                  <span class=\"in-band\">List of all items</span>\
339                  <span class=\"out-of-band\">\
340                      <span id=\"render-detail\">\
341                          <a id=\"toggle-all-docs\" href=\"javascript:void(0)\" \
342                             title=\"collapse all docs\">\
343                              [<span class=\"inner\">&#x2212;</span>]\
344                          </a>\
345                      </span>
346                  </span>
347              </h1>",
348         );
349         // Note: print_entries does not escape the title, because we know the current set of titles
350         // don't require escaping.
351         print_entries(f, &self.structs, "Structs", "structs");
352         print_entries(f, &self.enums, "Enums", "enums");
353         print_entries(f, &self.unions, "Unions", "unions");
354         print_entries(f, &self.primitives, "Primitives", "primitives");
355         print_entries(f, &self.traits, "Traits", "traits");
356         print_entries(f, &self.macros, "Macros", "macros");
357         print_entries(f, &self.attributes, "Attribute Macros", "attributes");
358         print_entries(f, &self.derives, "Derive Macros", "derives");
359         print_entries(f, &self.functions, "Functions", "functions");
360         print_entries(f, &self.typedefs, "Typedefs", "typedefs");
361         print_entries(f, &self.trait_aliases, "Trait Aliases", "trait-aliases");
362         print_entries(f, &self.opaque_tys, "Opaque Types", "opaque-types");
363         print_entries(f, &self.statics, "Statics", "statics");
364         print_entries(f, &self.constants, "Constants", "constants")
365     }
366 }
367
368 #[derive(Debug)]
369 enum Setting {
370     Section {
371         description: &'static str,
372         sub_settings: Vec<Setting>,
373     },
374     Toggle {
375         js_data_name: &'static str,
376         description: &'static str,
377         default_value: bool,
378     },
379     Select {
380         js_data_name: &'static str,
381         description: &'static str,
382         default_value: &'static str,
383         options: Vec<(String, String)>,
384     },
385 }
386
387 impl Setting {
388     fn display(&self, root_path: &str, suffix: &str) -> String {
389         match *self {
390             Setting::Section { description, ref sub_settings } => format!(
391                 "<div class=\"setting-line\">\
392                      <div class=\"title\">{}</div>\
393                      <div class=\"sub-settings\">{}</div>
394                  </div>",
395                 description,
396                 sub_settings.iter().map(|s| s.display(root_path, suffix)).collect::<String>()
397             ),
398             Setting::Toggle { js_data_name, description, default_value } => format!(
399                 "<div class=\"setting-line\">\
400                      <label class=\"toggle\">\
401                      <input type=\"checkbox\" id=\"{}\" {}>\
402                      <span class=\"slider\"></span>\
403                      </label>\
404                      <div>{}</div>\
405                  </div>",
406                 js_data_name,
407                 if default_value { " checked" } else { "" },
408                 description,
409             ),
410             Setting::Select { js_data_name, description, default_value, ref options } => format!(
411                 "<div class=\"setting-line\">\
412                      <div>{}</div>\
413                      <label class=\"select-wrapper\">\
414                          <select id=\"{}\" autocomplete=\"off\">{}</select>\
415                          <img src=\"{}down-arrow{}.svg\" alt=\"Select item\">\
416                      </label>\
417                  </div>",
418                 description,
419                 js_data_name,
420                 options
421                     .iter()
422                     .map(|opt| format!(
423                         "<option value=\"{}\" {}>{}</option>",
424                         opt.0,
425                         if opt.0 == default_value { "selected" } else { "" },
426                         opt.1,
427                     ))
428                     .collect::<String>(),
429                 root_path,
430                 suffix,
431             ),
432         }
433     }
434 }
435
436 impl From<(&'static str, &'static str, bool)> for Setting {
437     fn from(values: (&'static str, &'static str, bool)) -> Setting {
438         Setting::Toggle { js_data_name: values.0, description: values.1, default_value: values.2 }
439     }
440 }
441
442 impl<T: Into<Setting>> From<(&'static str, Vec<T>)> for Setting {
443     fn from(values: (&'static str, Vec<T>)) -> Setting {
444         Setting::Section {
445             description: values.0,
446             sub_settings: values.1.into_iter().map(|v| v.into()).collect::<Vec<_>>(),
447         }
448     }
449 }
450
451 fn settings(root_path: &str, suffix: &str, themes: &[StylePath]) -> Result<String, Error> {
452     let theme_names: Vec<(String, String)> = themes
453         .iter()
454         .map(|entry| {
455             let theme =
456                 try_none!(try_none!(entry.path.file_stem(), &entry.path).to_str(), &entry.path)
457                     .to_string();
458
459             Ok((theme.clone(), theme))
460         })
461         .collect::<Result<_, Error>>()?;
462
463     // (id, explanation, default value)
464     let settings: &[Setting] = &[
465         (
466             "Theme preferences",
467             vec![
468                 Setting::from(("use-system-theme", "Use system theme", true)),
469                 Setting::Select {
470                     js_data_name: "preferred-dark-theme",
471                     description: "Preferred dark theme",
472                     default_value: "dark",
473                     options: theme_names.clone(),
474                 },
475                 Setting::Select {
476                     js_data_name: "preferred-light-theme",
477                     description: "Preferred light theme",
478                     default_value: "light",
479                     options: theme_names,
480                 },
481             ],
482         )
483             .into(),
484         ("auto-hide-large-items", "Auto-hide item contents for large items.", true).into(),
485         ("auto-hide-method-docs", "Auto-hide item methods' documentation", false).into(),
486         ("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", true)
487             .into(),
488         ("auto-collapse-implementors", "Auto-hide implementors of a trait", true).into(),
489         ("go-to-only-result", "Directly go to item in search if there is only one result", false)
490             .into(),
491         ("line-numbers", "Show line numbers on code examples", false).into(),
492         ("disable-shortcuts", "Disable keyboard shortcuts", false).into(),
493     ];
494
495     Ok(format!(
496         "<h1 class=\"fqn\">\
497             <span class=\"in-band\">Rustdoc settings</span>\
498         </h1>\
499         <div class=\"settings\">{}</div>\
500         <script src=\"{}settings{}.js\"></script>",
501         settings.iter().map(|s| s.display(root_path, suffix)).collect::<String>(),
502         root_path,
503         suffix
504     ))
505 }
506
507 fn document(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, parent: Option<&clean::Item>) {
508     if let Some(ref name) = item.name {
509         info!("Documenting {}", name);
510     }
511     document_item_info(w, cx, item, parent);
512     if parent.is_none() {
513         document_full_collapsible(w, item, cx);
514     } else {
515         document_full(w, item, cx);
516     }
517 }
518
519 /// Render md_text as markdown.
520 fn render_markdown(w: &mut Buffer, cx: &Context<'_>, md_text: &str, links: Vec<RenderedLink>) {
521     let mut ids = cx.id_map.borrow_mut();
522     write!(
523         w,
524         "<div class=\"docblock\">{}</div>",
525         Markdown(
526             md_text,
527             &links,
528             &mut ids,
529             cx.shared.codes,
530             cx.shared.edition(),
531             &cx.shared.playground
532         )
533         .into_string()
534     )
535 }
536
537 /// Writes a documentation block containing only the first paragraph of the documentation. If the
538 /// docs are longer, a "Read more" link is appended to the end.
539 fn document_short(
540     w: &mut Buffer,
541     item: &clean::Item,
542     cx: &Context<'_>,
543     link: AssocItemLink<'_>,
544     parent: &clean::Item,
545     show_def_docs: bool,
546 ) {
547     document_item_info(w, cx, item, Some(parent));
548     if !show_def_docs {
549         return;
550     }
551     if let Some(s) = item.doc_value() {
552         let mut summary_html = MarkdownSummaryLine(&s, &item.links(cx)).into_string();
553
554         if s.contains('\n') {
555             let link = format!(r#" <a href="{}">Read more</a>"#, naive_assoc_href(item, link, cx));
556
557             if let Some(idx) = summary_html.rfind("</p>") {
558                 summary_html.insert_str(idx, &link);
559             } else {
560                 summary_html.push_str(&link);
561             }
562         }
563
564         write!(w, "<div class='docblock'>{}</div>", summary_html,);
565     }
566 }
567
568 fn document_full_collapsible(w: &mut Buffer, item: &clean::Item, cx: &Context<'_>) {
569     document_full_inner(w, item, cx, true);
570 }
571
572 fn document_full(w: &mut Buffer, item: &clean::Item, cx: &Context<'_>) {
573     document_full_inner(w, item, cx, false);
574 }
575
576 fn document_full_inner(w: &mut Buffer, item: &clean::Item, cx: &Context<'_>, is_collapsible: bool) {
577     if let Some(s) = cx.shared.maybe_collapsed_doc_value(item) {
578         debug!("Doc block: =====\n{}\n=====", s);
579         if is_collapsible {
580             w.write_str(
581                 "<details class=\"rustdoc-toggle top-doc\" open>\
582                 <summary class=\"hideme\">\
583                      <span>Expand description</span>\
584                 </summary>",
585             );
586             render_markdown(w, cx, &s, item.links(cx));
587             w.write_str("</details>");
588         } else {
589             render_markdown(w, cx, &s, item.links(cx));
590         }
591     }
592 }
593
594 /// Add extra information about an item such as:
595 ///
596 /// * Stability
597 /// * Deprecated
598 /// * Required features (through the `doc_cfg` feature)
599 fn document_item_info(
600     w: &mut Buffer,
601     cx: &Context<'_>,
602     item: &clean::Item,
603     parent: Option<&clean::Item>,
604 ) {
605     let item_infos = short_item_info(item, cx, parent);
606     if !item_infos.is_empty() {
607         w.write_str("<div class=\"item-info\">");
608         for info in item_infos {
609             w.write_str(&info);
610         }
611         w.write_str("</div>");
612     }
613 }
614
615 fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> {
616     let cfg = match (&item.cfg, parent.and_then(|p| p.cfg.as_ref())) {
617         (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
618         (cfg, _) => cfg.as_deref().cloned(),
619     };
620
621     debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.and_then(|p| p.cfg.as_ref()), cfg);
622
623     Some(format!("<div class=\"stab portability\">{}</div>", cfg?.render_long_html()))
624 }
625
626 /// Render the stability, deprecation and portability information that is displayed at the top of
627 /// the item's documentation.
628 fn short_item_info(
629     item: &clean::Item,
630     cx: &Context<'_>,
631     parent: Option<&clean::Item>,
632 ) -> Vec<String> {
633     let mut extra_info = vec![];
634     let error_codes = cx.shared.codes;
635
636     if let Some(Deprecation { note, since, is_since_rustc_version, suggestion: _ }) =
637         item.deprecation(cx.tcx())
638     {
639         // We display deprecation messages for #[deprecated] and #[rustc_deprecated]
640         // but only display the future-deprecation messages for #[rustc_deprecated].
641         let mut message = if let Some(since) = since {
642             let since = &since.as_str();
643             if !stability::deprecation_in_effect(is_since_rustc_version, Some(since)) {
644                 if *since == "TBD" {
645                     String::from("Deprecating in a future Rust version")
646                 } else {
647                     format!("Deprecating in {}", Escape(since))
648                 }
649             } else {
650                 format!("Deprecated since {}", Escape(since))
651             }
652         } else {
653             String::from("Deprecated")
654         };
655
656         if let Some(note) = note {
657             let note = note.as_str();
658             let mut ids = cx.id_map.borrow_mut();
659             let html = MarkdownHtml(
660                 &note,
661                 &mut ids,
662                 error_codes,
663                 cx.shared.edition(),
664                 &cx.shared.playground,
665             );
666             message.push_str(&format!(": {}", html.into_string()));
667         }
668         extra_info.push(format!(
669             "<div class=\"stab deprecated\"><span class=\"emoji\">👎</span> {}</div>",
670             message,
671         ));
672     }
673
674     // Render unstable items. But don't render "rustc_private" crates (internal compiler crates).
675     // Those crates are permanently unstable so it makes no sense to render "unstable" everywhere.
676     if let Some((StabilityLevel::Unstable { reason, issue, .. }, feature)) = item
677         .stability(cx.tcx())
678         .as_ref()
679         .filter(|stab| stab.feature != sym::rustc_private)
680         .map(|stab| (stab.level, stab.feature))
681     {
682         let mut message =
683             "<span class=\"emoji\">🔬</span> This is a nightly-only experimental API.".to_owned();
684
685         let mut feature = format!("<code>{}</code>", Escape(&feature.as_str()));
686         if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) {
687             feature.push_str(&format!(
688                 "&nbsp;<a href=\"{url}{issue}\">#{issue}</a>",
689                 url = url,
690                 issue = issue
691             ));
692         }
693
694         message.push_str(&format!(" ({})", feature));
695
696         if let Some(unstable_reason) = reason {
697             let mut ids = cx.id_map.borrow_mut();
698             message = format!(
699                 "<details><summary>{}</summary>{}</details>",
700                 message,
701                 MarkdownHtml(
702                     &unstable_reason.as_str(),
703                     &mut ids,
704                     error_codes,
705                     cx.shared.edition(),
706                     &cx.shared.playground,
707                 )
708                 .into_string()
709             );
710         }
711
712         extra_info.push(format!("<div class=\"stab unstable\">{}</div>", message));
713     }
714
715     if let Some(portability) = portability(item, parent) {
716         extra_info.push(portability);
717     }
718
719     extra_info
720 }
721
722 fn render_impls(
723     cx: &Context<'_>,
724     w: &mut Buffer,
725     traits: &[&&Impl],
726     containing_item: &clean::Item,
727 ) {
728     let cache = cx.cache();
729     let tcx = cx.tcx();
730     let mut impls = traits
731         .iter()
732         .map(|i| {
733             let did = i.trait_did_full(cache).unwrap();
734             let provided_trait_methods = i.inner_impl().provided_trait_methods(tcx);
735             let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods);
736             let mut buffer = if w.is_for_html() { Buffer::html() } else { Buffer::new() };
737             render_impl(
738                 &mut buffer,
739                 cx,
740                 i,
741                 containing_item,
742                 assoc_link,
743                 RenderMode::Normal,
744                 containing_item.stable_since(tcx).as_deref(),
745                 containing_item.const_stable_since(tcx).as_deref(),
746                 true,
747                 None,
748                 false,
749                 true,
750                 &[],
751             );
752             buffer.into_inner()
753         })
754         .collect::<Vec<_>>();
755     impls.sort();
756     w.write_str(&impls.join(""));
757 }
758
759 fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) -> String {
760     use crate::formats::item_type::ItemType::*;
761
762     let name = it.name.as_ref().unwrap();
763     let ty = match it.type_() {
764         Typedef | AssocType => AssocType,
765         s => s,
766     };
767
768     let anchor = format!("#{}.{}", ty, name);
769     match link {
770         AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id),
771         AssocItemLink::Anchor(None) => anchor,
772         AssocItemLink::GotoSource(did, _) => {
773             href(did.expect_real(), cx).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor)
774         }
775     }
776 }
777
778 fn assoc_const(
779     w: &mut Buffer,
780     it: &clean::Item,
781     ty: &clean::Type,
782     _default: Option<&String>,
783     link: AssocItemLink<'_>,
784     extra: &str,
785     cx: &Context<'_>,
786 ) {
787     write!(
788         w,
789         "{}{}const <a href=\"{}\" class=\"constant\"><b>{}</b></a>: {}",
790         extra,
791         it.visibility.print_with_space(it.def_id, cx),
792         naive_assoc_href(it, link, cx),
793         it.name.as_ref().unwrap(),
794         ty.print(cx)
795     );
796 }
797
798 fn assoc_type(
799     w: &mut Buffer,
800     it: &clean::Item,
801     bounds: &[clean::GenericBound],
802     default: Option<&clean::Type>,
803     link: AssocItemLink<'_>,
804     extra: &str,
805     cx: &Context<'_>,
806 ) {
807     write!(
808         w,
809         "{}type <a href=\"{}\" class=\"type\">{}</a>",
810         extra,
811         naive_assoc_href(it, link, cx),
812         it.name.as_ref().unwrap()
813     );
814     if !bounds.is_empty() {
815         write!(w, ": {}", print_generic_bounds(bounds, cx))
816     }
817     if let Some(default) = default {
818         write!(w, " = {}", default.print(cx))
819     }
820 }
821
822 fn render_stability_since_raw(
823     w: &mut Buffer,
824     ver: Option<&str>,
825     const_ver: Option<&str>,
826     containing_ver: Option<&str>,
827     containing_const_ver: Option<&str>,
828 ) {
829     let ver = ver.filter(|inner| !inner.is_empty());
830     let const_ver = const_ver.filter(|inner| !inner.is_empty());
831
832     match (ver, const_ver) {
833         (Some(v), Some(cv)) if const_ver != containing_const_ver => {
834             write!(
835                 w,
836                 "<span class=\"since\" title=\"Stable since Rust version {0}, const since {1}\">{0} (const: {1})</span>",
837                 v, cv
838             );
839         }
840         (Some(v), _) if ver != containing_ver => {
841             write!(
842                 w,
843                 "<span class=\"since\" title=\"Stable since Rust version {0}\">{0}</span>",
844                 v
845             );
846         }
847         _ => {}
848     }
849 }
850
851 fn render_assoc_item(
852     w: &mut Buffer,
853     item: &clean::Item,
854     link: AssocItemLink<'_>,
855     parent: ItemType,
856     cx: &Context<'_>,
857 ) {
858     fn method(
859         w: &mut Buffer,
860         meth: &clean::Item,
861         header: hir::FnHeader,
862         g: &clean::Generics,
863         d: &clean::FnDecl,
864         link: AssocItemLink<'_>,
865         parent: ItemType,
866         cx: &Context<'_>,
867     ) {
868         let name = meth.name.as_ref().unwrap();
869         let href = match link {
870             AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id),
871             AssocItemLink::Anchor(None) => format!("#{}.{}", meth.type_(), name),
872             AssocItemLink::GotoSource(did, provided_methods) => {
873                 // We're creating a link from an impl-item to the corresponding
874                 // trait-item and need to map the anchored type accordingly.
875                 let ty = if provided_methods.contains(&name) {
876                     ItemType::Method
877                 } else {
878                     ItemType::TyMethod
879                 };
880
881                 href(did.expect_real(), cx)
882                     .map(|p| format!("{}#{}.{}", p.0, ty, name))
883                     .unwrap_or_else(|| format!("#{}.{}", ty, name))
884             }
885         };
886         let vis = meth.visibility.print_with_space(meth.def_id, cx).to_string();
887         let constness = header.constness.print_with_space();
888         let asyncness = header.asyncness.print_with_space();
889         let unsafety = header.unsafety.print_with_space();
890         let defaultness = print_default_space(meth.is_default());
891         let abi = print_abi_with_space(header.abi).to_string();
892         // NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`.
893         let generics_len = format!("{:#}", g.print(cx)).len();
894         let mut header_len = "fn ".len()
895             + vis.len()
896             + constness.len()
897             + asyncness.len()
898             + unsafety.len()
899             + defaultness.len()
900             + abi.len()
901             + name.as_str().len()
902             + generics_len;
903
904         let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
905             header_len += 4;
906             let indent_str = "    ";
907             render_attributes_in_pre(w, meth, indent_str);
908             (4, indent_str, false)
909         } else {
910             render_attributes_in_code(w, meth);
911             (0, "", true)
912         };
913         w.reserve(header_len + "<a href=\"\" class=\"fnname\">{".len() + "</a>".len());
914         write!(
915             w,
916             "{}{}{}{}{}{}{}fn <a href=\"{href}\" class=\"fnname\">{name}</a>\
917              {generics}{decl}{notable_traits}{where_clause}",
918             indent_str,
919             vis,
920             constness,
921             asyncness,
922             unsafety,
923             defaultness,
924             abi,
925             href = href,
926             name = name,
927             generics = g.print(cx),
928             decl = d.full_print(header_len, indent, header.asyncness, cx),
929             notable_traits = notable_traits_decl(&d, cx),
930             where_clause = print_where_clause(g, cx, indent, end_newline),
931         )
932     }
933     match *item.kind {
934         clean::StrippedItem(..) => {}
935         clean::TyMethodItem(ref m) => {
936             method(w, item, m.header, &m.generics, &m.decl, link, parent, cx)
937         }
938         clean::MethodItem(ref m, _) => {
939             method(w, item, m.header, &m.generics, &m.decl, link, parent, cx)
940         }
941         clean::AssocConstItem(ref ty, ref default) => assoc_const(
942             w,
943             item,
944             ty,
945             default.as_ref(),
946             link,
947             if parent == ItemType::Trait { "    " } else { "" },
948             cx,
949         ),
950         clean::AssocTypeItem(ref bounds, ref default) => assoc_type(
951             w,
952             item,
953             bounds,
954             default.as_ref(),
955             link,
956             if parent == ItemType::Trait { "    " } else { "" },
957             cx,
958         ),
959         _ => panic!("render_assoc_item called on non-associated-item"),
960     }
961 }
962
963 const ALLOWED_ATTRIBUTES: &[Symbol] = &[
964     sym::export_name,
965     sym::link_section,
966     sym::must_use,
967     sym::no_mangle,
968     sym::repr,
969     sym::non_exhaustive,
970 ];
971
972 fn attributes(it: &clean::Item) -> Vec<String> {
973     it.attrs
974         .other_attrs
975         .iter()
976         .filter_map(|attr| {
977             if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) {
978                 Some(pprust::attribute_to_string(&attr).replace("\n", "").replace("  ", " "))
979             } else {
980                 None
981             }
982         })
983         .collect()
984 }
985
986 // When an attribute is rendered inside a `<pre>` tag, it is formatted using
987 // a whitespace prefix and newline.
988 fn render_attributes_in_pre(w: &mut Buffer, it: &clean::Item, prefix: &str) {
989     for a in attributes(it) {
990         writeln!(w, "{}{}", prefix, a);
991     }
992 }
993
994 // When an attribute is rendered inside a <code> tag, it is formatted using
995 // a div to produce a newline after it.
996 fn render_attributes_in_code(w: &mut Buffer, it: &clean::Item) {
997     for a in attributes(it) {
998         write!(w, "<div class=\"code-attribute\">{}</div>", a);
999     }
1000 }
1001
1002 #[derive(Copy, Clone)]
1003 enum AssocItemLink<'a> {
1004     Anchor(Option<&'a str>),
1005     GotoSource(FakeDefId, &'a FxHashSet<Symbol>),
1006 }
1007
1008 impl<'a> AssocItemLink<'a> {
1009     fn anchor(&self, id: &'a str) -> Self {
1010         match *self {
1011             AssocItemLink::Anchor(_) => AssocItemLink::Anchor(Some(&id)),
1012             ref other => *other,
1013         }
1014     }
1015 }
1016
1017 fn render_assoc_items(
1018     w: &mut Buffer,
1019     cx: &Context<'_>,
1020     containing_item: &clean::Item,
1021     it: DefId,
1022     what: AssocItemRender<'_>,
1023 ) {
1024     info!("Documenting associated items of {:?}", containing_item.name);
1025     let v = match cx.cache.impls.get(&it) {
1026         Some(v) => v,
1027         None => return,
1028     };
1029     let tcx = cx.tcx();
1030     let cache = cx.cache();
1031     let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
1032     if !non_trait.is_empty() {
1033         let render_mode = match what {
1034             AssocItemRender::All => {
1035                 w.write_str(
1036                     "<h2 id=\"implementations\" class=\"small-section-header\">\
1037                          Implementations<a href=\"#implementations\" class=\"anchor\"></a>\
1038                     </h2>",
1039                 );
1040                 RenderMode::Normal
1041             }
1042             AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
1043                 let id =
1044                     cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
1045                 debug!("Adding {} to deref id map", type_.print(cx));
1046                 cx.deref_id_map.borrow_mut().insert(type_.def_id_full(cache).unwrap(), id.clone());
1047                 write!(
1048                     w,
1049                     "<h2 id=\"{id}\" class=\"small-section-header\">\
1050                          Methods from {trait_}&lt;Target = {type_}&gt;\
1051                          <a href=\"#{id}\" class=\"anchor\"></a>\
1052                      </h2>",
1053                     id = id,
1054                     trait_ = trait_.print(cx),
1055                     type_ = type_.print(cx),
1056                 );
1057                 RenderMode::ForDeref { mut_: deref_mut_ }
1058             }
1059         };
1060         for i in &non_trait {
1061             render_impl(
1062                 w,
1063                 cx,
1064                 i,
1065                 containing_item,
1066                 AssocItemLink::Anchor(None),
1067                 render_mode,
1068                 containing_item.stable_since(tcx).as_deref(),
1069                 containing_item.const_stable_since(tcx).as_deref(),
1070                 true,
1071                 None,
1072                 false,
1073                 true,
1074                 &[],
1075             );
1076         }
1077     }
1078     if !traits.is_empty() {
1079         let deref_impl = traits
1080             .iter()
1081             .find(|t| t.inner_impl().trait_.def_id_full(cache) == cx.cache.deref_trait_did);
1082         if let Some(impl_) = deref_impl {
1083             let has_deref_mut = traits
1084                 .iter()
1085                 .any(|t| t.inner_impl().trait_.def_id_full(cache) == cx.cache.deref_mut_trait_did);
1086             render_deref_methods(w, cx, impl_, containing_item, has_deref_mut);
1087         }
1088
1089         // If we were already one level into rendering deref methods, we don't want to render
1090         // anything after recursing into any further deref methods above.
1091         if let AssocItemRender::DerefFor { .. } = what {
1092             return;
1093         }
1094
1095         let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) =
1096             traits.iter().partition(|t| t.inner_impl().synthetic);
1097         let (blanket_impl, concrete): (Vec<&&Impl>, _) =
1098             concrete.into_iter().partition(|t| t.inner_impl().blanket_impl.is_some());
1099
1100         let mut impls = Buffer::empty_from(&w);
1101         render_impls(cx, &mut impls, &concrete, containing_item);
1102         let impls = impls.into_inner();
1103         if !impls.is_empty() {
1104             write!(
1105                 w,
1106                 "<h2 id=\"trait-implementations\" class=\"small-section-header\">\
1107                      Trait Implementations<a href=\"#trait-implementations\" class=\"anchor\"></a>\
1108                  </h2>\
1109                  <div id=\"trait-implementations-list\">{}</div>",
1110                 impls
1111             );
1112         }
1113
1114         if !synthetic.is_empty() {
1115             w.write_str(
1116                 "<h2 id=\"synthetic-implementations\" class=\"small-section-header\">\
1117                      Auto Trait Implementations\
1118                      <a href=\"#synthetic-implementations\" class=\"anchor\"></a>\
1119                  </h2>\
1120                  <div id=\"synthetic-implementations-list\">",
1121             );
1122             render_impls(cx, w, &synthetic, containing_item);
1123             w.write_str("</div>");
1124         }
1125
1126         if !blanket_impl.is_empty() {
1127             w.write_str(
1128                 "<h2 id=\"blanket-implementations\" class=\"small-section-header\">\
1129                      Blanket Implementations\
1130                      <a href=\"#blanket-implementations\" class=\"anchor\"></a>\
1131                  </h2>\
1132                  <div id=\"blanket-implementations-list\">",
1133             );
1134             render_impls(cx, w, &blanket_impl, containing_item);
1135             w.write_str("</div>");
1136         }
1137     }
1138 }
1139
1140 fn render_deref_methods(
1141     w: &mut Buffer,
1142     cx: &Context<'_>,
1143     impl_: &Impl,
1144     container_item: &clean::Item,
1145     deref_mut: bool,
1146 ) {
1147     let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
1148     let (target, real_target) = impl_
1149         .inner_impl()
1150         .items
1151         .iter()
1152         .find_map(|item| match *item.kind {
1153             clean::TypedefItem(ref t, true) => Some(match *t {
1154                 clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
1155                 _ => (&t.type_, &t.type_),
1156             }),
1157             _ => None,
1158         })
1159         .expect("Expected associated type binding");
1160     debug!("Render deref methods for {:#?}, target {:#?}", impl_.inner_impl().for_, target);
1161     let what =
1162         AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
1163     if let Some(did) = target.def_id_full(cx.cache()) {
1164         if let Some(type_did) = impl_.inner_impl().for_.def_id_full(cx.cache()) {
1165             // `impl Deref<Target = S> for S`
1166             if did == type_did {
1167                 // Avoid infinite cycles
1168                 return;
1169             }
1170         }
1171         render_assoc_items(w, cx, container_item, did, what);
1172     } else {
1173         if let Some(prim) = target.primitive_type() {
1174             if let Some(&did) = cx.cache.primitive_locations.get(&prim) {
1175                 render_assoc_items(w, cx, container_item, did, what);
1176             }
1177         }
1178     }
1179 }
1180
1181 fn should_render_item(item: &clean::Item, deref_mut_: bool, cache: &Cache) -> bool {
1182     let self_type_opt = match *item.kind {
1183         clean::MethodItem(ref method, _) => method.decl.self_type(),
1184         clean::TyMethodItem(ref method) => method.decl.self_type(),
1185         _ => None,
1186     };
1187
1188     if let Some(self_ty) = self_type_opt {
1189         let (by_mut_ref, by_box, by_value) = match self_ty {
1190             SelfTy::SelfBorrowed(_, mutability)
1191             | SelfTy::SelfExplicit(clean::BorrowedRef { mutability, .. }) => {
1192                 (mutability == Mutability::Mut, false, false)
1193             }
1194             SelfTy::SelfExplicit(clean::ResolvedPath { did, .. }) => {
1195                 (false, Some(did) == cache.owned_box_did, false)
1196             }
1197             SelfTy::SelfValue => (false, false, true),
1198             _ => (false, false, false),
1199         };
1200
1201         (deref_mut_ || !by_mut_ref) && !by_box && !by_value
1202     } else {
1203         false
1204     }
1205 }
1206
1207 fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String {
1208     let mut out = Buffer::html();
1209     let mut trait_ = String::new();
1210
1211     if let Some(did) = decl.output.def_id_full(cx.cache()) {
1212         if let Some(impls) = cx.cache().impls.get(&did) {
1213             for i in impls {
1214                 let impl_ = i.inner_impl();
1215                 if impl_.trait_.def_id().map_or(false, |d| {
1216                     cx.cache().traits.get(&d).map(|t| t.is_notable).unwrap_or(false)
1217                 }) {
1218                     if out.is_empty() {
1219                         write!(
1220                             &mut out,
1221                             "<h3 class=\"notable\">Notable traits for {}</h3>\
1222                              <code class=\"content\">",
1223                             impl_.for_.print(cx)
1224                         );
1225                         trait_.push_str(&impl_.for_.print(cx).to_string());
1226                     }
1227
1228                     //use the "where" class here to make it small
1229                     write!(
1230                         &mut out,
1231                         "<span class=\"where fmt-newline\">{}</span>",
1232                         impl_.print(false, cx)
1233                     );
1234                     let t_did = impl_.trait_.def_id_full(cx.cache()).unwrap();
1235                     for it in &impl_.items {
1236                         if let clean::TypedefItem(ref tydef, _) = *it.kind {
1237                             out.push_str("<span class=\"where fmt-newline\">    ");
1238                             assoc_type(
1239                                 &mut out,
1240                                 it,
1241                                 &[],
1242                                 Some(&tydef.type_),
1243                                 AssocItemLink::GotoSource(t_did.into(), &FxHashSet::default()),
1244                                 "",
1245                                 cx,
1246                             );
1247                             out.push_str(";</span>");
1248                         }
1249                     }
1250                 }
1251             }
1252         }
1253     }
1254
1255     if !out.is_empty() {
1256         out.insert_str(
1257             0,
1258             "<span class=\"notable-traits\"><span class=\"notable-traits-tooltip\">ⓘ\
1259             <div class=\"notable-traits-tooltiptext\"><span class=\"docblock\">",
1260         );
1261         out.push_str("</code></span></div></span></span>");
1262     }
1263
1264     out.into_inner()
1265 }
1266
1267 fn render_impl(
1268     w: &mut Buffer,
1269     cx: &Context<'_>,
1270     i: &Impl,
1271     parent: &clean::Item,
1272     link: AssocItemLink<'_>,
1273     render_mode: RenderMode,
1274     outer_version: Option<&str>,
1275     outer_const_version: Option<&str>,
1276     show_def_docs: bool,
1277     use_absolute: Option<bool>,
1278     is_on_foreign_type: bool,
1279     show_default_items: bool,
1280     // This argument is used to reference same type with different paths to avoid duplication
1281     // in documentation pages for trait with automatic implementations like "Send" and "Sync".
1282     aliases: &[String],
1283 ) {
1284     let tcx = cx.tcx();
1285     let cache = cx.cache();
1286     let traits = &cache.traits;
1287     let trait_ = i.trait_did_full(cache).map(|did| &traits[&did]);
1288     let mut close_tags = String::new();
1289
1290     // For trait implementations, the `interesting` output contains all methods that have doc
1291     // comments, and the `boring` output contains all methods that do not. The distinction is
1292     // used to allow hiding the boring methods.
1293     fn doc_impl_item(
1294         boring: &mut Buffer,
1295         interesting: &mut Buffer,
1296         cx: &Context<'_>,
1297         item: &clean::Item,
1298         parent: &clean::Item,
1299         link: AssocItemLink<'_>,
1300         render_mode: RenderMode,
1301         is_default_item: bool,
1302         outer_version: Option<&str>,
1303         outer_const_version: Option<&str>,
1304         trait_: Option<&clean::Trait>,
1305         show_def_docs: bool,
1306     ) {
1307         let item_type = item.type_();
1308         let name = item.name.as_ref().unwrap();
1309         let tcx = cx.tcx();
1310
1311         let render_method_item = match render_mode {
1312             RenderMode::Normal => true,
1313             RenderMode::ForDeref { mut_: deref_mut_ } => {
1314                 should_render_item(&item, deref_mut_, &cx.cache)
1315             }
1316         };
1317
1318         let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
1319
1320         let mut doc_buffer = Buffer::empty_from(boring);
1321         let mut info_buffer = Buffer::empty_from(boring);
1322         let mut short_documented = true;
1323
1324         if render_method_item {
1325             if !is_default_item {
1326                 if let Some(t) = trait_ {
1327                     // The trait item may have been stripped so we might not
1328                     // find any documentation or stability for it.
1329                     if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1330                         // We need the stability of the item from the trait
1331                         // because impls can't have a stability.
1332                         if item.doc_value().is_some() {
1333                             document_item_info(&mut info_buffer, cx, it, Some(parent));
1334                             document_full(&mut doc_buffer, item, cx);
1335                             short_documented = false;
1336                         } else {
1337                             // In case the item isn't documented,
1338                             // provide short documentation from the trait.
1339                             document_short(&mut doc_buffer, it, cx, link, parent, show_def_docs);
1340                         }
1341                     }
1342                 } else {
1343                     document_item_info(&mut info_buffer, cx, item, Some(parent));
1344                     if show_def_docs {
1345                         document_full(&mut doc_buffer, item, cx);
1346                         short_documented = false;
1347                     }
1348                 }
1349             } else {
1350                 document_short(&mut doc_buffer, item, cx, link, parent, show_def_docs);
1351             }
1352         }
1353         let w = if short_documented && trait_.is_some() { interesting } else { boring };
1354
1355         let toggled = !doc_buffer.is_empty();
1356         if toggled {
1357             let method_toggle_class =
1358                 if item_type == ItemType::Method { " method-toggle" } else { "" };
1359             write!(w, "<details class=\"rustdoc-toggle{}\" open><summary>", method_toggle_class);
1360         }
1361         match *item.kind {
1362             clean::MethodItem(..) | clean::TyMethodItem(_) => {
1363                 // Only render when the method is not static or we allow static methods
1364                 if render_method_item {
1365                     let id = cx.derive_id(format!("{}.{}", item_type, name));
1366                     let source_id = trait_
1367                         .and_then(|trait_| {
1368                             trait_.items.iter().find(|item| {
1369                                 item.name.map(|n| n.as_str().eq(&name.as_str())).unwrap_or(false)
1370                             })
1371                         })
1372                         .map(|item| format!("{}.{}", item.type_(), name));
1373                     write!(w, "<h4 id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class,);
1374                     w.write_str("<code>");
1375                     render_assoc_item(
1376                         w,
1377                         item,
1378                         link.anchor(source_id.as_ref().unwrap_or(&id)),
1379                         ItemType::Impl,
1380                         cx,
1381                     );
1382                     w.write_str("</code>");
1383                     render_stability_since_raw(
1384                         w,
1385                         item.stable_since(tcx).as_deref(),
1386                         item.const_stable_since(tcx).as_deref(),
1387                         outer_version,
1388                         outer_const_version,
1389                     );
1390                     write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
1391                     write_srclink(cx, item, w);
1392                     w.write_str("</h4>");
1393                 }
1394             }
1395             clean::TypedefItem(ref tydef, _) => {
1396                 let source_id = format!("{}.{}", ItemType::AssocType, name);
1397                 let id = cx.derive_id(source_id.clone());
1398                 write!(w, "<h4 id=\"{}\" class=\"{}{}\"><code>", id, item_type, in_trait_class);
1399                 assoc_type(
1400                     w,
1401                     item,
1402                     &Vec::new(),
1403                     Some(&tydef.type_),
1404                     link.anchor(if trait_.is_some() { &source_id } else { &id }),
1405                     "",
1406                     cx,
1407                 );
1408                 w.write_str("</code>");
1409                 write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
1410                 w.write_str("</h4>");
1411             }
1412             clean::AssocConstItem(ref ty, ref default) => {
1413                 let source_id = format!("{}.{}", item_type, name);
1414                 let id = cx.derive_id(source_id.clone());
1415                 write!(w, "<h4 id=\"{}\" class=\"{}{}\"><code>", id, item_type, in_trait_class);
1416                 assoc_const(
1417                     w,
1418                     item,
1419                     ty,
1420                     default.as_ref(),
1421                     link.anchor(if trait_.is_some() { &source_id } else { &id }),
1422                     "",
1423                     cx,
1424                 );
1425                 w.write_str("</code>");
1426                 render_stability_since_raw(
1427                     w,
1428                     item.stable_since(tcx).as_deref(),
1429                     item.const_stable_since(tcx).as_deref(),
1430                     outer_version,
1431                     outer_const_version,
1432                 );
1433                 write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
1434                 write_srclink(cx, item, w);
1435                 w.write_str("</h4>");
1436             }
1437             clean::AssocTypeItem(ref bounds, ref default) => {
1438                 let source_id = format!("{}.{}", item_type, name);
1439                 let id = cx.derive_id(source_id.clone());
1440                 write!(w, "<h4 id=\"{}\" class=\"{}{}\"><code>", id, item_type, in_trait_class);
1441                 assoc_type(
1442                     w,
1443                     item,
1444                     bounds,
1445                     default.as_ref(),
1446                     link.anchor(if trait_.is_some() { &source_id } else { &id }),
1447                     "",
1448                     cx,
1449                 );
1450                 w.write_str("</code>");
1451                 write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
1452                 w.write_str("</h4>");
1453             }
1454             clean::StrippedItem(..) => return,
1455             _ => panic!("can't make docs for trait item with name {:?}", item.name),
1456         }
1457
1458         w.push_buffer(info_buffer);
1459         if toggled {
1460             w.write_str("</summary>");
1461             w.push_buffer(doc_buffer);
1462             w.push_str("</details>");
1463         }
1464     }
1465
1466     let mut impl_items = Buffer::empty_from(w);
1467     let mut default_impl_items = Buffer::empty_from(w);
1468
1469     for trait_item in &i.inner_impl().items {
1470         doc_impl_item(
1471             &mut default_impl_items,
1472             &mut impl_items,
1473             cx,
1474             trait_item,
1475             if trait_.is_some() { &i.impl_item } else { parent },
1476             link,
1477             render_mode,
1478             false,
1479             outer_version,
1480             outer_const_version,
1481             trait_.map(|t| &t.trait_),
1482             show_def_docs,
1483         );
1484     }
1485
1486     fn render_default_items(
1487         boring: &mut Buffer,
1488         interesting: &mut Buffer,
1489         cx: &Context<'_>,
1490         t: &clean::Trait,
1491         i: &clean::Impl,
1492         parent: &clean::Item,
1493         render_mode: RenderMode,
1494         outer_version: Option<&str>,
1495         outer_const_version: Option<&str>,
1496         show_def_docs: bool,
1497     ) {
1498         for trait_item in &t.items {
1499             let n = trait_item.name;
1500             if i.items.iter().any(|m| m.name == n) {
1501                 continue;
1502             }
1503             let did = i.trait_.as_ref().unwrap().def_id_full(cx.cache()).unwrap();
1504             let provided_methods = i.provided_trait_methods(cx.tcx());
1505             let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
1506
1507             doc_impl_item(
1508                 boring,
1509                 interesting,
1510                 cx,
1511                 trait_item,
1512                 parent,
1513                 assoc_link,
1514                 render_mode,
1515                 true,
1516                 outer_version,
1517                 outer_const_version,
1518                 Some(t),
1519                 show_def_docs,
1520             );
1521         }
1522     }
1523
1524     // If we've implemented a trait, then also emit documentation for all
1525     // default items which weren't overridden in the implementation block.
1526     // We don't emit documentation for default items if they appear in the
1527     // Implementations on Foreign Types or Implementors sections.
1528     if show_default_items {
1529         if let Some(t) = trait_ {
1530             render_default_items(
1531                 &mut default_impl_items,
1532                 &mut impl_items,
1533                 cx,
1534                 &t.trait_,
1535                 &i.inner_impl(),
1536                 &i.impl_item,
1537                 render_mode,
1538                 outer_version,
1539                 outer_const_version,
1540                 show_def_docs,
1541             );
1542         }
1543     }
1544     let toggled = !impl_items.is_empty() || !default_impl_items.is_empty();
1545     let open_details = |close_tags: &mut String, is_collapsed: bool| {
1546         if toggled {
1547             close_tags.insert_str(0, "</details>");
1548             if is_collapsed {
1549                 "<details class=\"rustdoc-toggle implementors-toggle\"><summary>"
1550             } else {
1551                 "<details class=\"rustdoc-toggle implementors-toggle\" open><summary>"
1552             }
1553         } else {
1554             ""
1555         }
1556     };
1557     if render_mode == RenderMode::Normal {
1558         let is_implementing_trait;
1559         let id = cx.derive_id(match i.inner_impl().trait_ {
1560             Some(ref t) => {
1561                 is_implementing_trait = true;
1562                 if is_on_foreign_type {
1563                     get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t, cx)
1564                 } else {
1565                     format!("impl-{}", small_url_encode(format!("{:#}", t.print(cx))))
1566                 }
1567             }
1568             None => {
1569                 is_implementing_trait = false;
1570                 "impl".to_string()
1571             }
1572         });
1573         let aliases = if aliases.is_empty() {
1574             String::new()
1575         } else {
1576             format!(" data-aliases=\"{}\"", aliases.join(","))
1577         };
1578         if let Some(use_absolute) = use_absolute {
1579             write!(
1580                 w,
1581                 "{}<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">",
1582                 open_details(&mut close_tags, is_implementing_trait),
1583                 id,
1584                 aliases
1585             );
1586             write!(w, "{}", i.inner_impl().print(use_absolute, cx));
1587             if show_def_docs {
1588                 for it in &i.inner_impl().items {
1589                     if let clean::TypedefItem(ref tydef, _) = *it.kind {
1590                         w.write_str("<span class=\"where fmt-newline\">  ");
1591                         assoc_type(
1592                             w,
1593                             it,
1594                             &[],
1595                             Some(&tydef.type_),
1596                             AssocItemLink::Anchor(None),
1597                             "",
1598                             cx,
1599                         );
1600                         w.write_str(";</span>");
1601                     }
1602                 }
1603             }
1604             w.write_str("</code>");
1605         } else {
1606             write!(
1607                 w,
1608                 "{}<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">{}</code>",
1609                 open_details(&mut close_tags, is_implementing_trait),
1610                 id,
1611                 aliases,
1612                 i.inner_impl().print(false, cx)
1613             );
1614         }
1615         write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
1616         render_stability_since_raw(
1617             w,
1618             i.impl_item.stable_since(tcx).as_deref(),
1619             i.impl_item.const_stable_since(tcx).as_deref(),
1620             outer_version,
1621             outer_const_version,
1622         );
1623         write_srclink(cx, &i.impl_item, w);
1624         if !toggled {
1625             w.write_str("</h3>");
1626         } else {
1627             w.write_str("</h3></summary>");
1628         }
1629
1630         if trait_.is_some() {
1631             if let Some(portability) = portability(&i.impl_item, Some(parent)) {
1632                 write!(w, "<div class=\"item-info\">{}</div>", portability);
1633             }
1634         }
1635
1636         if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) {
1637             let mut ids = cx.id_map.borrow_mut();
1638             write!(
1639                 w,
1640                 "<div class=\"docblock\">{}</div>",
1641                 Markdown(
1642                     &*dox,
1643                     &i.impl_item.links(cx),
1644                     &mut ids,
1645                     cx.shared.codes,
1646                     cx.shared.edition(),
1647                     &cx.shared.playground
1648                 )
1649                 .into_string()
1650             );
1651         }
1652     }
1653     if toggled {
1654         w.write_str("<div class=\"impl-items\">");
1655         w.push_buffer(default_impl_items);
1656         if trait_.is_some() && !impl_items.is_empty() {
1657             w.write_str("<details class=\"undocumented\"><summary></summary>");
1658             close_tags.insert_str(0, "</details>");
1659         }
1660         w.push_buffer(impl_items);
1661         close_tags.insert_str(0, "</div>");
1662     }
1663     w.write_str(&close_tags);
1664 }
1665
1666 fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
1667     let parentlen = cx.current.len() - if it.is_mod() { 1 } else { 0 };
1668
1669     if it.is_struct()
1670         || it.is_trait()
1671         || it.is_primitive()
1672         || it.is_union()
1673         || it.is_enum()
1674         || it.is_mod()
1675         || it.is_typedef()
1676     {
1677         write!(
1678             buffer,
1679             "<p class=\"location\">{}{}</p>",
1680             match *it.kind {
1681                 clean::StructItem(..) => "Struct ",
1682                 clean::TraitItem(..) => "Trait ",
1683                 clean::PrimitiveItem(..) => "Primitive Type ",
1684                 clean::UnionItem(..) => "Union ",
1685                 clean::EnumItem(..) => "Enum ",
1686                 clean::TypedefItem(..) => "Type Definition ",
1687                 clean::ForeignTypeItem => "Foreign Type ",
1688                 clean::ModuleItem(..) =>
1689                     if it.is_crate() {
1690                         "Crate "
1691                     } else {
1692                         "Module "
1693                     },
1694                 _ => "",
1695             },
1696             it.name.as_ref().unwrap()
1697         );
1698     }
1699
1700     if it.is_crate() {
1701         if let Some(ref version) = cx.cache.crate_version {
1702             write!(
1703                 buffer,
1704                 "<div class=\"block version\">\
1705                      <p>Version {}</p>\
1706                  </div>",
1707                 Escape(version)
1708             );
1709         }
1710     }
1711
1712     buffer.write_str("<div class=\"sidebar-elems\">");
1713     if it.is_crate() {
1714         write!(
1715             buffer,
1716             "<a id=\"all-types\" href=\"all.html\"><p>See all {}'s items</p></a>",
1717             it.name.as_ref().expect("crates always have a name")
1718         );
1719     }
1720     match *it.kind {
1721         clean::StructItem(ref s) => sidebar_struct(cx, buffer, it, s),
1722         clean::TraitItem(ref t) => sidebar_trait(cx, buffer, it, t),
1723         clean::PrimitiveItem(_) => sidebar_primitive(cx, buffer, it),
1724         clean::UnionItem(ref u) => sidebar_union(cx, buffer, it, u),
1725         clean::EnumItem(ref e) => sidebar_enum(cx, buffer, it, e),
1726         clean::TypedefItem(_, _) => sidebar_typedef(cx, buffer, it),
1727         clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items),
1728         clean::ForeignTypeItem => sidebar_foreign_type(cx, buffer, it),
1729         _ => (),
1730     }
1731
1732     // The sidebar is designed to display sibling functions, modules and
1733     // other miscellaneous information. since there are lots of sibling
1734     // items (and that causes quadratic growth in large modules),
1735     // we refactor common parts into a shared JavaScript file per module.
1736     // still, we don't move everything into JS because we want to preserve
1737     // as much HTML as possible in order to allow non-JS-enabled browsers
1738     // to navigate the documentation (though slightly inefficiently).
1739
1740     buffer.write_str("<p class=\"location\">");
1741     for (i, name) in cx.current.iter().take(parentlen).enumerate() {
1742         if i > 0 {
1743             buffer.write_str("::<wbr>");
1744         }
1745         write!(
1746             buffer,
1747             "<a href=\"{}index.html\">{}</a>",
1748             &cx.root_path()[..(cx.current.len() - i - 1) * 3],
1749             *name
1750         );
1751     }
1752     buffer.write_str("</p>");
1753
1754     // Sidebar refers to the enclosing module, not this module.
1755     let relpath = if it.is_mod() { "../" } else { "" };
1756     write!(
1757         buffer,
1758         "<div id=\"sidebar-vars\" data-name=\"{name}\" data-ty=\"{ty}\" data-relpath=\"{path}\">\
1759         </div>",
1760         name = it.name.unwrap_or(kw::Empty),
1761         ty = it.type_(),
1762         path = relpath
1763     );
1764
1765     if parentlen == 0 {
1766         write!(
1767             buffer,
1768             "<script defer src=\"{}sidebar-items{}.js\"></script>",
1769             relpath, cx.shared.resource_suffix
1770         );
1771     } else {
1772         write!(buffer, "<script defer src=\"{}sidebar-items.js\"></script>", relpath);
1773     }
1774
1775     // Closes sidebar-elems div.
1776     buffer.write_str("</div>");
1777 }
1778
1779 fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String {
1780     if used_links.insert(url.clone()) {
1781         return url;
1782     }
1783     let mut add = 1;
1784     while !used_links.insert(format!("{}-{}", url, add)) {
1785         add += 1;
1786     }
1787     format!("{}-{}", url, add)
1788 }
1789
1790 fn get_methods(
1791     i: &clean::Impl,
1792     for_deref: bool,
1793     used_links: &mut FxHashSet<String>,
1794     deref_mut: bool,
1795     cache: &Cache,
1796 ) -> Vec<String> {
1797     i.items
1798         .iter()
1799         .filter_map(|item| match item.name {
1800             Some(ref name) if !name.is_empty() && item.is_method() => {
1801                 if !for_deref || should_render_item(item, deref_mut, cache) {
1802                     Some(format!(
1803                         "<a href=\"#{}\">{}</a>",
1804                         get_next_url(used_links, format!("method.{}", name)),
1805                         name
1806                     ))
1807                 } else {
1808                     None
1809                 }
1810             }
1811             _ => None,
1812         })
1813         .collect::<Vec<_>>()
1814 }
1815
1816 // The point is to url encode any potential character from a type with genericity.
1817 fn small_url_encode(s: String) -> String {
1818     let mut st = String::new();
1819     let mut last_match = 0;
1820     for (idx, c) in s.char_indices() {
1821         let escaped = match c {
1822             '<' => "%3C",
1823             '>' => "%3E",
1824             ' ' => "%20",
1825             '?' => "%3F",
1826             '\'' => "%27",
1827             '&' => "%26",
1828             ',' => "%2C",
1829             ':' => "%3A",
1830             ';' => "%3B",
1831             '[' => "%5B",
1832             ']' => "%5D",
1833             '"' => "%22",
1834             _ => continue,
1835         };
1836
1837         st += &s[last_match..idx];
1838         st += escaped;
1839         // NOTE: we only expect single byte characters here - which is fine as long as we
1840         // only match single byte characters
1841         last_match = idx + 1;
1842     }
1843
1844     if last_match != 0 {
1845         st += &s[last_match..];
1846         st
1847     } else {
1848         s
1849     }
1850 }
1851
1852 fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
1853     let did = it.def_id.expect_real();
1854     if let Some(v) = cx.cache.impls.get(&did) {
1855         let mut used_links = FxHashSet::default();
1856         let cache = cx.cache();
1857
1858         {
1859             let used_links_bor = &mut used_links;
1860             let mut ret = v
1861                 .iter()
1862                 .filter(|i| i.inner_impl().trait_.is_none())
1863                 .flat_map(move |i| {
1864                     get_methods(i.inner_impl(), false, used_links_bor, false, &cx.cache)
1865                 })
1866                 .collect::<Vec<_>>();
1867             if !ret.is_empty() {
1868                 // We want links' order to be reproducible so we don't use unstable sort.
1869                 ret.sort();
1870
1871                 out.push_str(
1872                     "<a class=\"sidebar-title\" href=\"#implementations\">Methods</a>\
1873                      <div class=\"sidebar-links\">",
1874                 );
1875                 for line in ret {
1876                     out.push_str(&line);
1877                 }
1878                 out.push_str("</div>");
1879             }
1880         }
1881
1882         if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
1883             let format_impls = |impls: Vec<&Impl>| {
1884                 let mut links = FxHashSet::default();
1885
1886                 let mut ret = impls
1887                     .iter()
1888                     .filter_map(|it| {
1889                         if let Some(ref i) = it.inner_impl().trait_ {
1890                             let i_display = format!("{:#}", i.print(cx));
1891                             let out = Escape(&i_display);
1892                             let encoded = small_url_encode(format!("{:#}", i.print(cx)));
1893                             let generated = format!(
1894                                 "<a href=\"#impl-{}\">{}{}</a>",
1895                                 encoded,
1896                                 if it.inner_impl().negative_polarity { "!" } else { "" },
1897                                 out
1898                             );
1899                             if links.insert(generated.clone()) { Some(generated) } else { None }
1900                         } else {
1901                             None
1902                         }
1903                     })
1904                     .collect::<Vec<String>>();
1905                 ret.sort();
1906                 ret
1907             };
1908
1909             let write_sidebar_links = |out: &mut Buffer, links: Vec<String>| {
1910                 out.push_str("<div class=\"sidebar-links\">");
1911                 for link in links {
1912                     out.push_str(&link);
1913                 }
1914                 out.push_str("</div>");
1915             };
1916
1917             let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
1918                 v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().synthetic);
1919             let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) = concrete
1920                 .into_iter()
1921                 .partition::<Vec<_>, _>(|i| i.inner_impl().blanket_impl.is_some());
1922
1923             let concrete_format = format_impls(concrete);
1924             let synthetic_format = format_impls(synthetic);
1925             let blanket_format = format_impls(blanket_impl);
1926
1927             if !concrete_format.is_empty() {
1928                 out.push_str(
1929                     "<a class=\"sidebar-title\" href=\"#trait-implementations\">\
1930                         Trait Implementations</a>",
1931                 );
1932                 write_sidebar_links(out, concrete_format);
1933             }
1934
1935             if !synthetic_format.is_empty() {
1936                 out.push_str(
1937                     "<a class=\"sidebar-title\" href=\"#synthetic-implementations\">\
1938                         Auto Trait Implementations</a>",
1939                 );
1940                 write_sidebar_links(out, synthetic_format);
1941             }
1942
1943             if !blanket_format.is_empty() {
1944                 out.push_str(
1945                     "<a class=\"sidebar-title\" href=\"#blanket-implementations\">\
1946                         Blanket Implementations</a>",
1947                 );
1948                 write_sidebar_links(out, blanket_format);
1949             }
1950
1951             if let Some(impl_) = v
1952                 .iter()
1953                 .filter(|i| i.inner_impl().trait_.is_some())
1954                 .find(|i| i.inner_impl().trait_.def_id_full(cache) == cx.cache.deref_trait_did)
1955             {
1956                 sidebar_deref_methods(cx, out, impl_, v);
1957             }
1958         }
1959     }
1960 }
1961
1962 fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &Vec<Impl>) {
1963     let c = cx.cache();
1964
1965     debug!("found Deref: {:?}", impl_);
1966     if let Some((target, real_target)) =
1967         impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
1968             clean::TypedefItem(ref t, true) => Some(match *t {
1969                 clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
1970                 _ => (&t.type_, &t.type_),
1971             }),
1972             _ => None,
1973         })
1974     {
1975         debug!("found target, real_target: {:?} {:?}", target, real_target);
1976         if let Some(did) = target.def_id_full(c) {
1977             if let Some(type_did) = impl_.inner_impl().for_.def_id_full(c) {
1978                 // `impl Deref<Target = S> for S`
1979                 if did == type_did {
1980                     // Avoid infinite cycles
1981                     return;
1982                 }
1983             }
1984         }
1985         let deref_mut = v
1986             .iter()
1987             .filter(|i| i.inner_impl().trait_.is_some())
1988             .any(|i| i.inner_impl().trait_.def_id_full(c) == c.deref_mut_trait_did);
1989         let inner_impl = target
1990             .def_id_full(c)
1991             .or_else(|| {
1992                 target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
1993             })
1994             .and_then(|did| c.impls.get(&did));
1995         if let Some(impls) = inner_impl {
1996             debug!("found inner_impl: {:?}", impls);
1997             let mut used_links = FxHashSet::default();
1998             let mut ret = impls
1999                 .iter()
2000                 .filter(|i| i.inner_impl().trait_.is_none())
2001                 .flat_map(|i| get_methods(i.inner_impl(), true, &mut used_links, deref_mut, c))
2002                 .collect::<Vec<_>>();
2003             if !ret.is_empty() {
2004                 let deref_id_map = cx.deref_id_map.borrow();
2005                 let id = deref_id_map
2006                     .get(&real_target.def_id_full(c).unwrap())
2007                     .expect("Deref section without derived id");
2008                 write!(
2009                     out,
2010                     "<a class=\"sidebar-title\" href=\"#{}\">Methods from {}&lt;Target={}&gt;</a>",
2011                     id,
2012                     Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))),
2013                     Escape(&format!("{:#}", real_target.print(cx))),
2014                 );
2015                 // We want links' order to be reproducible so we don't use unstable sort.
2016                 ret.sort();
2017                 out.push_str("<div class=\"sidebar-links\">");
2018                 for link in ret {
2019                     out.push_str(&link);
2020                 }
2021                 out.push_str("</div>");
2022             }
2023         }
2024
2025         // Recurse into any further impls that might exist for `target`
2026         if let Some(target_did) = target.def_id_full(c) {
2027             if let Some(target_impls) = c.impls.get(&target_did) {
2028                 if let Some(target_deref_impl) = target_impls
2029                     .iter()
2030                     .filter(|i| i.inner_impl().trait_.is_some())
2031                     .find(|i| i.inner_impl().trait_.def_id_full(c) == c.deref_trait_did)
2032                 {
2033                     sidebar_deref_methods(cx, out, target_deref_impl, target_impls);
2034                 }
2035             }
2036         }
2037     }
2038 }
2039
2040 fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) {
2041     let mut sidebar = Buffer::new();
2042     let fields = get_struct_fields_name(&s.fields);
2043
2044     if !fields.is_empty() {
2045         if let CtorKind::Fictive = s.struct_type {
2046             sidebar.push_str(
2047                 "<a class=\"sidebar-title\" href=\"#fields\">Fields</a>\
2048                 <div class=\"sidebar-links\">",
2049             );
2050
2051             for field in fields {
2052                 sidebar.push_str(&field);
2053             }
2054
2055             sidebar.push_str("</div>");
2056         }
2057     }
2058
2059     sidebar_assoc_items(cx, &mut sidebar, it);
2060
2061     if !sidebar.is_empty() {
2062         write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
2063     }
2064 }
2065
2066 fn get_id_for_impl_on_foreign_type(
2067     for_: &clean::Type,
2068     trait_: &clean::Type,
2069     cx: &Context<'_>,
2070 ) -> String {
2071     small_url_encode(format!("impl-{:#}-for-{:#}", trait_.print(cx), for_.print(cx),))
2072 }
2073
2074 fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2075     match *item.kind {
2076         clean::ItemKind::ImplItem(ref i) => {
2077             if let Some(ref trait_) = i.trait_ {
2078                 // Alternative format produces no URLs,
2079                 // so this parameter does nothing.
2080                 Some((
2081                     format!("{:#}", i.for_.print(cx)),
2082                     get_id_for_impl_on_foreign_type(&i.for_, trait_, cx),
2083                 ))
2084             } else {
2085                 None
2086             }
2087         }
2088         _ => None,
2089     }
2090 }
2091
2092 fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) {
2093     buf.write_str("<div class=\"block items\">");
2094
2095     fn print_sidebar_section(
2096         out: &mut Buffer,
2097         items: &[clean::Item],
2098         before: &str,
2099         filter: impl Fn(&clean::Item) -> bool,
2100         write: impl Fn(&mut Buffer, &str),
2101         after: &str,
2102     ) {
2103         let mut items = items
2104             .iter()
2105             .filter_map(|m| match m.name {
2106                 Some(ref name) if filter(m) => Some(name.as_str()),
2107                 _ => None,
2108             })
2109             .collect::<Vec<_>>();
2110
2111         if !items.is_empty() {
2112             items.sort_unstable();
2113             out.push_str(before);
2114             for item in items.into_iter() {
2115                 write(out, &item);
2116             }
2117             out.push_str(after);
2118         }
2119     }
2120
2121     print_sidebar_section(
2122         buf,
2123         &t.items,
2124         "<a class=\"sidebar-title\" href=\"#associated-types\">\
2125             Associated Types</a><div class=\"sidebar-links\">",
2126         |m| m.is_associated_type(),
2127         |out, sym| write!(out, "<a href=\"#associatedtype.{0}\">{0}</a>", sym),
2128         "</div>",
2129     );
2130
2131     print_sidebar_section(
2132         buf,
2133         &t.items,
2134         "<a class=\"sidebar-title\" href=\"#associated-const\">\
2135             Associated Constants</a><div class=\"sidebar-links\">",
2136         |m| m.is_associated_const(),
2137         |out, sym| write!(out, "<a href=\"#associatedconstant.{0}\">{0}</a>", sym),
2138         "</div>",
2139     );
2140
2141     print_sidebar_section(
2142         buf,
2143         &t.items,
2144         "<a class=\"sidebar-title\" href=\"#required-methods\">\
2145             Required Methods</a><div class=\"sidebar-links\">",
2146         |m| m.is_ty_method(),
2147         |out, sym| write!(out, "<a href=\"#tymethod.{0}\">{0}</a>", sym),
2148         "</div>",
2149     );
2150
2151     print_sidebar_section(
2152         buf,
2153         &t.items,
2154         "<a class=\"sidebar-title\" href=\"#provided-methods\">\
2155             Provided Methods</a><div class=\"sidebar-links\">",
2156         |m| m.is_method(),
2157         |out, sym| write!(out, "<a href=\"#method.{0}\">{0}</a>", sym),
2158         "</div>",
2159     );
2160
2161     if let Some(implementors) = cx.cache.implementors.get(&it.def_id.expect_real()) {
2162         let cache = cx.cache();
2163         let mut res = implementors
2164             .iter()
2165             .filter(|i| {
2166                 i.inner_impl()
2167                     .for_
2168                     .def_id_full(cache)
2169                     .map_or(false, |d| !cx.cache.paths.contains_key(&d))
2170             })
2171             .filter_map(|i| extract_for_impl_name(&i.impl_item, cx))
2172             .collect::<Vec<_>>();
2173
2174         if !res.is_empty() {
2175             res.sort();
2176             buf.push_str(
2177                 "<a class=\"sidebar-title\" href=\"#foreign-impls\">\
2178                     Implementations on Foreign Types</a>\
2179                  <div class=\"sidebar-links\">",
2180             );
2181             for (name, id) in res.into_iter() {
2182                 write!(buf, "<a href=\"#{}\">{}</a>", id, Escape(&name));
2183             }
2184             buf.push_str("</div>");
2185         }
2186     }
2187
2188     sidebar_assoc_items(cx, buf, it);
2189
2190     buf.push_str("<a class=\"sidebar-title\" href=\"#implementors\">Implementors</a>");
2191     if t.is_auto {
2192         buf.push_str(
2193             "<a class=\"sidebar-title\" \
2194                 href=\"#synthetic-implementors\">Auto Implementors</a>",
2195         );
2196     }
2197
2198     buf.push_str("</div>")
2199 }
2200
2201 fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
2202     let mut sidebar = Buffer::new();
2203     sidebar_assoc_items(cx, &mut sidebar, it);
2204
2205     if !sidebar.is_empty() {
2206         write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
2207     }
2208 }
2209
2210 fn sidebar_typedef(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
2211     let mut sidebar = Buffer::new();
2212     sidebar_assoc_items(cx, &mut sidebar, it);
2213
2214     if !sidebar.is_empty() {
2215         write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
2216     }
2217 }
2218
2219 fn get_struct_fields_name(fields: &[clean::Item]) -> Vec<String> {
2220     let mut fields = fields
2221         .iter()
2222         .filter(|f| matches!(*f.kind, clean::StructFieldItem(..)))
2223         .filter_map(|f| {
2224             f.name.map(|name| format!("<a href=\"#structfield.{name}\">{name}</a>", name = name))
2225         })
2226         .collect::<Vec<_>>();
2227     fields.sort();
2228     fields
2229 }
2230
2231 fn sidebar_union(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, u: &clean::Union) {
2232     let mut sidebar = Buffer::new();
2233     let fields = get_struct_fields_name(&u.fields);
2234
2235     if !fields.is_empty() {
2236         sidebar.push_str(
2237             "<a class=\"sidebar-title\" href=\"#fields\">Fields</a>\
2238             <div class=\"sidebar-links\">",
2239         );
2240
2241         for field in fields {
2242             sidebar.push_str(&field);
2243         }
2244
2245         sidebar.push_str("</div>");
2246     }
2247
2248     sidebar_assoc_items(cx, &mut sidebar, it);
2249
2250     if !sidebar.is_empty() {
2251         write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
2252     }
2253 }
2254
2255 fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) {
2256     let mut sidebar = Buffer::new();
2257
2258     let mut variants = e
2259         .variants
2260         .iter()
2261         .filter_map(|v| match v.name {
2262             Some(ref name) => Some(format!("<a href=\"#variant.{name}\">{name}</a>", name = name)),
2263             _ => None,
2264         })
2265         .collect::<Vec<_>>();
2266     if !variants.is_empty() {
2267         variants.sort_unstable();
2268         sidebar.push_str(&format!(
2269             "<a class=\"sidebar-title\" href=\"#variants\">Variants</a>\
2270              <div class=\"sidebar-links\">{}</div>",
2271             variants.join(""),
2272         ));
2273     }
2274
2275     sidebar_assoc_items(cx, &mut sidebar, it);
2276
2277     if !sidebar.is_empty() {
2278         write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
2279     }
2280 }
2281
2282 fn item_ty_to_strs(ty: &ItemType) -> (&'static str, &'static str) {
2283     match *ty {
2284         ItemType::ExternCrate | ItemType::Import => ("reexports", "Re-exports"),
2285         ItemType::Module => ("modules", "Modules"),
2286         ItemType::Struct => ("structs", "Structs"),
2287         ItemType::Union => ("unions", "Unions"),
2288         ItemType::Enum => ("enums", "Enums"),
2289         ItemType::Function => ("functions", "Functions"),
2290         ItemType::Typedef => ("types", "Type Definitions"),
2291         ItemType::Static => ("statics", "Statics"),
2292         ItemType::Constant => ("constants", "Constants"),
2293         ItemType::Trait => ("traits", "Traits"),
2294         ItemType::Impl => ("impls", "Implementations"),
2295         ItemType::TyMethod => ("tymethods", "Type Methods"),
2296         ItemType::Method => ("methods", "Methods"),
2297         ItemType::StructField => ("fields", "Struct Fields"),
2298         ItemType::Variant => ("variants", "Variants"),
2299         ItemType::Macro => ("macros", "Macros"),
2300         ItemType::Primitive => ("primitives", "Primitive Types"),
2301         ItemType::AssocType => ("associated-types", "Associated Types"),
2302         ItemType::AssocConst => ("associated-consts", "Associated Constants"),
2303         ItemType::ForeignType => ("foreign-types", "Foreign Types"),
2304         ItemType::Keyword => ("keywords", "Keywords"),
2305         ItemType::OpaqueTy => ("opaque-types", "Opaque Types"),
2306         ItemType::ProcAttribute => ("attributes", "Attribute Macros"),
2307         ItemType::ProcDerive => ("derives", "Derive Macros"),
2308         ItemType::TraitAlias => ("trait-aliases", "Trait aliases"),
2309     }
2310 }
2311
2312 fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) {
2313     let mut sidebar = String::new();
2314
2315     if items.iter().any(|it| {
2316         it.type_() == ItemType::ExternCrate || (it.type_() == ItemType::Import && !it.is_stripped())
2317     }) {
2318         sidebar.push_str("<li><a href=\"#reexports\">Re-exports</a></li>");
2319     }
2320
2321     // ordering taken from item_module, reorder, where it prioritized elements in a certain order
2322     // to print its headings
2323     for &myty in &[
2324         ItemType::Primitive,
2325         ItemType::Module,
2326         ItemType::Macro,
2327         ItemType::Struct,
2328         ItemType::Enum,
2329         ItemType::Constant,
2330         ItemType::Static,
2331         ItemType::Trait,
2332         ItemType::Function,
2333         ItemType::Typedef,
2334         ItemType::Union,
2335         ItemType::Impl,
2336         ItemType::TyMethod,
2337         ItemType::Method,
2338         ItemType::StructField,
2339         ItemType::Variant,
2340         ItemType::AssocType,
2341         ItemType::AssocConst,
2342         ItemType::ForeignType,
2343         ItemType::Keyword,
2344     ] {
2345         if items.iter().any(|it| !it.is_stripped() && it.type_() == myty) {
2346             let (short, name) = item_ty_to_strs(&myty);
2347             sidebar.push_str(&format!(
2348                 "<li><a href=\"#{id}\">{name}</a></li>",
2349                 id = short,
2350                 name = name
2351             ));
2352         }
2353     }
2354
2355     if !sidebar.is_empty() {
2356         write!(buf, "<div class=\"block items\"><ul>{}</ul></div>", sidebar);
2357     }
2358 }
2359
2360 fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
2361     let mut sidebar = Buffer::new();
2362     sidebar_assoc_items(cx, &mut sidebar, it);
2363
2364     if !sidebar.is_empty() {
2365         write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
2366     }
2367 }
2368
2369 crate const BASIC_KEYWORDS: &str = "rust, rustlang, rust-lang";
2370
2371 /// Returns a list of all paths used in the type.
2372 /// This is used to help deduplicate imported impls
2373 /// for reexported types. If any of the contained
2374 /// types are re-exported, we don't use the corresponding
2375 /// entry from the js file, as inlining will have already
2376 /// picked up the impl
2377 fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
2378     let mut out = Vec::new();
2379     let mut visited = FxHashSet::default();
2380     let mut work = VecDeque::new();
2381
2382     work.push_back(first_ty);
2383
2384     while let Some(ty) = work.pop_front() {
2385         if !visited.insert(ty.clone()) {
2386             continue;
2387         }
2388
2389         match ty {
2390             clean::Type::ResolvedPath { did, .. } => {
2391                 let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone());
2392                 let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern);
2393
2394                 if let Some(path) = fqp {
2395                     out.push(path.join("::"));
2396                 }
2397             }
2398             clean::Type::Tuple(tys) => {
2399                 work.extend(tys.into_iter());
2400             }
2401             clean::Type::Slice(ty) => {
2402                 work.push_back(*ty);
2403             }
2404             clean::Type::Array(ty, _) => {
2405                 work.push_back(*ty);
2406             }
2407             clean::Type::RawPointer(_, ty) => {
2408                 work.push_back(*ty);
2409             }
2410             clean::Type::BorrowedRef { type_, .. } => {
2411                 work.push_back(*type_);
2412             }
2413             clean::Type::QPath { self_type, trait_, .. } => {
2414                 work.push_back(*self_type);
2415                 work.push_back(*trait_);
2416             }
2417             _ => {}
2418         }
2419     }
2420     out
2421 }