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