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