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