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