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