]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/render/mod.rs
Rollup merge of #98811 - RalfJung:interpret-alloc-range, r=oli-obk
[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         write!(w, " = ");
720
721         // FIXME: `.value()` uses `clean::utils::format_integer_with_underscore_sep` under the
722         //        hood which adds noisy underscores and a type suffix to number literals.
723         //        This hurts readability in this context especially when more complex expressions
724         //        are involved and it doesn't add much of value.
725         //        Find a way to print constants here without all that jazz.
726         write!(w, "{}", Escape(&default.value(cx.tcx()).unwrap_or_else(|| default.expr(cx.tcx()))));
727     }
728 }
729
730 fn assoc_type(
731     w: &mut Buffer,
732     it: &clean::Item,
733     generics: &clean::Generics,
734     bounds: &[clean::GenericBound],
735     default: Option<&clean::Type>,
736     link: AssocItemLink<'_>,
737     indent: usize,
738     cx: &Context<'_>,
739 ) {
740     write!(
741         w,
742         "{indent}type <a{href} class=\"associatedtype\">{name}</a>{generics}",
743         indent = " ".repeat(indent),
744         href = assoc_href_attr(it, link, cx),
745         name = it.name.as_ref().unwrap(),
746         generics = generics.print(cx),
747     );
748     if !bounds.is_empty() {
749         write!(w, ": {}", print_generic_bounds(bounds, cx))
750     }
751     write!(w, "{}", print_where_clause(generics, cx, indent, false));
752     if let Some(default) = default {
753         write!(w, " = {}", default.print(cx))
754     }
755 }
756
757 fn assoc_method(
758     w: &mut Buffer,
759     meth: &clean::Item,
760     g: &clean::Generics,
761     d: &clean::FnDecl,
762     link: AssocItemLink<'_>,
763     parent: ItemType,
764     cx: &Context<'_>,
765     render_mode: RenderMode,
766 ) {
767     let header = meth.fn_header(cx.tcx()).expect("Trying to get header from a non-function item");
768     let name = meth.name.as_ref().unwrap();
769     let vis = meth.visibility.print_with_space(meth.item_id, cx).to_string();
770     // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove
771     // this condition.
772     let constness = match render_mode {
773         RenderMode::Normal => {
774             print_constness_with_space(&header.constness, meth.const_stability(cx.tcx()))
775         }
776         RenderMode::ForDeref { .. } => "",
777     };
778     let asyncness = header.asyncness.print_with_space();
779     let unsafety = header.unsafety.print_with_space();
780     let defaultness = print_default_space(meth.is_default());
781     let abi = print_abi_with_space(header.abi).to_string();
782     let href = assoc_href_attr(meth, link, cx);
783
784     // NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`.
785     let generics_len = format!("{:#}", g.print(cx)).len();
786     let mut header_len = "fn ".len()
787         + vis.len()
788         + constness.len()
789         + asyncness.len()
790         + unsafety.len()
791         + defaultness.len()
792         + abi.len()
793         + name.as_str().len()
794         + generics_len;
795
796     let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
797         header_len += 4;
798         let indent_str = "    ";
799         render_attributes_in_pre(w, meth, indent_str);
800         (4, indent_str, false)
801     } else {
802         render_attributes_in_code(w, meth);
803         (0, "", true)
804     };
805     w.reserve(header_len + "<a href=\"\" class=\"fnname\">{".len() + "</a>".len());
806     write!(
807         w,
808         "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn <a{href} class=\"fnname\">{name}</a>\
809          {generics}{decl}{notable_traits}{where_clause}",
810         indent = indent_str,
811         vis = vis,
812         constness = constness,
813         asyncness = asyncness,
814         unsafety = unsafety,
815         defaultness = defaultness,
816         abi = abi,
817         href = href,
818         name = name,
819         generics = g.print(cx),
820         decl = d.full_print(header_len, indent, header.asyncness, cx),
821         notable_traits = notable_traits_decl(d, cx),
822         where_clause = print_where_clause(g, cx, indent, end_newline),
823     )
824 }
825
826 /// Writes a span containing the versions at which an item became stable and/or const-stable. For
827 /// example, if the item became stable at 1.0.0, and const-stable at 1.45.0, this function would
828 /// write a span containing "1.0.0 (const: 1.45.0)".
829 ///
830 /// Returns `true` if a stability annotation was rendered.
831 ///
832 /// Stability and const-stability are considered separately. If the item is unstable, no version
833 /// will be written. If the item is const-unstable, "const: unstable" will be appended to the
834 /// span, with a link to the tracking issue if present. If an item's stability or const-stability
835 /// version matches the version of its enclosing item, that version will be omitted.
836 ///
837 /// Note that it is possible for an unstable function to be const-stable. In that case, the span
838 /// will include the const-stable version, but no stable version will be emitted, as a natural
839 /// consequence of the above rules.
840 fn render_stability_since_raw(
841     w: &mut Buffer,
842     ver: Option<Symbol>,
843     const_stability: Option<ConstStability>,
844     containing_ver: Option<Symbol>,
845     containing_const_ver: Option<Symbol>,
846 ) -> bool {
847     let stable_version = ver.filter(|inner| !inner.is_empty() && Some(*inner) != containing_ver);
848
849     let mut title = String::new();
850     let mut stability = String::new();
851
852     if let Some(ver) = stable_version {
853         stability.push_str(ver.as_str());
854         title.push_str(&format!("Stable since Rust version {}", ver));
855     }
856
857     let const_title_and_stability = match const_stability {
858         Some(ConstStability { level: StabilityLevel::Stable { since }, .. })
859             if Some(since) != containing_const_ver =>
860         {
861             Some((format!("const since {}", since), format!("const: {}", since)))
862         }
863         Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => {
864             let unstable = if let Some(n) = issue {
865                 format!(
866                     r#"<a href="https://github.com/rust-lang/rust/issues/{}" title="Tracking issue for {}">unstable</a>"#,
867                     n, feature
868                 )
869             } else {
870                 String::from("unstable")
871             };
872
873             Some((String::from("const unstable"), format!("const: {}", unstable)))
874         }
875         _ => None,
876     };
877
878     if let Some((const_title, const_stability)) = const_title_and_stability {
879         if !title.is_empty() {
880             title.push_str(&format!(", {}", const_title));
881         } else {
882             title.push_str(&const_title);
883         }
884
885         if !stability.is_empty() {
886             stability.push_str(&format!(" ({})", const_stability));
887         } else {
888             stability.push_str(&const_stability);
889         }
890     }
891
892     if !stability.is_empty() {
893         write!(w, r#"<span class="since" title="{}">{}</span>"#, title, stability);
894     }
895
896     !stability.is_empty()
897 }
898
899 fn render_assoc_item(
900     w: &mut Buffer,
901     item: &clean::Item,
902     link: AssocItemLink<'_>,
903     parent: ItemType,
904     cx: &Context<'_>,
905     render_mode: RenderMode,
906 ) {
907     match &*item.kind {
908         clean::StrippedItem(..) => {}
909         clean::TyMethodItem(m) => {
910             assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode)
911         }
912         clean::MethodItem(m, _) => {
913             assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode)
914         }
915         kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => assoc_const(
916             w,
917             item,
918             ty,
919             match kind {
920                 clean::TyAssocConstItem(_) => None,
921                 clean::AssocConstItem(_, default) => Some(default),
922                 _ => unreachable!(),
923             },
924             link,
925             if parent == ItemType::Trait { "    " } else { "" },
926             cx,
927         ),
928         clean::TyAssocTypeItem(ref generics, ref bounds) => assoc_type(
929             w,
930             item,
931             generics,
932             bounds,
933             None,
934             link,
935             if parent == ItemType::Trait { 4 } else { 0 },
936             cx,
937         ),
938         clean::AssocTypeItem(ref ty, ref bounds) => assoc_type(
939             w,
940             item,
941             &ty.generics,
942             bounds,
943             Some(ty.item_type.as_ref().unwrap_or(&ty.type_)),
944             link,
945             if parent == ItemType::Trait { 4 } else { 0 },
946             cx,
947         ),
948         _ => panic!("render_assoc_item called on non-associated-item"),
949     }
950 }
951
952 const ALLOWED_ATTRIBUTES: &[Symbol] =
953     &[sym::export_name, sym::link_section, sym::no_mangle, sym::repr, sym::non_exhaustive];
954
955 fn attributes(it: &clean::Item) -> Vec<String> {
956     it.attrs
957         .other_attrs
958         .iter()
959         .filter_map(|attr| {
960             if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) {
961                 Some(
962                     pprust::attribute_to_string(attr)
963                         .replace("\\\n", "")
964                         .replace('\n', "")
965                         .replace("  ", " "),
966                 )
967             } else {
968                 None
969             }
970         })
971         .collect()
972 }
973
974 // When an attribute is rendered inside a `<pre>` tag, it is formatted using
975 // a whitespace prefix and newline.
976 fn render_attributes_in_pre(w: &mut Buffer, it: &clean::Item, prefix: &str) {
977     for a in attributes(it) {
978         writeln!(w, "{}{}", prefix, a);
979     }
980 }
981
982 // When an attribute is rendered inside a <code> tag, it is formatted using
983 // a div to produce a newline after it.
984 fn render_attributes_in_code(w: &mut Buffer, it: &clean::Item) {
985     for a in attributes(it) {
986         write!(w, "<div class=\"code-attribute\">{}</div>", a);
987     }
988 }
989
990 #[derive(Copy, Clone)]
991 enum AssocItemLink<'a> {
992     Anchor(Option<&'a str>),
993     GotoSource(ItemId, &'a FxHashSet<Symbol>),
994 }
995
996 impl<'a> AssocItemLink<'a> {
997     fn anchor(&self, id: &'a str) -> Self {
998         match *self {
999             AssocItemLink::Anchor(_) => AssocItemLink::Anchor(Some(id)),
1000             ref other => *other,
1001         }
1002     }
1003 }
1004
1005 fn render_assoc_items(
1006     w: &mut Buffer,
1007     cx: &mut Context<'_>,
1008     containing_item: &clean::Item,
1009     it: DefId,
1010     what: AssocItemRender<'_>,
1011 ) {
1012     let mut derefs = FxHashSet::default();
1013     derefs.insert(it);
1014     render_assoc_items_inner(w, cx, containing_item, it, what, &mut derefs)
1015 }
1016
1017 fn render_assoc_items_inner(
1018     w: &mut Buffer,
1019     cx: &mut Context<'_>,
1020     containing_item: &clean::Item,
1021     it: DefId,
1022     what: AssocItemRender<'_>,
1023     derefs: &mut FxHashSet<DefId>,
1024 ) {
1025     info!("Documenting associated items of {:?}", containing_item.name);
1026     let shared = Rc::clone(&cx.shared);
1027     let cache = &shared.cache;
1028     let Some(v) = cache.impls.get(&it) else { return };
1029     let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
1030     if !non_trait.is_empty() {
1031         let mut tmp_buf = Buffer::empty_from(w);
1032         let (render_mode, id) = match what {
1033             AssocItemRender::All => {
1034                 tmp_buf.write_str(
1035                     "<h2 id=\"implementations\" class=\"small-section-header\">\
1036                          Implementations\
1037                          <a href=\"#implementations\" class=\"anchor\"></a>\
1038                      </h2>",
1039                 );
1040                 (RenderMode::Normal, "implementations-list".to_owned())
1041             }
1042             AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
1043                 let id =
1044                     cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
1045                 if let Some(def_id) = type_.def_id(cx.cache()) {
1046                     cx.deref_id_map.insert(def_id, id.clone());
1047                 }
1048                 write!(
1049                     tmp_buf,
1050                     "<h2 id=\"{id}\" class=\"small-section-header\">\
1051                          <span>Methods from {trait_}&lt;Target = {type_}&gt;</span>\
1052                          <a href=\"#{id}\" class=\"anchor\"></a>\
1053                      </h2>",
1054                     id = id,
1055                     trait_ = trait_.print(cx),
1056                     type_ = type_.print(cx),
1057                 );
1058                 (RenderMode::ForDeref { mut_: deref_mut_ }, cx.derive_id(id))
1059             }
1060         };
1061         let mut impls_buf = Buffer::empty_from(w);
1062         for i in &non_trait {
1063             render_impl(
1064                 &mut impls_buf,
1065                 cx,
1066                 i,
1067                 containing_item,
1068                 AssocItemLink::Anchor(None),
1069                 render_mode,
1070                 None,
1071                 &[],
1072                 ImplRenderingParameters {
1073                     show_def_docs: true,
1074                     is_on_foreign_type: false,
1075                     show_default_items: true,
1076                     show_non_assoc_items: true,
1077                     toggle_open_by_default: true,
1078                 },
1079             );
1080         }
1081         if !impls_buf.is_empty() {
1082             w.push_buffer(tmp_buf);
1083             write!(w, "<div id=\"{}\">", id);
1084             w.push_buffer(impls_buf);
1085             w.write_str("</div>");
1086         }
1087     }
1088
1089     if !traits.is_empty() {
1090         let deref_impl =
1091             traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait());
1092         if let Some(impl_) = deref_impl {
1093             let has_deref_mut =
1094                 traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
1095             render_deref_methods(w, cx, impl_, containing_item, has_deref_mut, derefs);
1096         }
1097
1098         // If we were already one level into rendering deref methods, we don't want to render
1099         // anything after recursing into any further deref methods above.
1100         if let AssocItemRender::DerefFor { .. } = what {
1101             return;
1102         }
1103
1104         let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) =
1105             traits.iter().partition(|t| t.inner_impl().kind.is_auto());
1106         let (blanket_impl, concrete): (Vec<&&Impl>, _) =
1107             concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
1108
1109         let mut impls = Buffer::empty_from(w);
1110         render_impls(cx, &mut impls, &concrete, containing_item, true);
1111         let impls = impls.into_inner();
1112         if !impls.is_empty() {
1113             write!(
1114                 w,
1115                 "<h2 id=\"trait-implementations\" class=\"small-section-header\">\
1116                      Trait Implementations\
1117                      <a href=\"#trait-implementations\" class=\"anchor\"></a>\
1118                  </h2>\
1119                  <div id=\"trait-implementations-list\">{}</div>",
1120                 impls
1121             );
1122         }
1123
1124         if !synthetic.is_empty() {
1125             w.write_str(
1126                 "<h2 id=\"synthetic-implementations\" class=\"small-section-header\">\
1127                      Auto Trait Implementations\
1128                      <a href=\"#synthetic-implementations\" class=\"anchor\"></a>\
1129                  </h2>\
1130                  <div id=\"synthetic-implementations-list\">",
1131             );
1132             render_impls(cx, w, &synthetic, containing_item, false);
1133             w.write_str("</div>");
1134         }
1135
1136         if !blanket_impl.is_empty() {
1137             w.write_str(
1138                 "<h2 id=\"blanket-implementations\" class=\"small-section-header\">\
1139                      Blanket Implementations\
1140                      <a href=\"#blanket-implementations\" class=\"anchor\"></a>\
1141                  </h2>\
1142                  <div id=\"blanket-implementations-list\">",
1143             );
1144             render_impls(cx, w, &blanket_impl, containing_item, false);
1145             w.write_str("</div>");
1146         }
1147     }
1148 }
1149
1150 fn render_deref_methods(
1151     w: &mut Buffer,
1152     cx: &mut Context<'_>,
1153     impl_: &Impl,
1154     container_item: &clean::Item,
1155     deref_mut: bool,
1156     derefs: &mut FxHashSet<DefId>,
1157 ) {
1158     let cache = cx.cache();
1159     let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
1160     let (target, real_target) = impl_
1161         .inner_impl()
1162         .items
1163         .iter()
1164         .find_map(|item| match *item.kind {
1165             clean::AssocTypeItem(ref t, _) => Some(match *t {
1166                 clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
1167                 _ => (&t.type_, &t.type_),
1168             }),
1169             _ => None,
1170         })
1171         .expect("Expected associated type binding");
1172     debug!("Render deref methods for {:#?}, target {:#?}", impl_.inner_impl().for_, target);
1173     let what =
1174         AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
1175     if let Some(did) = target.def_id(cache) {
1176         if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) {
1177             // `impl Deref<Target = S> for S`
1178             if did == type_did || !derefs.insert(did) {
1179                 // Avoid infinite cycles
1180                 return;
1181             }
1182         }
1183         render_assoc_items_inner(w, cx, container_item, did, what, derefs);
1184     } else if let Some(prim) = target.primitive_type() {
1185         if let Some(&did) = cache.primitive_locations.get(&prim) {
1186             render_assoc_items_inner(w, cx, container_item, did, what, derefs);
1187         }
1188     }
1189 }
1190
1191 fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool {
1192     let self_type_opt = match *item.kind {
1193         clean::MethodItem(ref method, _) => method.decl.self_type(),
1194         clean::TyMethodItem(ref method) => method.decl.self_type(),
1195         _ => None,
1196     };
1197
1198     if let Some(self_ty) = self_type_opt {
1199         let (by_mut_ref, by_box, by_value) = match self_ty {
1200             SelfTy::SelfBorrowed(_, mutability)
1201             | SelfTy::SelfExplicit(clean::BorrowedRef { mutability, .. }) => {
1202                 (mutability == Mutability::Mut, false, false)
1203             }
1204             SelfTy::SelfExplicit(clean::Type::Path { path }) => {
1205                 (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false)
1206             }
1207             SelfTy::SelfValue => (false, false, true),
1208             _ => (false, false, false),
1209         };
1210
1211         (deref_mut_ || !by_mut_ref) && !by_box && !by_value
1212     } else {
1213         false
1214     }
1215 }
1216
1217 fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String {
1218     let mut out = Buffer::html();
1219
1220     if let Some((did, ty)) = decl.output.as_return().and_then(|t| Some((t.def_id(cx.cache())?, t)))
1221     {
1222         if let Some(impls) = cx.cache().impls.get(&did) {
1223             for i in impls {
1224                 let impl_ = i.inner_impl();
1225                 if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache())
1226                 {
1227                     // Two different types might have the same did,
1228                     // without actually being the same.
1229                     continue;
1230                 }
1231                 if let Some(trait_) = &impl_.trait_ {
1232                     let trait_did = trait_.def_id();
1233
1234                     if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable) {
1235                         if out.is_empty() {
1236                             write!(
1237                                 &mut out,
1238                                 "<span class=\"notable\">Notable traits for {}</span>\
1239                              <code class=\"content\">",
1240                                 impl_.for_.print(cx)
1241                             );
1242                         }
1243
1244                         //use the "where" class here to make it small
1245                         write!(
1246                             &mut out,
1247                             "<span class=\"where fmt-newline\">{}</span>",
1248                             impl_.print(false, cx)
1249                         );
1250                         for it in &impl_.items {
1251                             if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
1252                                 out.push_str("<span class=\"where fmt-newline\">    ");
1253                                 let empty_set = FxHashSet::default();
1254                                 let src_link =
1255                                     AssocItemLink::GotoSource(trait_did.into(), &empty_set);
1256                                 assoc_type(
1257                                     &mut out,
1258                                     it,
1259                                     &tydef.generics,
1260                                     &[], // intentionally leaving out bounds
1261                                     Some(&tydef.type_),
1262                                     src_link,
1263                                     0,
1264                                     cx,
1265                                 );
1266                                 out.push_str(";</span>");
1267                             }
1268                         }
1269                     }
1270                 }
1271             }
1272         }
1273     }
1274
1275     if !out.is_empty() {
1276         out.insert_str(
1277             0,
1278             "<span class=\"notable-traits\"><span class=\"notable-traits-tooltip\">ⓘ\
1279             <span class=\"notable-traits-tooltiptext\"><span class=\"docblock\">",
1280         );
1281         out.push_str("</code></span></span></span></span>");
1282     }
1283
1284     out.into_inner()
1285 }
1286
1287 #[derive(Clone, Copy, Debug)]
1288 struct ImplRenderingParameters {
1289     show_def_docs: bool,
1290     is_on_foreign_type: bool,
1291     show_default_items: bool,
1292     /// Whether or not to show methods.
1293     show_non_assoc_items: bool,
1294     toggle_open_by_default: bool,
1295 }
1296
1297 fn render_impl(
1298     w: &mut Buffer,
1299     cx: &mut Context<'_>,
1300     i: &Impl,
1301     parent: &clean::Item,
1302     link: AssocItemLink<'_>,
1303     render_mode: RenderMode,
1304     use_absolute: Option<bool>,
1305     aliases: &[String],
1306     rendering_params: ImplRenderingParameters,
1307 ) {
1308     let shared = Rc::clone(&cx.shared);
1309     let cache = &shared.cache;
1310     let traits = &cache.traits;
1311     let trait_ = i.trait_did().map(|did| &traits[&did]);
1312     let mut close_tags = String::new();
1313
1314     // For trait implementations, the `interesting` output contains all methods that have doc
1315     // comments, and the `boring` output contains all methods that do not. The distinction is
1316     // used to allow hiding the boring methods.
1317     // `containing_item` is used for rendering stability info. If the parent is a trait impl,
1318     // `containing_item` will the grandparent, since trait impls can't have stability attached.
1319     fn doc_impl_item(
1320         boring: &mut Buffer,
1321         interesting: &mut Buffer,
1322         cx: &mut Context<'_>,
1323         item: &clean::Item,
1324         parent: &clean::Item,
1325         containing_item: &clean::Item,
1326         link: AssocItemLink<'_>,
1327         render_mode: RenderMode,
1328         is_default_item: bool,
1329         trait_: Option<&clean::Trait>,
1330         rendering_params: ImplRenderingParameters,
1331     ) {
1332         let item_type = item.type_();
1333         let name = item.name.as_ref().unwrap();
1334
1335         let render_method_item = rendering_params.show_non_assoc_items
1336             && match render_mode {
1337                 RenderMode::Normal => true,
1338                 RenderMode::ForDeref { mut_: deref_mut_ } => {
1339                     should_render_item(item, deref_mut_, cx.tcx())
1340                 }
1341             };
1342
1343         let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
1344
1345         let mut doc_buffer = Buffer::empty_from(boring);
1346         let mut info_buffer = Buffer::empty_from(boring);
1347         let mut short_documented = true;
1348
1349         if render_method_item {
1350             if !is_default_item {
1351                 if let Some(t) = trait_ {
1352                     // The trait item may have been stripped so we might not
1353                     // find any documentation or stability for it.
1354                     if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1355                         // We need the stability of the item from the trait
1356                         // because impls can't have a stability.
1357                         if item.doc_value().is_some() {
1358                             document_item_info(&mut info_buffer, cx, it, Some(parent));
1359                             document_full(&mut doc_buffer, item, cx, HeadingOffset::H5);
1360                             short_documented = false;
1361                         } else {
1362                             // In case the item isn't documented,
1363                             // provide short documentation from the trait.
1364                             document_short(
1365                                 &mut doc_buffer,
1366                                 it,
1367                                 cx,
1368                                 link,
1369                                 parent,
1370                                 rendering_params.show_def_docs,
1371                             );
1372                         }
1373                     }
1374                 } else {
1375                     document_item_info(&mut info_buffer, cx, item, Some(parent));
1376                     if rendering_params.show_def_docs {
1377                         document_full(&mut doc_buffer, item, cx, HeadingOffset::H5);
1378                         short_documented = false;
1379                     }
1380                 }
1381             } else {
1382                 document_short(
1383                     &mut doc_buffer,
1384                     item,
1385                     cx,
1386                     link,
1387                     parent,
1388                     rendering_params.show_def_docs,
1389                 );
1390             }
1391         }
1392         let w = if short_documented && trait_.is_some() { interesting } else { boring };
1393
1394         let toggled = !doc_buffer.is_empty();
1395         if toggled {
1396             let method_toggle_class =
1397                 if item_type == ItemType::Method { " method-toggle" } else { "" };
1398             write!(w, "<details class=\"rustdoc-toggle{}\" open><summary>", method_toggle_class);
1399         }
1400         match &*item.kind {
1401             clean::MethodItem(..) | clean::TyMethodItem(_) => {
1402                 // Only render when the method is not static or we allow static methods
1403                 if render_method_item {
1404                     let id = cx.derive_id(format!("{}.{}", item_type, name));
1405                     let source_id = trait_
1406                         .and_then(|trait_| {
1407                             trait_.items.iter().find(|item| {
1408                                 item.name.map(|n| n.as_str().eq(name.as_str())).unwrap_or(false)
1409                             })
1410                         })
1411                         .map(|item| format!("{}.{}", item.type_(), name));
1412                     write!(
1413                         w,
1414                         "<section id=\"{}\" class=\"{}{} has-srclink\">",
1415                         id, item_type, in_trait_class,
1416                     );
1417                     render_rightside(w, cx, item, containing_item, render_mode);
1418                     write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
1419                     w.write_str("<h4 class=\"code-header\">");
1420                     render_assoc_item(
1421                         w,
1422                         item,
1423                         link.anchor(source_id.as_ref().unwrap_or(&id)),
1424                         ItemType::Impl,
1425                         cx,
1426                         render_mode,
1427                     );
1428                     w.write_str("</h4>");
1429                     w.write_str("</section>");
1430                 }
1431             }
1432             kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => {
1433                 let source_id = format!("{}.{}", item_type, name);
1434                 let id = cx.derive_id(source_id.clone());
1435                 write!(
1436                     w,
1437                     "<section id=\"{}\" class=\"{}{} has-srclink\">",
1438                     id, item_type, in_trait_class
1439                 );
1440                 render_rightside(w, cx, item, containing_item, render_mode);
1441                 write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
1442                 w.write_str("<h4 class=\"code-header\">");
1443                 assoc_const(
1444                     w,
1445                     item,
1446                     ty,
1447                     match kind {
1448                         clean::TyAssocConstItem(_) => None,
1449                         clean::AssocConstItem(_, default) => Some(default),
1450                         _ => unreachable!(),
1451                     },
1452                     link.anchor(if trait_.is_some() { &source_id } else { &id }),
1453                     "",
1454                     cx,
1455                 );
1456                 w.write_str("</h4>");
1457                 w.write_str("</section>");
1458             }
1459             clean::TyAssocTypeItem(generics, bounds) => {
1460                 let source_id = format!("{}.{}", item_type, name);
1461                 let id = cx.derive_id(source_id.clone());
1462                 write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class);
1463                 write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
1464                 w.write_str("<h4 class=\"code-header\">");
1465                 assoc_type(
1466                     w,
1467                     item,
1468                     generics,
1469                     bounds,
1470                     None,
1471                     link.anchor(if trait_.is_some() { &source_id } else { &id }),
1472                     0,
1473                     cx,
1474                 );
1475                 w.write_str("</h4>");
1476                 w.write_str("</section>");
1477             }
1478             clean::AssocTypeItem(tydef, _bounds) => {
1479                 let source_id = format!("{}.{}", item_type, name);
1480                 let id = cx.derive_id(source_id.clone());
1481                 write!(
1482                     w,
1483                     "<section id=\"{}\" class=\"{}{} has-srclink\">",
1484                     id, item_type, in_trait_class
1485                 );
1486                 write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
1487                 w.write_str("<h4 class=\"code-header\">");
1488                 assoc_type(
1489                     w,
1490                     item,
1491                     &tydef.generics,
1492                     &[], // intentionally leaving out bounds
1493                     Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
1494                     link.anchor(if trait_.is_some() { &source_id } else { &id }),
1495                     0,
1496                     cx,
1497                 );
1498                 w.write_str("</h4>");
1499                 w.write_str("</section>");
1500             }
1501             clean::StrippedItem(..) => return,
1502             _ => panic!("can't make docs for trait item with name {:?}", item.name),
1503         }
1504
1505         w.push_buffer(info_buffer);
1506         if toggled {
1507             w.write_str("</summary>");
1508             w.push_buffer(doc_buffer);
1509             w.push_str("</details>");
1510         }
1511     }
1512
1513     let mut impl_items = Buffer::empty_from(w);
1514     let mut default_impl_items = Buffer::empty_from(w);
1515
1516     for trait_item in &i.inner_impl().items {
1517         doc_impl_item(
1518             &mut default_impl_items,
1519             &mut impl_items,
1520             cx,
1521             trait_item,
1522             if trait_.is_some() { &i.impl_item } else { parent },
1523             parent,
1524             link,
1525             render_mode,
1526             false,
1527             trait_.map(|t| &t.trait_),
1528             rendering_params,
1529         );
1530     }
1531
1532     fn render_default_items(
1533         boring: &mut Buffer,
1534         interesting: &mut Buffer,
1535         cx: &mut Context<'_>,
1536         t: &clean::Trait,
1537         i: &clean::Impl,
1538         parent: &clean::Item,
1539         containing_item: &clean::Item,
1540         render_mode: RenderMode,
1541         rendering_params: ImplRenderingParameters,
1542     ) {
1543         for trait_item in &t.items {
1544             let n = trait_item.name;
1545             if i.items.iter().any(|m| m.name == n) {
1546                 continue;
1547             }
1548             let did = i.trait_.as_ref().unwrap().def_id();
1549             let provided_methods = i.provided_trait_methods(cx.tcx());
1550             let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
1551
1552             doc_impl_item(
1553                 boring,
1554                 interesting,
1555                 cx,
1556                 trait_item,
1557                 parent,
1558                 containing_item,
1559                 assoc_link,
1560                 render_mode,
1561                 true,
1562                 Some(t),
1563                 rendering_params,
1564             );
1565         }
1566     }
1567
1568     // If we've implemented a trait, then also emit documentation for all
1569     // default items which weren't overridden in the implementation block.
1570     // We don't emit documentation for default items if they appear in the
1571     // Implementations on Foreign Types or Implementors sections.
1572     if rendering_params.show_default_items {
1573         if let Some(t) = trait_ {
1574             render_default_items(
1575                 &mut default_impl_items,
1576                 &mut impl_items,
1577                 cx,
1578                 &t.trait_,
1579                 i.inner_impl(),
1580                 &i.impl_item,
1581                 parent,
1582                 render_mode,
1583                 rendering_params,
1584             );
1585         }
1586     }
1587     if render_mode == RenderMode::Normal {
1588         let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
1589         if toggled {
1590             close_tags.insert_str(0, "</details>");
1591             write!(
1592                 w,
1593                 "<details class=\"rustdoc-toggle implementors-toggle\"{}>",
1594                 if rendering_params.toggle_open_by_default { " open" } else { "" }
1595             );
1596             write!(w, "<summary>")
1597         }
1598         render_impl_summary(
1599             w,
1600             cx,
1601             i,
1602             parent,
1603             parent,
1604             rendering_params.show_def_docs,
1605             use_absolute,
1606             rendering_params.is_on_foreign_type,
1607             aliases,
1608         );
1609         if toggled {
1610             write!(w, "</summary>")
1611         }
1612
1613         if let Some(ref dox) = i.impl_item.collapsed_doc_value() {
1614             if trait_.is_none() && i.inner_impl().items.is_empty() {
1615                 w.write_str(
1616                     "<div class=\"item-info\">\
1617                     <div class=\"stab empty-impl\">This impl block contains no items.</div>
1618                 </div>",
1619                 );
1620             }
1621             write!(
1622                 w,
1623                 "<div class=\"docblock\">{}</div>",
1624                 Markdown {
1625                     content: &*dox,
1626                     links: &i.impl_item.links(cx),
1627                     ids: &mut cx.id_map,
1628                     error_codes: cx.shared.codes,
1629                     edition: cx.shared.edition(),
1630                     playground: &cx.shared.playground,
1631                     heading_offset: HeadingOffset::H4
1632                 }
1633                 .into_string()
1634             );
1635         }
1636     }
1637     if !default_impl_items.is_empty() || !impl_items.is_empty() {
1638         w.write_str("<div class=\"impl-items\">");
1639         w.push_buffer(default_impl_items);
1640         w.push_buffer(impl_items);
1641         close_tags.insert_str(0, "</div>");
1642     }
1643     w.write_str(&close_tags);
1644 }
1645
1646 // Render the items that appear on the right side of methods, impls, and
1647 // associated types. For example "1.0.0 (const: 1.39.0) Â· source".
1648 fn render_rightside(
1649     w: &mut Buffer,
1650     cx: &Context<'_>,
1651     item: &clean::Item,
1652     containing_item: &clean::Item,
1653     render_mode: RenderMode,
1654 ) {
1655     let tcx = cx.tcx();
1656
1657     // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove
1658     // this condition.
1659     let (const_stability, const_stable_since) = match render_mode {
1660         RenderMode::Normal => (item.const_stability(tcx), containing_item.const_stable_since(tcx)),
1661         RenderMode::ForDeref { .. } => (None, None),
1662     };
1663
1664     let mut rightside = Buffer::new();
1665     let has_stability = render_stability_since_raw(
1666         &mut rightside,
1667         item.stable_since(tcx),
1668         const_stability,
1669         containing_item.stable_since(tcx),
1670         const_stable_since,
1671     );
1672     let mut srclink = Buffer::empty_from(w);
1673     write_srclink(cx, item, &mut srclink);
1674     if has_stability && !srclink.is_empty() {
1675         rightside.write_str(" Â· ");
1676     }
1677     rightside.push_buffer(srclink);
1678     if !rightside.is_empty() {
1679         write!(w, "<span class=\"rightside\">{}</span>", rightside.into_inner());
1680     }
1681 }
1682
1683 pub(crate) fn render_impl_summary(
1684     w: &mut Buffer,
1685     cx: &mut Context<'_>,
1686     i: &Impl,
1687     parent: &clean::Item,
1688     containing_item: &clean::Item,
1689     show_def_docs: bool,
1690     use_absolute: Option<bool>,
1691     is_on_foreign_type: bool,
1692     // This argument is used to reference same type with different paths to avoid duplication
1693     // in documentation pages for trait with automatic implementations like "Send" and "Sync".
1694     aliases: &[String],
1695 ) {
1696     let id = cx.derive_id(match i.inner_impl().trait_ {
1697         Some(ref t) => {
1698             if is_on_foreign_type {
1699                 get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t, cx)
1700             } else {
1701                 format!("impl-{}", small_url_encode(format!("{:#}", t.print(cx))))
1702             }
1703         }
1704         None => "impl".to_string(),
1705     });
1706     let aliases = if aliases.is_empty() {
1707         String::new()
1708     } else {
1709         format!(" data-aliases=\"{}\"", aliases.join(","))
1710     };
1711     write!(w, "<section id=\"{}\" class=\"impl has-srclink\"{}>", id, aliases);
1712     render_rightside(w, cx, &i.impl_item, containing_item, RenderMode::Normal);
1713     write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
1714     write!(w, "<h3 class=\"code-header in-band\">");
1715
1716     if let Some(use_absolute) = use_absolute {
1717         write!(w, "{}", i.inner_impl().print(use_absolute, cx));
1718         if show_def_docs {
1719             for it in &i.inner_impl().items {
1720                 if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
1721                     w.write_str("<span class=\"where fmt-newline\">  ");
1722                     assoc_type(
1723                         w,
1724                         it,
1725                         &tydef.generics,
1726                         &[], // intentionally leaving out bounds
1727                         Some(&tydef.type_),
1728                         AssocItemLink::Anchor(None),
1729                         0,
1730                         cx,
1731                     );
1732                     w.write_str(";</span>");
1733                 }
1734             }
1735         }
1736     } else {
1737         write!(w, "{}", i.inner_impl().print(false, cx));
1738     }
1739     write!(w, "</h3>");
1740
1741     let is_trait = i.inner_impl().trait_.is_some();
1742     if is_trait {
1743         if let Some(portability) = portability(&i.impl_item, Some(parent)) {
1744             write!(w, "<span class=\"item-info\">{}</span>", portability);
1745         }
1746     }
1747
1748     w.write_str("</section>");
1749 }
1750
1751 fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
1752     if it.is_struct()
1753         || it.is_trait()
1754         || it.is_primitive()
1755         || it.is_union()
1756         || it.is_enum()
1757         || it.is_mod()
1758         || it.is_typedef()
1759     {
1760         write!(
1761             buffer,
1762             "<h2 class=\"location\"><a href=\"#\">{}{}</a></h2>",
1763             match *it.kind {
1764                 clean::ModuleItem(..) =>
1765                     if it.is_crate() {
1766                         "Crate "
1767                     } else {
1768                         "Module "
1769                     },
1770                 _ => "",
1771             },
1772             it.name.as_ref().unwrap()
1773         );
1774     }
1775
1776     buffer.write_str("<div class=\"sidebar-elems\">");
1777     if it.is_crate() {
1778         write!(buffer, "<div class=\"block\"><ul>");
1779         if let Some(ref version) = cx.cache().crate_version {
1780             write!(buffer, "<li class=\"version\">Version {}</li>", Escape(version));
1781         }
1782         write!(buffer, "<li><a id=\"all-types\" href=\"all.html\">All Items</a></li>");
1783         buffer.write_str("</ul></div>");
1784     }
1785
1786     match *it.kind {
1787         clean::StructItem(ref s) => sidebar_struct(cx, buffer, it, s),
1788         clean::TraitItem(ref t) => sidebar_trait(cx, buffer, it, t),
1789         clean::PrimitiveItem(_) => sidebar_primitive(cx, buffer, it),
1790         clean::UnionItem(ref u) => sidebar_union(cx, buffer, it, u),
1791         clean::EnumItem(ref e) => sidebar_enum(cx, buffer, it, e),
1792         clean::TypedefItem(_) => sidebar_typedef(cx, buffer, it),
1793         clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items),
1794         clean::ForeignTypeItem => sidebar_foreign_type(cx, buffer, it),
1795         _ => {}
1796     }
1797
1798     // The sidebar is designed to display sibling functions, modules and
1799     // other miscellaneous information. since there are lots of sibling
1800     // items (and that causes quadratic growth in large modules),
1801     // we refactor common parts into a shared JavaScript file per module.
1802     // still, we don't move everything into JS because we want to preserve
1803     // as much HTML as possible in order to allow non-JS-enabled browsers
1804     // to navigate the documentation (though slightly inefficiently).
1805
1806     if !it.is_mod() {
1807         let path: String = cx.current.iter().map(|s| s.as_str()).intersperse("::").collect();
1808
1809         write!(buffer, "<h2 class=\"location\"><a href=\"index.html\">In {}</a></h2>", path);
1810     }
1811
1812     // Closes sidebar-elems div.
1813     buffer.write_str("</div>");
1814 }
1815
1816 fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String {
1817     if used_links.insert(url.clone()) {
1818         return url;
1819     }
1820     let mut add = 1;
1821     while !used_links.insert(format!("{}-{}", url, add)) {
1822         add += 1;
1823     }
1824     format!("{}-{}", url, add)
1825 }
1826
1827 struct SidebarLink {
1828     name: Symbol,
1829     url: String,
1830 }
1831
1832 impl fmt::Display for SidebarLink {
1833     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1834         write!(f, "<a href=\"#{}\">{}</a>", self.url, self.name)
1835     }
1836 }
1837
1838 impl PartialEq for SidebarLink {
1839     fn eq(&self, other: &Self) -> bool {
1840         self.url == other.url
1841     }
1842 }
1843
1844 impl Eq for SidebarLink {}
1845
1846 impl PartialOrd for SidebarLink {
1847     fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1848         Some(self.cmp(other))
1849     }
1850 }
1851
1852 impl Ord for SidebarLink {
1853     fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1854         self.url.cmp(&other.url)
1855     }
1856 }
1857
1858 fn get_methods(
1859     i: &clean::Impl,
1860     for_deref: bool,
1861     used_links: &mut FxHashSet<String>,
1862     deref_mut: bool,
1863     tcx: TyCtxt<'_>,
1864 ) -> Vec<SidebarLink> {
1865     i.items
1866         .iter()
1867         .filter_map(|item| match item.name {
1868             Some(name) if !name.is_empty() && item.is_method() => {
1869                 if !for_deref || should_render_item(item, deref_mut, tcx) {
1870                     Some(SidebarLink {
1871                         name,
1872                         url: get_next_url(used_links, format!("{}.{}", ItemType::Method, name)),
1873                     })
1874                 } else {
1875                     None
1876                 }
1877             }
1878             _ => None,
1879         })
1880         .collect::<Vec<_>>()
1881 }
1882
1883 fn get_associated_constants(
1884     i: &clean::Impl,
1885     used_links: &mut FxHashSet<String>,
1886 ) -> Vec<SidebarLink> {
1887     i.items
1888         .iter()
1889         .filter_map(|item| match item.name {
1890             Some(name) if !name.is_empty() && item.is_associated_const() => Some(SidebarLink {
1891                 name,
1892                 url: get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)),
1893             }),
1894             _ => None,
1895         })
1896         .collect::<Vec<_>>()
1897 }
1898
1899 // The point is to url encode any potential character from a type with genericity.
1900 fn small_url_encode(s: String) -> String {
1901     let mut st = String::new();
1902     let mut last_match = 0;
1903     for (idx, c) in s.char_indices() {
1904         let escaped = match c {
1905             '<' => "%3C",
1906             '>' => "%3E",
1907             ' ' => "%20",
1908             '?' => "%3F",
1909             '\'' => "%27",
1910             '&' => "%26",
1911             ',' => "%2C",
1912             ':' => "%3A",
1913             ';' => "%3B",
1914             '[' => "%5B",
1915             ']' => "%5D",
1916             '"' => "%22",
1917             _ => continue,
1918         };
1919
1920         st += &s[last_match..idx];
1921         st += escaped;
1922         // NOTE: we only expect single byte characters here - which is fine as long as we
1923         // only match single byte characters
1924         last_match = idx + 1;
1925     }
1926
1927     if last_match != 0 {
1928         st += &s[last_match..];
1929         st
1930     } else {
1931         s
1932     }
1933 }
1934
1935 fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
1936     let did = it.item_id.expect_def_id();
1937     let cache = cx.cache();
1938
1939     if let Some(v) = cache.impls.get(&did) {
1940         let mut used_links = FxHashSet::default();
1941         let mut id_map = IdMap::new();
1942
1943         {
1944             let used_links_bor = &mut used_links;
1945             let mut assoc_consts = v
1946                 .iter()
1947                 .filter(|i| i.inner_impl().trait_.is_none())
1948                 .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor))
1949                 .collect::<Vec<_>>();
1950             if !assoc_consts.is_empty() {
1951                 // We want links' order to be reproducible so we don't use unstable sort.
1952                 assoc_consts.sort();
1953
1954                 print_sidebar_block(
1955                     out,
1956                     "implementations",
1957                     "Associated Constants",
1958                     assoc_consts.iter(),
1959                 );
1960             }
1961             let mut methods = v
1962                 .iter()
1963                 .filter(|i| i.inner_impl().trait_.is_none())
1964                 .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx()))
1965                 .collect::<Vec<_>>();
1966             if !methods.is_empty() {
1967                 // We want links' order to be reproducible so we don't use unstable sort.
1968                 methods.sort();
1969
1970                 print_sidebar_block(out, "implementations", "Methods", methods.iter());
1971             }
1972         }
1973
1974         if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
1975             if let Some(impl_) =
1976                 v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
1977             {
1978                 let mut derefs = FxHashSet::default();
1979                 derefs.insert(did);
1980                 sidebar_deref_methods(cx, out, impl_, v, &mut derefs);
1981             }
1982
1983             let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
1984                 let mut links = FxHashSet::default();
1985
1986                 let mut ret = impls
1987                     .iter()
1988                     .filter_map(|it| {
1989                         if let Some(ref i) = it.inner_impl().trait_ {
1990                             let i_display = format!("{:#}", i.print(cx));
1991                             let out = Escape(&i_display);
1992                             let encoded =
1993                                 id_map.derive(small_url_encode(format!("impl-{:#}", i.print(cx))));
1994                             let prefix = match it.inner_impl().polarity {
1995                                 ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
1996                                 ty::ImplPolarity::Negative => "!",
1997                             };
1998                             let generated =
1999                                 format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out);
2000                             if links.insert(generated.clone()) { Some(generated) } else { None }
2001                         } else {
2002                             None
2003                         }
2004                     })
2005                     .collect::<Vec<String>>();
2006                 ret.sort();
2007                 ret
2008             };
2009
2010             let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
2011                 v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
2012             let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
2013                 concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
2014
2015             let concrete_format = format_impls(concrete, &mut id_map);
2016             let synthetic_format = format_impls(synthetic, &mut id_map);
2017             let blanket_format = format_impls(blanket_impl, &mut id_map);
2018
2019             if !concrete_format.is_empty() {
2020                 print_sidebar_block(
2021                     out,
2022                     "trait-implementations",
2023                     "Trait Implementations",
2024                     concrete_format.iter(),
2025                 );
2026             }
2027
2028             if !synthetic_format.is_empty() {
2029                 print_sidebar_block(
2030                     out,
2031                     "synthetic-implementations",
2032                     "Auto Trait Implementations",
2033                     synthetic_format.iter(),
2034                 );
2035             }
2036
2037             if !blanket_format.is_empty() {
2038                 print_sidebar_block(
2039                     out,
2040                     "blanket-implementations",
2041                     "Blanket Implementations",
2042                     blanket_format.iter(),
2043                 );
2044             }
2045         }
2046     }
2047 }
2048
2049 fn sidebar_deref_methods(
2050     cx: &Context<'_>,
2051     out: &mut Buffer,
2052     impl_: &Impl,
2053     v: &[Impl],
2054     derefs: &mut FxHashSet<DefId>,
2055 ) {
2056     let c = cx.cache();
2057
2058     debug!("found Deref: {:?}", impl_);
2059     if let Some((target, real_target)) =
2060         impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
2061             clean::AssocTypeItem(ref t, _) => Some(match *t {
2062                 clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
2063                 _ => (&t.type_, &t.type_),
2064             }),
2065             _ => None,
2066         })
2067     {
2068         debug!("found target, real_target: {:?} {:?}", target, real_target);
2069         if let Some(did) = target.def_id(c) {
2070             if let Some(type_did) = impl_.inner_impl().for_.def_id(c) {
2071                 // `impl Deref<Target = S> for S`
2072                 if did == type_did || !derefs.insert(did) {
2073                     // Avoid infinite cycles
2074                     return;
2075                 }
2076             }
2077         }
2078         let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
2079         let inner_impl = target
2080             .def_id(c)
2081             .or_else(|| {
2082                 target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
2083             })
2084             .and_then(|did| c.impls.get(&did));
2085         if let Some(impls) = inner_impl {
2086             debug!("found inner_impl: {:?}", impls);
2087             let mut used_links = FxHashSet::default();
2088             let mut ret = impls
2089                 .iter()
2090                 .filter(|i| i.inner_impl().trait_.is_none())
2091                 .flat_map(|i| {
2092                     get_methods(i.inner_impl(), true, &mut used_links, deref_mut, cx.tcx())
2093                 })
2094                 .collect::<Vec<_>>();
2095             if !ret.is_empty() {
2096                 let id = if let Some(target_def_id) = real_target.def_id(c) {
2097                     cx.deref_id_map.get(&target_def_id).expect("Deref section without derived id")
2098                 } else {
2099                     "deref-methods"
2100                 };
2101                 let title = format!(
2102                     "Methods from {}&lt;Target={}&gt;",
2103                     Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))),
2104                     Escape(&format!("{:#}", real_target.print(cx))),
2105                 );
2106                 // We want links' order to be reproducible so we don't use unstable sort.
2107                 ret.sort();
2108                 print_sidebar_block(out, id, &title, ret.iter());
2109             }
2110         }
2111
2112         // Recurse into any further impls that might exist for `target`
2113         if let Some(target_did) = target.def_id(c) {
2114             if let Some(target_impls) = c.impls.get(&target_did) {
2115                 if let Some(target_deref_impl) = target_impls.iter().find(|i| {
2116                     i.inner_impl()
2117                         .trait_
2118                         .as_ref()
2119                         .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
2120                         .unwrap_or(false)
2121                 }) {
2122                     sidebar_deref_methods(cx, out, target_deref_impl, target_impls, derefs);
2123                 }
2124             }
2125         }
2126     }
2127 }
2128
2129 fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) {
2130     let mut sidebar = Buffer::new();
2131     let fields = get_struct_fields_name(&s.fields);
2132
2133     if !fields.is_empty() {
2134         match s.struct_type {
2135             CtorKind::Fictive => {
2136                 print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
2137             }
2138             CtorKind::Fn => print_sidebar_title(&mut sidebar, "fields", "Tuple Fields"),
2139             CtorKind::Const => {}
2140         }
2141     }
2142
2143     sidebar_assoc_items(cx, &mut sidebar, it);
2144
2145     if !sidebar.is_empty() {
2146         write!(buf, "<section>{}</section>", sidebar.into_inner());
2147     }
2148 }
2149
2150 fn get_id_for_impl_on_foreign_type(
2151     for_: &clean::Type,
2152     trait_: &clean::Path,
2153     cx: &Context<'_>,
2154 ) -> String {
2155     small_url_encode(format!("impl-{:#}-for-{:#}", trait_.print(cx), for_.print(cx)))
2156 }
2157
2158 fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2159     match *item.kind {
2160         clean::ItemKind::ImplItem(ref i) => {
2161             i.trait_.as_ref().map(|trait_| {
2162                 // Alternative format produces no URLs,
2163                 // so this parameter does nothing.
2164                 (
2165                     format!("{:#}", i.for_.print(cx)),
2166                     get_id_for_impl_on_foreign_type(&i.for_, trait_, cx),
2167                 )
2168             })
2169         }
2170         _ => None,
2171     }
2172 }
2173
2174 /// Don't call this function directly!!! Use `print_sidebar_title` or `print_sidebar_block` instead!
2175 fn print_sidebar_title_inner(buf: &mut Buffer, id: &str, title: &str) {
2176     write!(
2177         buf,
2178         "<h3 class=\"sidebar-title\">\
2179              <a href=\"#{}\">{}</a>\
2180          </h3>",
2181         id, title
2182     );
2183 }
2184
2185 fn print_sidebar_title(buf: &mut Buffer, id: &str, title: &str) {
2186     buf.push_str("<div class=\"block\">");
2187     print_sidebar_title_inner(buf, id, title);
2188     buf.push_str("</div>");
2189 }
2190
2191 fn print_sidebar_block(
2192     buf: &mut Buffer,
2193     id: &str,
2194     title: &str,
2195     items: impl Iterator<Item = impl fmt::Display>,
2196 ) {
2197     buf.push_str("<div class=\"block\">");
2198     print_sidebar_title_inner(buf, id, title);
2199     buf.push_str("<ul>");
2200     for item in items {
2201         write!(buf, "<li>{}</li>", item);
2202     }
2203     buf.push_str("</ul></div>");
2204 }
2205
2206 fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) {
2207     buf.write_str("<section>");
2208
2209     fn print_sidebar_section(
2210         out: &mut Buffer,
2211         items: &[clean::Item],
2212         id: &str,
2213         title: &str,
2214         filter: impl Fn(&clean::Item) -> bool,
2215         mapper: impl Fn(&str) -> String,
2216     ) {
2217         let mut items: Vec<&str> = items
2218             .iter()
2219             .filter_map(|m| match m.name {
2220                 Some(ref name) if filter(m) => Some(name.as_str()),
2221                 _ => None,
2222             })
2223             .collect::<Vec<_>>();
2224
2225         if !items.is_empty() {
2226             items.sort_unstable();
2227             print_sidebar_block(out, id, title, items.into_iter().map(mapper));
2228         }
2229     }
2230
2231     print_sidebar_section(
2232         buf,
2233         &t.items,
2234         "required-associated-types",
2235         "Required Associated Types",
2236         |m| m.is_ty_associated_type(),
2237         |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocType),
2238     );
2239
2240     print_sidebar_section(
2241         buf,
2242         &t.items,
2243         "provided-associated-types",
2244         "Provided Associated Types",
2245         |m| m.is_associated_type(),
2246         |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocType),
2247     );
2248
2249     print_sidebar_section(
2250         buf,
2251         &t.items,
2252         "required-associated-consts",
2253         "Required Associated Constants",
2254         |m| m.is_ty_associated_const(),
2255         |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocConst),
2256     );
2257
2258     print_sidebar_section(
2259         buf,
2260         &t.items,
2261         "provided-associated-consts",
2262         "Provided Associated Constants",
2263         |m| m.is_associated_const(),
2264         |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocConst),
2265     );
2266
2267     print_sidebar_section(
2268         buf,
2269         &t.items,
2270         "required-methods",
2271         "Required Methods",
2272         |m| m.is_ty_method(),
2273         |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::TyMethod),
2274     );
2275
2276     print_sidebar_section(
2277         buf,
2278         &t.items,
2279         "provided-methods",
2280         "Provided Methods",
2281         |m| m.is_method(),
2282         |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::Method),
2283     );
2284
2285     if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
2286         let mut res = implementors
2287             .iter()
2288             .filter(|i| !i.is_on_local_type(cx))
2289             .filter_map(|i| extract_for_impl_name(&i.impl_item, cx))
2290             .collect::<Vec<_>>();
2291
2292         if !res.is_empty() {
2293             res.sort();
2294             print_sidebar_block(
2295                 buf,
2296                 "foreign-impls",
2297                 "Implementations on Foreign Types",
2298                 res.iter().map(|(name, id)| format!("<a href=\"#{}\">{}</a>", id, Escape(name))),
2299             );
2300         }
2301     }
2302
2303     sidebar_assoc_items(cx, buf, it);
2304
2305     print_sidebar_title(buf, "implementors", "Implementors");
2306     if t.is_auto {
2307         print_sidebar_title(buf, "synthetic-implementors", "Auto Implementors");
2308     }
2309
2310     buf.push_str("</section>")
2311 }
2312
2313 fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
2314     let mut sidebar = Buffer::new();
2315     sidebar_assoc_items(cx, &mut sidebar, it);
2316
2317     if !sidebar.is_empty() {
2318         write!(buf, "<section>{}</section>", sidebar.into_inner());
2319     }
2320 }
2321
2322 fn sidebar_typedef(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
2323     let mut sidebar = Buffer::new();
2324     sidebar_assoc_items(cx, &mut sidebar, it);
2325
2326     if !sidebar.is_empty() {
2327         write!(buf, "<section>{}</section>", sidebar.into_inner());
2328     }
2329 }
2330
2331 fn get_struct_fields_name(fields: &[clean::Item]) -> Vec<String> {
2332     let mut fields = fields
2333         .iter()
2334         .filter(|f| matches!(*f.kind, clean::StructFieldItem(..)))
2335         .filter_map(|f| {
2336             f.name.map(|name| format!("<a href=\"#structfield.{name}\">{name}</a>", name = name))
2337         })
2338         .collect::<Vec<_>>();
2339     fields.sort();
2340     fields
2341 }
2342
2343 fn sidebar_union(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, u: &clean::Union) {
2344     let mut sidebar = Buffer::new();
2345     let fields = get_struct_fields_name(&u.fields);
2346
2347     if !fields.is_empty() {
2348         print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
2349     }
2350
2351     sidebar_assoc_items(cx, &mut sidebar, it);
2352
2353     if !sidebar.is_empty() {
2354         write!(buf, "<section>{}</section>", sidebar.into_inner());
2355     }
2356 }
2357
2358 fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) {
2359     let mut sidebar = Buffer::new();
2360
2361     let mut variants = e
2362         .variants()
2363         .filter_map(|v| {
2364             v.name
2365                 .as_ref()
2366                 .map(|name| format!("<a href=\"#variant.{name}\">{name}</a>", name = name))
2367         })
2368         .collect::<Vec<_>>();
2369     if !variants.is_empty() {
2370         variants.sort_unstable();
2371         print_sidebar_block(&mut sidebar, "variants", "Variants", variants.iter());
2372     }
2373
2374     sidebar_assoc_items(cx, &mut sidebar, it);
2375
2376     if !sidebar.is_empty() {
2377         write!(buf, "<section>{}</section>", sidebar.into_inner());
2378     }
2379 }
2380
2381 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2382 enum ItemSection {
2383     Reexports,
2384     PrimitiveTypes,
2385     Modules,
2386     Macros,
2387     Structs,
2388     Enums,
2389     Constants,
2390     Statics,
2391     Traits,
2392     Functions,
2393     TypeDefinitions,
2394     Unions,
2395     Implementations,
2396     TypeMethods,
2397     Methods,
2398     StructFields,
2399     Variants,
2400     AssociatedTypes,
2401     AssociatedConstants,
2402     ForeignTypes,
2403     Keywords,
2404     OpaqueTypes,
2405     AttributeMacros,
2406     DeriveMacros,
2407     TraitAliases,
2408 }
2409
2410 impl ItemSection {
2411     const ALL: &'static [Self] = {
2412         use ItemSection::*;
2413         // NOTE: The order here affects the order in the UI.
2414         &[
2415             Reexports,
2416             PrimitiveTypes,
2417             Modules,
2418             Macros,
2419             Structs,
2420             Enums,
2421             Constants,
2422             Statics,
2423             Traits,
2424             Functions,
2425             TypeDefinitions,
2426             Unions,
2427             Implementations,
2428             TypeMethods,
2429             Methods,
2430             StructFields,
2431             Variants,
2432             AssociatedTypes,
2433             AssociatedConstants,
2434             ForeignTypes,
2435             Keywords,
2436             OpaqueTypes,
2437             AttributeMacros,
2438             DeriveMacros,
2439             TraitAliases,
2440         ]
2441     };
2442
2443     fn id(self) -> &'static str {
2444         match self {
2445             Self::Reexports => "reexports",
2446             Self::Modules => "modules",
2447             Self::Structs => "structs",
2448             Self::Unions => "unions",
2449             Self::Enums => "enums",
2450             Self::Functions => "functions",
2451             Self::TypeDefinitions => "types",
2452             Self::Statics => "statics",
2453             Self::Constants => "constants",
2454             Self::Traits => "traits",
2455             Self::Implementations => "impls",
2456             Self::TypeMethods => "tymethods",
2457             Self::Methods => "methods",
2458             Self::StructFields => "fields",
2459             Self::Variants => "variants",
2460             Self::Macros => "macros",
2461             Self::PrimitiveTypes => "primitives",
2462             Self::AssociatedTypes => "associated-types",
2463             Self::AssociatedConstants => "associated-consts",
2464             Self::ForeignTypes => "foreign-types",
2465             Self::Keywords => "keywords",
2466             Self::OpaqueTypes => "opaque-types",
2467             Self::AttributeMacros => "attributes",
2468             Self::DeriveMacros => "derives",
2469             Self::TraitAliases => "trait-aliases",
2470         }
2471     }
2472
2473     fn name(self) -> &'static str {
2474         match self {
2475             Self::Reexports => "Re-exports",
2476             Self::Modules => "Modules",
2477             Self::Structs => "Structs",
2478             Self::Unions => "Unions",
2479             Self::Enums => "Enums",
2480             Self::Functions => "Functions",
2481             Self::TypeDefinitions => "Type Definitions",
2482             Self::Statics => "Statics",
2483             Self::Constants => "Constants",
2484             Self::Traits => "Traits",
2485             Self::Implementations => "Implementations",
2486             Self::TypeMethods => "Type Methods",
2487             Self::Methods => "Methods",
2488             Self::StructFields => "Struct Fields",
2489             Self::Variants => "Variants",
2490             Self::Macros => "Macros",
2491             Self::PrimitiveTypes => "Primitive Types",
2492             Self::AssociatedTypes => "Associated Types",
2493             Self::AssociatedConstants => "Associated Constants",
2494             Self::ForeignTypes => "Foreign Types",
2495             Self::Keywords => "Keywords",
2496             Self::OpaqueTypes => "Opaque Types",
2497             Self::AttributeMacros => "Attribute Macros",
2498             Self::DeriveMacros => "Derive Macros",
2499             Self::TraitAliases => "Trait Aliases",
2500         }
2501     }
2502 }
2503
2504 fn item_ty_to_section(ty: ItemType) -> ItemSection {
2505     match ty {
2506         ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
2507         ItemType::Module => ItemSection::Modules,
2508         ItemType::Struct => ItemSection::Structs,
2509         ItemType::Union => ItemSection::Unions,
2510         ItemType::Enum => ItemSection::Enums,
2511         ItemType::Function => ItemSection::Functions,
2512         ItemType::Typedef => ItemSection::TypeDefinitions,
2513         ItemType::Static => ItemSection::Statics,
2514         ItemType::Constant => ItemSection::Constants,
2515         ItemType::Trait => ItemSection::Traits,
2516         ItemType::Impl => ItemSection::Implementations,
2517         ItemType::TyMethod => ItemSection::TypeMethods,
2518         ItemType::Method => ItemSection::Methods,
2519         ItemType::StructField => ItemSection::StructFields,
2520         ItemType::Variant => ItemSection::Variants,
2521         ItemType::Macro => ItemSection::Macros,
2522         ItemType::Primitive => ItemSection::PrimitiveTypes,
2523         ItemType::AssocType => ItemSection::AssociatedTypes,
2524         ItemType::AssocConst => ItemSection::AssociatedConstants,
2525         ItemType::ForeignType => ItemSection::ForeignTypes,
2526         ItemType::Keyword => ItemSection::Keywords,
2527         ItemType::OpaqueTy => ItemSection::OpaqueTypes,
2528         ItemType::ProcAttribute => ItemSection::AttributeMacros,
2529         ItemType::ProcDerive => ItemSection::DeriveMacros,
2530         ItemType::TraitAlias => ItemSection::TraitAliases,
2531     }
2532 }
2533
2534 fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) {
2535     use std::fmt::Write as _;
2536
2537     let mut sidebar = String::new();
2538
2539     let item_sections_in_use: FxHashSet<_> = items
2540         .iter()
2541         .filter(|it| {
2542             !it.is_stripped()
2543                 && it
2544                     .name
2545                     .or_else(|| {
2546                         if let clean::ImportItem(ref i) = *it.kind &&
2547                             let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None }
2548                     })
2549                     .is_some()
2550         })
2551         .map(|it| item_ty_to_section(it.type_()))
2552         .collect();
2553     for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) {
2554         let _ = write!(sidebar, "<li><a href=\"#{}\">{}</a></li>", sec.id(), sec.name());
2555     }
2556
2557     if !sidebar.is_empty() {
2558         write!(
2559             buf,
2560             "<section>\
2561                  <div class=\"block\">\
2562                      <ul>{}</ul>\
2563                  </div>\
2564              </section>",
2565             sidebar
2566         );
2567     }
2568 }
2569
2570 fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
2571     let mut sidebar = Buffer::new();
2572     sidebar_assoc_items(cx, &mut sidebar, it);
2573
2574     if !sidebar.is_empty() {
2575         write!(buf, "<section>{}</section>", sidebar.into_inner());
2576     }
2577 }
2578
2579 pub(crate) const BASIC_KEYWORDS: &str = "rust, rustlang, rust-lang";
2580
2581 /// Returns a list of all paths used in the type.
2582 /// This is used to help deduplicate imported impls
2583 /// for reexported types. If any of the contained
2584 /// types are re-exported, we don't use the corresponding
2585 /// entry from the js file, as inlining will have already
2586 /// picked up the impl
2587 fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
2588     let mut out = Vec::new();
2589     let mut visited = FxHashSet::default();
2590     let mut work = VecDeque::new();
2591
2592     let mut process_path = |did: DefId| {
2593         let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone());
2594         let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern);
2595
2596         if let Some(path) = fqp {
2597             out.push(join_with_double_colon(&path));
2598         }
2599     };
2600
2601     work.push_back(first_ty);
2602
2603     while let Some(ty) = work.pop_front() {
2604         if !visited.insert(ty.clone()) {
2605             continue;
2606         }
2607
2608         match ty {
2609             clean::Type::Path { path } => process_path(path.def_id()),
2610             clean::Type::Tuple(tys) => {
2611                 work.extend(tys.into_iter());
2612             }
2613             clean::Type::Slice(ty) => {
2614                 work.push_back(*ty);
2615             }
2616             clean::Type::Array(ty, _) => {
2617                 work.push_back(*ty);
2618             }
2619             clean::Type::RawPointer(_, ty) => {
2620                 work.push_back(*ty);
2621             }
2622             clean::Type::BorrowedRef { type_, .. } => {
2623                 work.push_back(*type_);
2624             }
2625             clean::Type::QPath { self_type, trait_, .. } => {
2626                 work.push_back(*self_type);
2627                 process_path(trait_.def_id());
2628             }
2629             _ => {}
2630         }
2631     }
2632     out
2633 }
2634
2635 const MAX_FULL_EXAMPLES: usize = 5;
2636 const NUM_VISIBLE_LINES: usize = 10;
2637
2638 /// Generates the HTML for example call locations generated via the --scrape-examples flag.
2639 fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item) {
2640     let tcx = cx.tcx();
2641     let def_id = item.item_id.expect_def_id();
2642     let key = tcx.def_path_hash(def_id);
2643     let Some(call_locations) = cx.shared.call_locations.get(&key) else { return };
2644
2645     // Generate a unique ID so users can link to this section for a given method
2646     let id = cx.id_map.derive("scraped-examples");
2647     write!(
2648         w,
2649         "<div class=\"docblock scraped-example-list\">\
2650           <span></span>\
2651           <h5 id=\"{id}\">\
2652              <a href=\"#{id}\">Examples found in repository</a>\
2653              <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
2654           </h5>",
2655         root_path = cx.root_path(),
2656         id = id
2657     );
2658
2659     // Create a URL to a particular location in a reverse-dependency's source file
2660     let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
2661         let (line_lo, line_hi) = loc.call_expr.line_span;
2662         let (anchor, title) = if line_lo == line_hi {
2663             ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
2664         } else {
2665             (
2666                 format!("{}-{}", line_lo + 1, line_hi + 1),
2667                 format!("lines {}-{}", line_lo + 1, line_hi + 1),
2668             )
2669         };
2670         let url = format!("{}{}#{}", cx.root_path(), call_data.url, anchor);
2671         (url, title)
2672     };
2673
2674     // Generate the HTML for a single example, being the title and code block
2675     let write_example = |w: &mut Buffer, (path, call_data): (&PathBuf, &CallData)| -> bool {
2676         let contents = match fs::read_to_string(&path) {
2677             Ok(contents) => contents,
2678             Err(err) => {
2679                 let span = item.span(tcx).inner();
2680                 tcx.sess
2681                     .span_err(span, &format!("failed to read file {}: {}", path.display(), err));
2682                 return false;
2683             }
2684         };
2685
2686         // To reduce file sizes, we only want to embed the source code needed to understand the example, not
2687         // the entire file. So we find the smallest byte range that covers all items enclosing examples.
2688         assert!(!call_data.locations.is_empty());
2689         let min_loc =
2690             call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2691         let byte_min = min_loc.enclosing_item.byte_span.0;
2692         let line_min = min_loc.enclosing_item.line_span.0;
2693         let max_loc =
2694             call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
2695         let byte_max = max_loc.enclosing_item.byte_span.1;
2696         let line_max = max_loc.enclosing_item.line_span.1;
2697
2698         // The output code is limited to that byte range.
2699         let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
2700
2701         // The call locations need to be updated to reflect that the size of the program has changed.
2702         // Specifically, the ranges are all subtracted by `byte_min` since that's the new zero point.
2703         let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
2704             .locations
2705             .iter()
2706             .map(|loc| {
2707                 let (byte_lo, byte_hi) = loc.call_ident.byte_span;
2708                 let (line_lo, line_hi) = loc.call_expr.line_span;
2709                 let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
2710
2711                 let line_range = (line_lo - line_min, line_hi - line_min);
2712                 let (line_url, line_title) = link_to_loc(call_data, loc);
2713
2714                 (byte_range, (line_range, line_url, line_title))
2715             })
2716             .unzip();
2717
2718         let (_, init_url, init_title) = &line_ranges[0];
2719         let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
2720         let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
2721
2722         write!(
2723             w,
2724             "<div class=\"scraped-example {expanded_cls}\" data-locs=\"{locations}\">\
2725                 <div class=\"scraped-example-title\">\
2726                    {name} (<a href=\"{url}\">{title}</a>)\
2727                 </div>\
2728                 <div class=\"code-wrapper\">",
2729             expanded_cls = if needs_expansion { "" } else { "expanded" },
2730             name = call_data.display_name,
2731             url = init_url,
2732             title = init_title,
2733             // The locations are encoded as a data attribute, so they can be read
2734             // later by the JS for interactions.
2735             locations = Escape(&locations_encoded)
2736         );
2737
2738         if line_ranges.len() > 1 {
2739             write!(w, r#"<span class="prev">&pr;</span> <span class="next">&sc;</span>"#);
2740         }
2741
2742         if needs_expansion {
2743             write!(w, r#"<span class="expand">&varr;</span>"#);
2744         }
2745
2746         // Look for the example file in the source map if it exists, otherwise return a dummy span
2747         let file_span = (|| {
2748             let source_map = tcx.sess.source_map();
2749             let crate_src = tcx.sess.local_crate_source_file.as_ref()?;
2750             let abs_crate_src = crate_src.canonicalize().ok()?;
2751             let crate_root = abs_crate_src.parent()?.parent()?;
2752             let rel_path = path.strip_prefix(crate_root).ok()?;
2753             let files = source_map.files();
2754             let file = files.iter().find(|file| match &file.name {
2755                 FileName::Real(RealFileName::LocalPath(other_path)) => rel_path == other_path,
2756                 _ => false,
2757             })?;
2758             Some(rustc_span::Span::with_root_ctxt(
2759                 file.start_pos + BytePos(byte_min),
2760                 file.start_pos + BytePos(byte_max),
2761             ))
2762         })()
2763         .unwrap_or(rustc_span::DUMMY_SP);
2764
2765         // The root path is the inverse of Context::current
2766         let root_path = vec!["../"; cx.current.len() - 1].join("");
2767
2768         let mut decoration_info = FxHashMap::default();
2769         decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
2770         decoration_info.insert("highlight", byte_ranges);
2771
2772         sources::print_src(
2773             w,
2774             contents_subset,
2775             call_data.edition,
2776             file_span,
2777             cx,
2778             &root_path,
2779             Some(highlight::DecorationInfo(decoration_info)),
2780             sources::SourceContext::Embedded { offset: line_min },
2781         );
2782         write!(w, "</div></div>");
2783
2784         true
2785     };
2786
2787     // The call locations are output in sequence, so that sequence needs to be determined.
2788     // Ideally the most "relevant" examples would be shown first, but there's no general algorithm
2789     // for determining relevance. Instead, we prefer the smallest examples being likely the easiest to
2790     // understand at a glance.
2791     let ordered_locations = {
2792         let sort_criterion = |(_, call_data): &(_, &CallData)| {
2793             // Use the first location because that's what the user will see initially
2794             let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2795             hi - lo
2796         };
2797
2798         let mut locs = call_locations.iter().collect::<Vec<_>>();
2799         locs.sort_by_key(sort_criterion);
2800         locs
2801     };
2802
2803     let mut it = ordered_locations.into_iter().peekable();
2804
2805     // An example may fail to write if its source can't be read for some reason, so this method
2806     // continues iterating until a write succeeds
2807     let write_and_skip_failure = |w: &mut Buffer, it: &mut Peekable<_>| {
2808         while let Some(example) = it.next() {
2809             if write_example(&mut *w, example) {
2810                 break;
2811             }
2812         }
2813     };
2814
2815     // Write just one example that's visible by default in the method's description.
2816     write_and_skip_failure(w, &mut it);
2817
2818     // Then add the remaining examples in a hidden section.
2819     if it.peek().is_some() {
2820         write!(
2821             w,
2822             "<details class=\"rustdoc-toggle more-examples-toggle\">\
2823                   <summary class=\"hideme\">\
2824                      <span>More examples</span>\
2825                   </summary>\
2826                   <div class=\"hide-more\">Hide additional examples</div>\
2827                   <div class=\"more-scraped-examples\">\
2828                     <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>\
2829                     <div class=\"more-scraped-examples-inner\">"
2830         );
2831
2832         // Only generate inline code for MAX_FULL_EXAMPLES number of examples. Otherwise we could
2833         // make the page arbitrarily huge!
2834         for _ in 0..MAX_FULL_EXAMPLES {
2835             write_and_skip_failure(w, &mut it);
2836         }
2837
2838         // For the remaining examples, generate a <ul> containing links to the source files.
2839         if it.peek().is_some() {
2840             write!(w, r#"<div class="example-links">Additional examples can be found in:<br><ul>"#);
2841             it.for_each(|(_, call_data)| {
2842                 let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
2843                 write!(
2844                     w,
2845                     r#"<li><a href="{url}">{name}</a></li>"#,
2846                     url = url,
2847                     name = call_data.display_name
2848                 );
2849             });
2850             write!(w, "</ul></div>");
2851         }
2852
2853         write!(w, "</div></div></details>");
2854     }
2855
2856     write!(w, "</div>");
2857 }