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