1pub(crate) mod search_index;
30
31#[cfg(test)]
32mod tests;
33
34mod context;
35mod ordered_json;
36mod print_item;
37pub(crate) mod sidebar;
38mod sorted_template;
39mod span_map;
40mod type_layout;
41mod write_shared;
42
43use std::borrow::Cow;
44use std::collections::VecDeque;
45use std::fmt::{self, Display as _, Write};
46use std::iter::Peekable;
47use std::path::PathBuf;
48use std::{fs, str};
49
50use askama::Template;
51use itertools::Either;
52use rustc_ast::join_path_syms;
53use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
54use rustc_hir::attrs::{DeprecatedSince, Deprecation};
55use rustc_hir::def_id::{DefId, DefIdSet};
56use rustc_hir::{ConstStability, Mutability, RustcVersion, StabilityLevel, StableSince};
57use rustc_middle::ty::print::PrintTraitRefExt;
58use rustc_middle::ty::{self, TyCtxt};
59use rustc_span::symbol::{Symbol, sym};
60use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName};
61use serde::ser::SerializeMap;
62use serde::{Serialize, Serializer};
63use tracing::{debug, info};
64
65pub(crate) use self::context::*;
66pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources};
67pub(crate) use self::write_shared::*;
68use crate::clean::{self, ItemId, RenderedLink};
69use crate::display::{Joined as _, MaybeDisplay as _};
70use crate::error::Error;
71use crate::formats::Impl;
72use crate::formats::cache::Cache;
73use crate::formats::item_type::ItemType;
74use crate::html::escape::Escape;
75use crate::html::format::{
76 Ending, HrefError, PrintWithSpace, href, print_abi_with_space, print_constness_with_space,
77 print_default_space, print_generic_bounds, print_where_clause, visibility_print_with_space,
78 write_str,
79};
80use crate::html::markdown::{
81 HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine,
82};
83use crate::html::static_files::SCRAPE_EXAMPLES_HELP_MD;
84use crate::html::{highlight, sources};
85use crate::scrape_examples::{CallData, CallLocation};
86use crate::{DOC_RUST_LANG_ORG_VERSION, try_none};
87
88pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display {
89 fmt::from_fn(move |f| {
90 if !v.ends_with('/') && !v.is_empty() { write!(f, "{v}/") } else { f.write_str(v) }
91 })
92}
93
94#[derive(Copy, Clone, Debug)]
97enum AssocItemRender<'a> {
98 All,
99 DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool },
100}
101
102impl AssocItemRender<'_> {
103 fn render_mode(&self) -> RenderMode {
104 match self {
105 Self::All => RenderMode::Normal,
106 &Self::DerefFor { deref_mut_, .. } => RenderMode::ForDeref { mut_: deref_mut_ },
107 }
108 }
109
110 fn class(&self) -> Option<&'static str> {
111 if let Self::DerefFor { .. } = self { Some("impl-items") } else { None }
112 }
113}
114
115#[derive(Copy, Clone, PartialEq)]
118enum RenderMode {
119 Normal,
120 ForDeref { mut_: bool },
121}
122
123#[derive(Debug)]
129pub(crate) struct IndexItem {
130 pub(crate) ty: ItemType,
131 pub(crate) defid: Option<DefId>,
132 pub(crate) name: Symbol,
133 pub(crate) module_path: Vec<Symbol>,
134 pub(crate) desc: String,
135 pub(crate) parent: Option<DefId>,
136 pub(crate) parent_idx: Option<usize>,
137 pub(crate) exact_module_path: Option<Vec<Symbol>>,
138 pub(crate) impl_id: Option<DefId>,
139 pub(crate) search_type: Option<IndexItemFunctionType>,
140 pub(crate) aliases: Box<[Symbol]>,
141 pub(crate) deprecation: Option<Deprecation>,
142 pub(crate) is_unstable: bool,
143}
144
145#[derive(Debug, Eq, PartialEq)]
147struct RenderType {
148 id: Option<RenderTypeId>,
149 generics: Option<Vec<RenderType>>,
150 bindings: Option<Vec<(RenderTypeId, Vec<RenderType>)>>,
151}
152
153impl RenderType {
154 fn size(&self) -> usize {
155 let mut size = 1;
156 if let Some(generics) = &self.generics {
157 size += generics.iter().map(RenderType::size).sum::<usize>();
158 }
159 if let Some(bindings) = &self.bindings {
160 for (_, constraints) in bindings.iter() {
161 size += 1;
162 size += constraints.iter().map(RenderType::size).sum::<usize>();
163 }
164 }
165 size
166 }
167 fn write_to_string(&self, string: &mut String) {
172 fn write_optional_id(id: Option<RenderTypeId>, string: &mut String) {
173 match id {
175 Some(id) => id.write_to_string(string),
176 None => string.push('`'),
177 }
178 }
179 if self.generics.is_some() || self.bindings.is_some() {
183 string.push('{');
184 write_optional_id(self.id, string);
185 string.push('{');
186 for generic in self.generics.as_deref().unwrap_or_default() {
187 generic.write_to_string(string);
188 }
189 string.push('}');
190 if self.bindings.is_some() {
191 string.push('{');
192 for binding in self.bindings.as_deref().unwrap_or_default() {
193 string.push('{');
194 binding.0.write_to_string(string);
195 string.push('{');
196 for constraint in &binding.1[..] {
197 constraint.write_to_string(string);
198 }
199 string.push_str("}}");
200 }
201 string.push('}');
202 }
203 string.push('}');
204 } else {
205 write_optional_id(self.id, string);
206 }
207 }
208 fn read_from_bytes(string: &[u8]) -> (RenderType, usize) {
209 let mut i = 0;
210 if string[i] == b'{' {
211 i += 1;
212 let (id, offset) = RenderTypeId::read_from_bytes(&string[i..]);
213 i += offset;
214 let generics = if string[i] == b'{' {
215 i += 1;
216 let mut generics = Vec::new();
217 while string[i] != b'}' {
218 let (ty, offset) = RenderType::read_from_bytes(&string[i..]);
219 i += offset;
220 generics.push(ty);
221 }
222 assert!(string[i] == b'}');
223 i += 1;
224 Some(generics)
225 } else {
226 None
227 };
228 let bindings = if string[i] == b'{' {
229 i += 1;
230 let mut bindings = Vec::new();
231 while string[i] == b'{' {
232 i += 1;
233 let (binding, boffset) = RenderTypeId::read_from_bytes(&string[i..]);
234 i += boffset;
235 let mut bconstraints = Vec::new();
236 assert!(string[i] == b'{');
237 i += 1;
238 while string[i] != b'}' {
239 let (constraint, coffset) = RenderType::read_from_bytes(&string[i..]);
240 i += coffset;
241 bconstraints.push(constraint);
242 }
243 assert!(string[i] == b'}');
244 i += 1;
245 bindings.push((binding.unwrap(), bconstraints));
246 assert!(string[i] == b'}');
247 i += 1;
248 }
249 assert!(string[i] == b'}');
250 i += 1;
251 Some(bindings)
252 } else {
253 None
254 };
255 assert!(string[i] == b'}');
256 i += 1;
257 (RenderType { id, generics, bindings }, i)
258 } else {
259 let (id, offset) = RenderTypeId::read_from_bytes(string);
260 i += offset;
261 (RenderType { id, generics: None, bindings: None }, i)
262 }
263 }
264}
265
266#[derive(Clone, Copy, Debug, Eq, PartialEq)]
267enum RenderTypeId {
268 DefId(DefId),
269 Primitive(clean::PrimitiveType),
270 AssociatedType(Symbol),
271 Index(isize),
272 Mut,
273}
274
275impl RenderTypeId {
276 fn write_to_string(&self, string: &mut String) {
277 let id: i32 = match &self {
278 RenderTypeId::Index(idx) if *idx >= 0 => (idx + 1isize).try_into().unwrap(),
281 RenderTypeId::Index(idx) => (*idx).try_into().unwrap(),
283 _ => panic!("must convert render types to indexes before serializing"),
284 };
285 search_index::encode::write_signed_vlqhex_to_string(id, string);
286 }
287 fn read_from_bytes(string: &[u8]) -> (Option<RenderTypeId>, usize) {
288 let Some((value, offset)) = search_index::encode::read_signed_vlqhex_from_string(string)
289 else {
290 return (None, 0);
291 };
292 let value = isize::try_from(value).unwrap();
293 let ty = match value {
294 ..0 => Some(RenderTypeId::Index(value)),
295 0 => None,
296 1.. => Some(RenderTypeId::Index(value - 1)),
297 };
298 (ty, offset)
299 }
300}
301
302#[derive(Debug, Eq, PartialEq)]
304pub(crate) struct IndexItemFunctionType {
305 inputs: Vec<RenderType>,
306 output: Vec<RenderType>,
307 where_clause: Vec<Vec<RenderType>>,
308 param_names: Vec<Option<Symbol>>,
309}
310
311impl IndexItemFunctionType {
312 fn size(&self) -> usize {
313 self.inputs.iter().map(RenderType::size).sum::<usize>()
314 + self.output.iter().map(RenderType::size).sum::<usize>()
315 + self
316 .where_clause
317 .iter()
318 .map(|constraints| constraints.iter().map(RenderType::size).sum::<usize>())
319 .sum::<usize>()
320 }
321 fn read_from_string_without_param_names(string: &[u8]) -> (IndexItemFunctionType, usize) {
322 let mut i = 0;
323 if string[i] == b'`' {
324 return (
325 IndexItemFunctionType {
326 inputs: Vec::new(),
327 output: Vec::new(),
328 where_clause: Vec::new(),
329 param_names: Vec::new(),
330 },
331 1,
332 );
333 }
334 assert_eq!(b'{', string[i]);
335 i += 1;
336 fn read_args_from_string(string: &[u8]) -> (Vec<RenderType>, usize) {
337 let mut i = 0;
338 let mut params = Vec::new();
339 if string[i] == b'{' {
340 i += 1;
342 while string[i] != b'}' {
343 let (ty, offset) = RenderType::read_from_bytes(&string[i..]);
344 i += offset;
345 params.push(ty);
346 }
347 i += 1;
348 } else if string[i] != b'}' {
349 let (tyid, offset) = RenderTypeId::read_from_bytes(&string[i..]);
350 params.push(RenderType { id: tyid, generics: None, bindings: None });
351 i += offset;
352 }
353 (params, i)
354 }
355 let (inputs, offset) = read_args_from_string(&string[i..]);
356 i += offset;
357 let (output, offset) = read_args_from_string(&string[i..]);
358 i += offset;
359 let mut where_clause = Vec::new();
360 while string[i] != b'}' {
361 let (constraint, offset) = read_args_from_string(&string[i..]);
362 i += offset;
363 where_clause.push(constraint);
364 }
365 assert_eq!(b'}', string[i], "{} {}", String::from_utf8_lossy(&string), i);
366 i += 1;
367 (IndexItemFunctionType { inputs, output, where_clause, param_names: Vec::new() }, i)
368 }
369 fn write_to_string_without_param_names<'a>(&'a self, string: &mut String) {
370 let has_missing = self
373 .inputs
374 .iter()
375 .chain(self.output.iter())
376 .any(|i| i.id.is_none() && i.generics.is_none());
377 if has_missing {
378 string.push('`');
379 } else {
380 string.push('{');
381 match &self.inputs[..] {
382 [one] if one.generics.is_none() && one.bindings.is_none() => {
383 one.write_to_string(string);
384 }
385 _ => {
386 string.push('{');
387 for item in &self.inputs[..] {
388 item.write_to_string(string);
389 }
390 string.push('}');
391 }
392 }
393 match &self.output[..] {
394 [] if self.where_clause.is_empty() => {}
395 [one] if one.generics.is_none() && one.bindings.is_none() => {
396 one.write_to_string(string);
397 }
398 _ => {
399 string.push('{');
400 for item in &self.output[..] {
401 item.write_to_string(string);
402 }
403 string.push('}');
404 }
405 }
406 for constraint in &self.where_clause {
407 if let [one] = &constraint[..]
408 && one.generics.is_none()
409 && one.bindings.is_none()
410 {
411 one.write_to_string(string);
412 } else {
413 string.push('{');
414 for item in &constraint[..] {
415 item.write_to_string(string);
416 }
417 string.push('}');
418 }
419 }
420 string.push('}');
421 }
422 }
423}
424
425#[derive(Debug, Clone)]
426pub(crate) struct StylePath {
427 pub(crate) path: PathBuf,
429}
430
431impl StylePath {
432 pub(crate) fn basename(&self) -> Result<String, Error> {
433 Ok(try_none!(try_none!(self.path.file_stem(), &self.path).to_str(), &self.path).to_string())
434 }
435}
436
437#[derive(Debug, Eq, PartialEq, Hash)]
438struct ItemEntry {
439 url: String,
440 name: String,
441}
442
443impl ItemEntry {
444 fn new(mut url: String, name: String) -> ItemEntry {
445 while url.starts_with('/') {
446 url.remove(0);
447 }
448 ItemEntry { url, name }
449 }
450}
451
452impl ItemEntry {
453 fn print(&self) -> impl fmt::Display {
454 fmt::from_fn(move |f| write!(f, "<a href=\"{}\">{}</a>", self.url, Escape(&self.name)))
455 }
456}
457
458impl PartialOrd for ItemEntry {
459 fn partial_cmp(&self, other: &ItemEntry) -> Option<::std::cmp::Ordering> {
460 Some(self.cmp(other))
461 }
462}
463
464impl Ord for ItemEntry {
465 fn cmp(&self, other: &ItemEntry) -> ::std::cmp::Ordering {
466 self.name.cmp(&other.name)
467 }
468}
469
470#[derive(Debug)]
471struct AllTypes {
472 structs: FxIndexSet<ItemEntry>,
473 enums: FxIndexSet<ItemEntry>,
474 unions: FxIndexSet<ItemEntry>,
475 primitives: FxIndexSet<ItemEntry>,
476 traits: FxIndexSet<ItemEntry>,
477 macros: FxIndexSet<ItemEntry>,
478 functions: FxIndexSet<ItemEntry>,
479 type_aliases: FxIndexSet<ItemEntry>,
480 statics: FxIndexSet<ItemEntry>,
481 constants: FxIndexSet<ItemEntry>,
482 attribute_macros: FxIndexSet<ItemEntry>,
483 derive_macros: FxIndexSet<ItemEntry>,
484 trait_aliases: FxIndexSet<ItemEntry>,
485}
486
487impl AllTypes {
488 fn new() -> AllTypes {
489 let new_set = |cap| FxIndexSet::with_capacity_and_hasher(cap, Default::default());
490 AllTypes {
491 structs: new_set(100),
492 enums: new_set(100),
493 unions: new_set(100),
494 primitives: new_set(26),
495 traits: new_set(100),
496 macros: new_set(100),
497 functions: new_set(100),
498 type_aliases: new_set(100),
499 statics: new_set(100),
500 constants: new_set(100),
501 attribute_macros: new_set(100),
502 derive_macros: new_set(100),
503 trait_aliases: new_set(100),
504 }
505 }
506
507 fn append(&mut self, item_name: String, item_type: &ItemType) {
508 let mut url: Vec<_> = item_name.split("::").skip(1).collect();
509 if let Some(name) = url.pop() {
510 let new_url = format!("{}/{item_type}.{name}.html", url.join("/"));
511 url.push(name);
512 let name = url.join("::");
513 match *item_type {
514 ItemType::Struct => self.structs.insert(ItemEntry::new(new_url, name)),
515 ItemType::Enum => self.enums.insert(ItemEntry::new(new_url, name)),
516 ItemType::Union => self.unions.insert(ItemEntry::new(new_url, name)),
517 ItemType::Primitive => self.primitives.insert(ItemEntry::new(new_url, name)),
518 ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)),
519 ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)),
520 ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)),
521 ItemType::TypeAlias => self.type_aliases.insert(ItemEntry::new(new_url, name)),
522 ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)),
523 ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)),
524 ItemType::ProcAttribute => {
525 self.attribute_macros.insert(ItemEntry::new(new_url, name))
526 }
527 ItemType::ProcDerive => self.derive_macros.insert(ItemEntry::new(new_url, name)),
528 ItemType::TraitAlias => self.trait_aliases.insert(ItemEntry::new(new_url, name)),
529 _ => true,
530 };
531 }
532 }
533
534 fn item_sections(&self) -> FxHashSet<ItemSection> {
535 let mut sections = FxHashSet::default();
536
537 if !self.structs.is_empty() {
538 sections.insert(ItemSection::Structs);
539 }
540 if !self.enums.is_empty() {
541 sections.insert(ItemSection::Enums);
542 }
543 if !self.unions.is_empty() {
544 sections.insert(ItemSection::Unions);
545 }
546 if !self.primitives.is_empty() {
547 sections.insert(ItemSection::PrimitiveTypes);
548 }
549 if !self.traits.is_empty() {
550 sections.insert(ItemSection::Traits);
551 }
552 if !self.macros.is_empty() {
553 sections.insert(ItemSection::Macros);
554 }
555 if !self.functions.is_empty() {
556 sections.insert(ItemSection::Functions);
557 }
558 if !self.type_aliases.is_empty() {
559 sections.insert(ItemSection::TypeAliases);
560 }
561 if !self.statics.is_empty() {
562 sections.insert(ItemSection::Statics);
563 }
564 if !self.constants.is_empty() {
565 sections.insert(ItemSection::Constants);
566 }
567 if !self.attribute_macros.is_empty() {
568 sections.insert(ItemSection::AttributeMacros);
569 }
570 if !self.derive_macros.is_empty() {
571 sections.insert(ItemSection::DeriveMacros);
572 }
573 if !self.trait_aliases.is_empty() {
574 sections.insert(ItemSection::TraitAliases);
575 }
576
577 sections
578 }
579
580 fn print(&self) -> impl fmt::Display {
581 fn print_entries(e: &FxIndexSet<ItemEntry>, kind: ItemSection) -> impl fmt::Display {
582 fmt::from_fn(move |f| {
583 if e.is_empty() {
584 return Ok(());
585 }
586
587 let mut e: Vec<&ItemEntry> = e.iter().collect();
588 e.sort();
589 write!(
590 f,
591 "<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">",
592 id = kind.id(),
593 title = kind.name(),
594 )?;
595
596 for s in e.iter() {
597 write!(f, "<li>{}</li>", s.print())?;
598 }
599
600 f.write_str("</ul>")
601 })
602 }
603
604 fmt::from_fn(|f| {
605 f.write_str("<h1>List of all items</h1>")?;
606 print_entries(&self.structs, ItemSection::Structs).fmt(f)?;
609 print_entries(&self.enums, ItemSection::Enums).fmt(f)?;
610 print_entries(&self.unions, ItemSection::Unions).fmt(f)?;
611 print_entries(&self.primitives, ItemSection::PrimitiveTypes).fmt(f)?;
612 print_entries(&self.traits, ItemSection::Traits).fmt(f)?;
613 print_entries(&self.macros, ItemSection::Macros).fmt(f)?;
614 print_entries(&self.attribute_macros, ItemSection::AttributeMacros).fmt(f)?;
615 print_entries(&self.derive_macros, ItemSection::DeriveMacros).fmt(f)?;
616 print_entries(&self.functions, ItemSection::Functions).fmt(f)?;
617 print_entries(&self.type_aliases, ItemSection::TypeAliases).fmt(f)?;
618 print_entries(&self.trait_aliases, ItemSection::TraitAliases).fmt(f)?;
619 print_entries(&self.statics, ItemSection::Statics).fmt(f)?;
620 print_entries(&self.constants, ItemSection::Constants).fmt(f)?;
621 Ok(())
622 })
623 }
624}
625
626fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
627 let mut content = SCRAPE_EXAMPLES_HELP_MD.to_owned();
628 content.push_str(&format!(
629 "## More information\n\n\
630 If you want more information about this feature, please read the [corresponding chapter in \
631 the Rustdoc book]({DOC_RUST_LANG_ORG_VERSION}/rustdoc/scraped-examples.html)."
632 ));
633
634 format!(
635 "<div class=\"main-heading\">\
636 <h1>About scraped examples</h1>\
637 </div>\
638 <div>{}</div>",
639 fmt::from_fn(|f| Markdown {
640 content: &content,
641 links: &[],
642 ids: &mut IdMap::default(),
643 error_codes: shared.codes,
644 edition: shared.edition(),
645 playground: &shared.playground,
646 heading_offset: HeadingOffset::H1,
647 }
648 .write_into(f))
649 )
650}
651
652fn document(
653 cx: &Context<'_>,
654 item: &clean::Item,
655 parent: Option<&clean::Item>,
656 heading_offset: HeadingOffset,
657) -> impl fmt::Display {
658 if let Some(ref name) = item.name {
659 info!("Documenting {name}");
660 }
661
662 fmt::from_fn(move |f| {
663 document_item_info(cx, item, parent).render_into(f)?;
664 if parent.is_none() {
665 write!(f, "{}", document_full_collapsible(item, cx, heading_offset))
666 } else {
667 write!(f, "{}", document_full(item, cx, heading_offset))
668 }
669 })
670}
671
672fn render_markdown(
674 cx: &Context<'_>,
675 md_text: &str,
676 links: Vec<RenderedLink>,
677 heading_offset: HeadingOffset,
678) -> impl fmt::Display {
679 fmt::from_fn(move |f| {
680 f.write_str("<div class=\"docblock\">")?;
681 Markdown {
682 content: md_text,
683 links: &links,
684 ids: &mut cx.id_map.borrow_mut(),
685 error_codes: cx.shared.codes,
686 edition: cx.shared.edition(),
687 playground: &cx.shared.playground,
688 heading_offset,
689 }
690 .write_into(&mut *f)?;
691 f.write_str("</div>")
692 })
693}
694
695fn document_short(
698 item: &clean::Item,
699 cx: &Context<'_>,
700 link: AssocItemLink<'_>,
701 parent: &clean::Item,
702 show_def_docs: bool,
703) -> impl fmt::Display {
704 fmt::from_fn(move |f| {
705 document_item_info(cx, item, Some(parent)).render_into(f)?;
706 if !show_def_docs {
707 return Ok(());
708 }
709 let s = item.doc_value();
710 if !s.is_empty() {
711 let (mut summary_html, has_more_content) =
712 MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content();
713
714 let link = if has_more_content {
715 let link = fmt::from_fn(|f| {
716 write!(
717 f,
718 " <a{}>Read more</a>",
719 assoc_href_attr(item, link, cx).maybe_display()
720 )
721 });
722
723 if let Some(idx) = summary_html.rfind("</p>") {
724 summary_html.insert_str(idx, &link.to_string());
725 None
726 } else {
727 Some(link)
728 }
729 } else {
730 None
731 }
732 .maybe_display();
733
734 write!(f, "<div class='docblock'>{summary_html}{link}</div>")?;
735 }
736 Ok(())
737 })
738}
739
740fn document_full_collapsible(
741 item: &clean::Item,
742 cx: &Context<'_>,
743 heading_offset: HeadingOffset,
744) -> impl fmt::Display {
745 document_full_inner(item, cx, true, heading_offset)
746}
747
748fn document_full(
749 item: &clean::Item,
750 cx: &Context<'_>,
751 heading_offset: HeadingOffset,
752) -> impl fmt::Display {
753 document_full_inner(item, cx, false, heading_offset)
754}
755
756fn document_full_inner(
757 item: &clean::Item,
758 cx: &Context<'_>,
759 is_collapsible: bool,
760 heading_offset: HeadingOffset,
761) -> impl fmt::Display {
762 fmt::from_fn(move |f| {
763 if let Some(s) = item.opt_doc_value() {
764 debug!("Doc block: =====\n{s}\n=====");
765 if is_collapsible {
766 write!(
767 f,
768 "<details class=\"toggle top-doc\" open>\
769 <summary class=\"hideme\">\
770 <span>Expand description</span>\
771 </summary>{}</details>",
772 render_markdown(cx, &s, item.links(cx), heading_offset)
773 )?;
774 } else {
775 write!(f, "{}", render_markdown(cx, &s, item.links(cx), heading_offset))?;
776 }
777 }
778
779 let kind = match &item.kind {
780 clean::ItemKind::StrippedItem(box kind) | kind => kind,
781 };
782
783 if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind {
784 render_call_locations(f, cx, item)?;
785 }
786 Ok(())
787 })
788}
789
790#[derive(Template)]
791#[template(path = "item_info.html")]
792struct ItemInfo {
793 items: Vec<ShortItemInfo>,
794}
795fn document_item_info(
801 cx: &Context<'_>,
802 item: &clean::Item,
803 parent: Option<&clean::Item>,
804) -> ItemInfo {
805 let items = short_item_info(item, cx, parent);
806 ItemInfo { items }
807}
808
809fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> {
810 let cfg = match (&item.cfg, parent.and_then(|p| p.cfg.as_ref())) {
811 (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
812 (cfg, _) => cfg.as_deref().cloned(),
813 };
814
815 debug!(
816 "Portability {name:?} {item_cfg:?} (parent: {parent:?}) - {parent_cfg:?} = {cfg:?}",
817 name = item.name,
818 item_cfg = item.cfg,
819 parent_cfg = parent.and_then(|p| p.cfg.as_ref()),
820 );
821
822 Some(cfg?.render_long_html())
823}
824
825#[derive(Template)]
826#[template(path = "short_item_info.html")]
827enum ShortItemInfo {
828 Deprecation {
830 message: String,
831 },
832 Unstable {
835 feature: String,
836 tracking: Option<(String, u32)>,
837 },
838 Portability {
839 message: String,
840 },
841}
842
843fn short_item_info(
846 item: &clean::Item,
847 cx: &Context<'_>,
848 parent: Option<&clean::Item>,
849) -> Vec<ShortItemInfo> {
850 let mut extra_info = vec![];
851
852 if let Some(depr @ Deprecation { note, since, suggestion: _ }) = item.deprecation(cx.tcx()) {
853 let mut message = match since {
856 DeprecatedSince::RustcVersion(version) => {
857 if depr.is_in_effect() {
858 format!("Deprecated since {version}")
859 } else {
860 format!("Deprecating in {version}")
861 }
862 }
863 DeprecatedSince::Future => String::from("Deprecating in a future version"),
864 DeprecatedSince::NonStandard(since) => {
865 format!("Deprecated since {}", Escape(since.as_str()))
866 }
867 DeprecatedSince::Unspecified | DeprecatedSince::Err => String::from("Deprecated"),
868 };
869
870 if let Some(note) = note {
871 let note = note.as_str();
872 let mut id_map = cx.id_map.borrow_mut();
873 let html = MarkdownItemInfo(note, &mut id_map);
874 message.push_str(": ");
875 html.write_into(&mut message).unwrap();
876 }
877 extra_info.push(ShortItemInfo::Deprecation { message });
878 }
879
880 if let Some((StabilityLevel::Unstable { reason: _, issue, .. }, feature)) = item
883 .stability(cx.tcx())
884 .as_ref()
885 .filter(|stab| stab.feature != sym::rustc_private)
886 .map(|stab| (stab.level, stab.feature))
887 {
888 let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue)
889 {
890 Some((url.clone(), issue.get()))
891 } else {
892 None
893 };
894 extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking });
895 }
896
897 if let Some(message) = portability(item, parent) {
898 extra_info.push(ShortItemInfo::Portability { message });
899 }
900
901 extra_info
902}
903
904fn render_impls(
907 cx: &Context<'_>,
908 mut w: impl Write,
909 impls: &[&Impl],
910 containing_item: &clean::Item,
911 toggle_open_by_default: bool,
912) {
913 let mut rendered_impls = impls
914 .iter()
915 .map(|i| {
916 let did = i.trait_did().unwrap();
917 let provided_trait_methods = i.inner_impl().provided_trait_methods(cx.tcx());
918 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods);
919 let imp = render_impl(
920 cx,
921 i,
922 containing_item,
923 assoc_link,
924 RenderMode::Normal,
925 None,
926 &[],
927 ImplRenderingParameters {
928 show_def_docs: true,
929 show_default_items: true,
930 show_non_assoc_items: true,
931 toggle_open_by_default,
932 },
933 );
934 imp.to_string()
935 })
936 .collect::<Vec<_>>();
937 rendered_impls.sort();
938 w.write_str(&rendered_impls.join("")).unwrap();
939}
940
941fn assoc_href_attr(
943 it: &clean::Item,
944 link: AssocItemLink<'_>,
945 cx: &Context<'_>,
946) -> Option<impl fmt::Display> {
947 let name = it.name.unwrap();
948 let item_type = it.type_();
949
950 enum Href<'a> {
951 AnchorId(&'a str),
952 Anchor(ItemType),
953 Url(String, ItemType),
954 }
955
956 let href = match link {
957 AssocItemLink::Anchor(Some(id)) => Href::AnchorId(id),
958 AssocItemLink::Anchor(None) => Href::Anchor(item_type),
959 AssocItemLink::GotoSource(did, provided_methods) => {
960 let item_type = match item_type {
963 ItemType::Method | ItemType::TyMethod => {
967 if provided_methods.contains(&name) {
968 ItemType::Method
969 } else {
970 ItemType::TyMethod
971 }
972 }
973 item_type => item_type,
975 };
976
977 match href(did.expect_def_id(), cx) {
978 Ok((url, ..)) => Href::Url(url, item_type),
979 Err(HrefError::DocumentationNotBuilt) => return None,
991 Err(_) => Href::Anchor(item_type),
992 }
993 }
994 };
995
996 let href = fmt::from_fn(move |f| match &href {
997 Href::AnchorId(id) => write!(f, "#{id}"),
998 Href::Url(url, item_type) => {
999 write!(f, "{url}#{item_type}.{name}")
1000 }
1001 Href::Anchor(item_type) => {
1002 write!(f, "#{item_type}.{name}")
1003 }
1004 });
1005
1006 Some(fmt::from_fn(move |f| write!(f, " href=\"{href}\"")))
1009}
1010
1011#[derive(Debug)]
1012enum AssocConstValue<'a> {
1013 TraitDefault(&'a clean::ConstantKind),
1017 Impl(&'a clean::ConstantKind),
1019 None,
1020}
1021
1022fn assoc_const(
1023 it: &clean::Item,
1024 generics: &clean::Generics,
1025 ty: &clean::Type,
1026 value: AssocConstValue<'_>,
1027 link: AssocItemLink<'_>,
1028 indent: usize,
1029 cx: &Context<'_>,
1030) -> impl fmt::Display {
1031 let tcx = cx.tcx();
1032 fmt::from_fn(move |w| {
1033 write!(
1034 w,
1035 "{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
1036 indent = " ".repeat(indent),
1037 vis = visibility_print_with_space(it, cx),
1038 href = assoc_href_attr(it, link, cx).maybe_display(),
1039 name = it.name.as_ref().unwrap(),
1040 generics = generics.print(cx),
1041 ty = ty.print(cx),
1042 )?;
1043 if let AssocConstValue::TraitDefault(konst) | AssocConstValue::Impl(konst) = value {
1044 let repr = konst.value(tcx).unwrap_or_else(|| konst.expr(tcx));
1050 if match value {
1051 AssocConstValue::TraitDefault(_) => true, AssocConstValue::Impl(_) => repr != "_", AssocConstValue::None => unreachable!(),
1054 } {
1055 write!(w, " = {}", Escape(&repr))?;
1056 }
1057 }
1058 write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display())
1059 })
1060}
1061
1062fn assoc_type(
1063 it: &clean::Item,
1064 generics: &clean::Generics,
1065 bounds: &[clean::GenericBound],
1066 default: Option<&clean::Type>,
1067 link: AssocItemLink<'_>,
1068 indent: usize,
1069 cx: &Context<'_>,
1070) -> impl fmt::Display {
1071 fmt::from_fn(move |w| {
1072 write!(
1073 w,
1074 "{indent}{vis}type <a{href} class=\"associatedtype\">{name}</a>{generics}",
1075 indent = " ".repeat(indent),
1076 vis = visibility_print_with_space(it, cx),
1077 href = assoc_href_attr(it, link, cx).maybe_display(),
1078 name = it.name.as_ref().unwrap(),
1079 generics = generics.print(cx),
1080 )?;
1081 if !bounds.is_empty() {
1082 write!(w, ": {}", print_generic_bounds(bounds, cx))?;
1083 }
1084 if let Some(default) = default {
1086 write!(w, " = {}", default.print(cx))?;
1087 }
1088 write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display())
1089 })
1090}
1091
1092fn assoc_method(
1093 meth: &clean::Item,
1094 g: &clean::Generics,
1095 d: &clean::FnDecl,
1096 link: AssocItemLink<'_>,
1097 parent: ItemType,
1098 cx: &Context<'_>,
1099 render_mode: RenderMode,
1100) -> impl fmt::Display {
1101 let tcx = cx.tcx();
1102 let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item");
1103 let name = meth.name.as_ref().unwrap();
1104 let vis = visibility_print_with_space(meth, cx).to_string();
1105 let defaultness = print_default_space(meth.is_default());
1106 let constness = match render_mode {
1109 RenderMode::Normal => print_constness_with_space(
1110 &header.constness,
1111 meth.stable_since(tcx),
1112 meth.const_stability(tcx),
1113 ),
1114 RenderMode::ForDeref { .. } => "",
1115 };
1116
1117 fmt::from_fn(move |w| {
1118 let asyncness = header.asyncness.print_with_space();
1119 let safety = header.safety.print_with_space();
1120 let abi = print_abi_with_space(header.abi).to_string();
1121 let href = assoc_href_attr(meth, link, cx).maybe_display();
1122
1123 let generics_len = format!("{:#}", g.print(cx)).len();
1125 let mut header_len = "fn ".len()
1126 + vis.len()
1127 + defaultness.len()
1128 + constness.len()
1129 + asyncness.len()
1130 + safety.len()
1131 + abi.len()
1132 + name.as_str().len()
1133 + generics_len;
1134
1135 let notable_traits = notable_traits_button(&d.output, cx).maybe_display();
1136
1137 let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
1138 header_len += 4;
1139 let indent_str = " ";
1140 write!(w, "{}", render_attributes_in_pre(meth, indent_str, cx))?;
1141 (4, indent_str, Ending::NoNewline)
1142 } else {
1143 render_attributes_in_code(w, meth, cx);
1144 (0, "", Ending::Newline)
1145 };
1146 write!(
1147 w,
1148 "{indent}{vis}{defaultness}{constness}{asyncness}{safety}{abi}fn \
1149 <a{href} class=\"fn\">{name}</a>{generics}{decl}{notable_traits}{where_clause}",
1150 indent = indent_str,
1151 generics = g.print(cx),
1152 decl = d.full_print(header_len, indent, cx),
1153 where_clause = print_where_clause(g, cx, indent, end_newline).maybe_display(),
1154 )
1155 })
1156}
1157
1158fn render_stability_since_raw_with_extra(
1173 stable_version: Option<StableSince>,
1174 const_stability: Option<ConstStability>,
1175 extra_class: &str,
1176) -> Option<impl fmt::Display> {
1177 let mut title = String::new();
1178 let mut stability = String::new();
1179
1180 if let Some(version) = stable_version.and_then(|version| since_to_string(&version)) {
1181 stability.push_str(&version);
1182 title.push_str(&format!("Stable since Rust version {version}"));
1183 }
1184
1185 let const_title_and_stability = match const_stability {
1186 Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) => {
1187 since_to_string(&since)
1188 .map(|since| (format!("const since {since}"), format!("const: {since}")))
1189 }
1190 Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => {
1191 if stable_version.is_none() {
1192 None
1194 } else {
1195 let unstable = if let Some(n) = issue {
1196 format!(
1197 "<a \
1198 href=\"https://github.com/rust-lang/rust/issues/{n}\" \
1199 title=\"Tracking issue for {feature}\"\
1200 >unstable</a>"
1201 )
1202 } else {
1203 String::from("unstable")
1204 };
1205
1206 Some((String::from("const unstable"), format!("const: {unstable}")))
1207 }
1208 }
1209 _ => None,
1210 };
1211
1212 if let Some((const_title, const_stability)) = const_title_and_stability {
1213 if !title.is_empty() {
1214 title.push_str(&format!(", {const_title}"));
1215 } else {
1216 title.push_str(&const_title);
1217 }
1218
1219 if !stability.is_empty() {
1220 stability.push_str(&format!(" ({const_stability})"));
1221 } else {
1222 stability.push_str(&const_stability);
1223 }
1224 }
1225
1226 (!stability.is_empty()).then_some(fmt::from_fn(move |w| {
1227 write!(w, r#"<span class="since{extra_class}" title="{title}">{stability}</span>"#)
1228 }))
1229}
1230
1231fn since_to_string(since: &StableSince) -> Option<String> {
1232 match since {
1233 StableSince::Version(since) => Some(since.to_string()),
1234 StableSince::Current => Some(RustcVersion::CURRENT.to_string()),
1235 StableSince::Err(_) => None,
1236 }
1237}
1238
1239#[inline]
1240fn render_stability_since_raw(
1241 ver: Option<StableSince>,
1242 const_stability: Option<ConstStability>,
1243) -> Option<impl fmt::Display> {
1244 render_stability_since_raw_with_extra(ver, const_stability, "")
1245}
1246
1247fn render_assoc_item(
1248 item: &clean::Item,
1249 link: AssocItemLink<'_>,
1250 parent: ItemType,
1251 cx: &Context<'_>,
1252 render_mode: RenderMode,
1253) -> impl fmt::Display {
1254 fmt::from_fn(move |f| match &item.kind {
1255 clean::StrippedItem(..) => Ok(()),
1256 clean::RequiredMethodItem(m) | clean::MethodItem(m, _) => {
1257 assoc_method(item, &m.generics, &m.decl, link, parent, cx, render_mode).fmt(f)
1258 }
1259 clean::RequiredAssocConstItem(generics, ty) => assoc_const(
1260 item,
1261 generics,
1262 ty,
1263 AssocConstValue::None,
1264 link,
1265 if parent == ItemType::Trait { 4 } else { 0 },
1266 cx,
1267 )
1268 .fmt(f),
1269 clean::ProvidedAssocConstItem(ci) => assoc_const(
1270 item,
1271 &ci.generics,
1272 &ci.type_,
1273 AssocConstValue::TraitDefault(&ci.kind),
1274 link,
1275 if parent == ItemType::Trait { 4 } else { 0 },
1276 cx,
1277 )
1278 .fmt(f),
1279 clean::ImplAssocConstItem(ci) => assoc_const(
1280 item,
1281 &ci.generics,
1282 &ci.type_,
1283 AssocConstValue::Impl(&ci.kind),
1284 link,
1285 if parent == ItemType::Trait { 4 } else { 0 },
1286 cx,
1287 )
1288 .fmt(f),
1289 clean::RequiredAssocTypeItem(generics, bounds) => assoc_type(
1290 item,
1291 generics,
1292 bounds,
1293 None,
1294 link,
1295 if parent == ItemType::Trait { 4 } else { 0 },
1296 cx,
1297 )
1298 .fmt(f),
1299 clean::AssocTypeItem(ty, bounds) => assoc_type(
1300 item,
1301 &ty.generics,
1302 bounds,
1303 Some(ty.item_type.as_ref().unwrap_or(&ty.type_)),
1304 link,
1305 if parent == ItemType::Trait { 4 } else { 0 },
1306 cx,
1307 )
1308 .fmt(f),
1309 _ => panic!("render_assoc_item called on non-associated-item"),
1310 })
1311}
1312
1313fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display {
1316 fmt::from_fn(move |f| {
1317 for a in it.attributes(cx.tcx(), cx.cache()) {
1318 writeln!(f, "{prefix}{a}")?;
1319 }
1320 Ok(())
1321 })
1322}
1323
1324struct CodeAttribute(String);
1325
1326fn render_code_attribute(code_attr: CodeAttribute, w: &mut impl fmt::Write) {
1327 write!(w, "<div class=\"code-attribute\">{}</div>", code_attr.0).unwrap();
1328}
1329
1330fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) {
1333 for attr in it.attributes(cx.tcx(), cx.cache()) {
1334 render_code_attribute(CodeAttribute(attr), w);
1335 }
1336}
1337
1338fn render_repr_attributes_in_code(
1340 w: &mut impl fmt::Write,
1341 cx: &Context<'_>,
1342 def_id: DefId,
1343 item_type: ItemType,
1344) {
1345 if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) {
1346 render_code_attribute(CodeAttribute(repr), w);
1347 }
1348}
1349
1350#[derive(Copy, Clone)]
1351enum AssocItemLink<'a> {
1352 Anchor(Option<&'a str>),
1353 GotoSource(ItemId, &'a FxIndexSet<Symbol>),
1354}
1355
1356impl<'a> AssocItemLink<'a> {
1357 fn anchor(&self, id: &'a str) -> Self {
1358 match *self {
1359 AssocItemLink::Anchor(_) => AssocItemLink::Anchor(Some(id)),
1360 ref other => *other,
1361 }
1362 }
1363}
1364
1365fn write_section_heading(
1366 title: impl fmt::Display,
1367 id: &str,
1368 extra_class: Option<&str>,
1369 extra: impl fmt::Display,
1370) -> impl fmt::Display {
1371 fmt::from_fn(move |w| {
1372 let (extra_class, whitespace) = match extra_class {
1373 Some(extra) => (extra, " "),
1374 None => ("", ""),
1375 };
1376 write!(
1377 w,
1378 "<h2 id=\"{id}\" class=\"{extra_class}{whitespace}section-header\">\
1379 {title}\
1380 <a href=\"#{id}\" class=\"anchor\">§</a>\
1381 </h2>{extra}",
1382 )
1383 })
1384}
1385
1386fn write_impl_section_heading(title: impl fmt::Display, id: &str) -> impl fmt::Display {
1387 write_section_heading(title, id, None, "")
1388}
1389
1390fn render_all_impls(
1391 mut w: impl Write,
1392 cx: &Context<'_>,
1393 containing_item: &clean::Item,
1394 concrete: &[&Impl],
1395 synthetic: &[&Impl],
1396 blanket_impl: &[&Impl],
1397) {
1398 let impls = {
1399 let mut buf = String::new();
1400 render_impls(cx, &mut buf, concrete, containing_item, true);
1401 buf
1402 };
1403 if !impls.is_empty() {
1404 write!(
1405 w,
1406 "{}<div id=\"trait-implementations-list\">{impls}</div>",
1407 write_impl_section_heading("Trait Implementations", "trait-implementations")
1408 )
1409 .unwrap();
1410 }
1411
1412 if !synthetic.is_empty() {
1413 write!(
1414 w,
1415 "{}<div id=\"synthetic-implementations-list\">",
1416 write_impl_section_heading("Auto Trait Implementations", "synthetic-implementations",)
1417 )
1418 .unwrap();
1419 render_impls(cx, &mut w, synthetic, containing_item, false);
1420 w.write_str("</div>").unwrap();
1421 }
1422
1423 if !blanket_impl.is_empty() {
1424 write!(
1425 w,
1426 "{}<div id=\"blanket-implementations-list\">",
1427 write_impl_section_heading("Blanket Implementations", "blanket-implementations")
1428 )
1429 .unwrap();
1430 render_impls(cx, &mut w, blanket_impl, containing_item, false);
1431 w.write_str("</div>").unwrap();
1432 }
1433}
1434
1435fn render_assoc_items(
1436 cx: &Context<'_>,
1437 containing_item: &clean::Item,
1438 it: DefId,
1439 what: AssocItemRender<'_>,
1440) -> impl fmt::Display {
1441 fmt::from_fn(move |f| {
1442 let mut derefs = DefIdSet::default();
1443 derefs.insert(it);
1444 render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
1445 Ok(())
1446 })
1447}
1448
1449fn render_assoc_items_inner(
1450 mut w: &mut dyn fmt::Write,
1451 cx: &Context<'_>,
1452 containing_item: &clean::Item,
1453 it: DefId,
1454 what: AssocItemRender<'_>,
1455 derefs: &mut DefIdSet,
1456) {
1457 info!("Documenting associated items of {:?}", containing_item.name);
1458 let cache = &cx.shared.cache;
1459 let Some(v) = cache.impls.get(&it) else { return };
1460 let (mut non_trait, traits): (Vec<_>, _) =
1461 v.iter().partition(|i| i.inner_impl().trait_.is_none());
1462 if !non_trait.is_empty() {
1463 let render_mode = what.render_mode();
1464 let class_html = what
1465 .class()
1466 .map(|class| fmt::from_fn(move |f| write!(f, r#" class="{class}""#)))
1467 .maybe_display();
1468 let (section_heading, id) = match what {
1469 AssocItemRender::All => (
1470 Either::Left(write_impl_section_heading("Implementations", "implementations")),
1471 Cow::Borrowed("implementations-list"),
1472 ),
1473 AssocItemRender::DerefFor { trait_, type_, .. } => {
1474 let id =
1475 cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
1476 non_trait.retain(|impl_| {
1484 type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache)
1485 });
1486 let derived_id = cx.derive_id(&id);
1487 if let Some(def_id) = type_.def_id(cx.cache()) {
1488 cx.deref_id_map.borrow_mut().insert(def_id, id.clone());
1489 }
1490 (
1491 Either::Right(fmt::from_fn(move |f| {
1492 write!(
1493 f,
1494 "<details class=\"toggle big-toggle\" open><summary>{}</summary>",
1495 write_impl_section_heading(
1496 fmt::from_fn(|f| write!(
1497 f,
1498 "<span>Methods from {trait_}<Target = {type_}></span>",
1499 trait_ = trait_.print(cx),
1500 type_ = type_.print(cx),
1501 )),
1502 &id,
1503 )
1504 )
1505 })),
1506 Cow::Owned(derived_id),
1507 )
1508 }
1509 };
1510 let mut impls_buf = String::new();
1511 for i in &non_trait {
1512 write_str(
1513 &mut impls_buf,
1514 format_args!(
1515 "{}",
1516 render_impl(
1517 cx,
1518 i,
1519 containing_item,
1520 AssocItemLink::Anchor(None),
1521 render_mode,
1522 None,
1523 &[],
1524 ImplRenderingParameters {
1525 show_def_docs: true,
1526 show_default_items: true,
1527 show_non_assoc_items: true,
1528 toggle_open_by_default: true,
1529 },
1530 )
1531 ),
1532 );
1533 }
1534 if !impls_buf.is_empty() {
1535 write!(
1536 w,
1537 "{section_heading}<div id=\"{id}\"{class_html}>{impls_buf}</div>{}",
1538 matches!(what, AssocItemRender::DerefFor { .. })
1539 .then_some("</details>")
1540 .maybe_display(),
1541 )
1542 .unwrap();
1543 }
1544 }
1545
1546 if !traits.is_empty() {
1547 let deref_impl =
1548 traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait());
1549 if let Some(impl_) = deref_impl {
1550 let has_deref_mut =
1551 traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
1552 render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs);
1553 }
1554
1555 if let AssocItemRender::DerefFor { .. } = what {
1558 return;
1559 }
1560
1561 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
1562 traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
1563 let (blanket_impl, concrete): (Vec<&Impl>, _) =
1564 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
1565
1566 render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
1567 }
1568}
1569
1570fn render_deref_methods(
1572 mut w: impl Write,
1573 cx: &Context<'_>,
1574 impl_: &Impl,
1575 container_item: &clean::Item,
1576 deref_mut: bool,
1577 derefs: &mut DefIdSet,
1578) {
1579 let cache = cx.cache();
1580 let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
1581 let (target, real_target) = impl_
1582 .inner_impl()
1583 .items
1584 .iter()
1585 .find_map(|item| match item.kind {
1586 clean::AssocTypeItem(box ref t, _) => Some(match *t {
1587 clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
1588 _ => (&t.type_, &t.type_),
1589 }),
1590 _ => None,
1591 })
1592 .expect("Expected associated type binding");
1593 debug!(
1594 "Render deref methods for {for_:#?}, target {target:#?}",
1595 for_ = impl_.inner_impl().for_
1596 );
1597 let what =
1598 AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
1599 if let Some(did) = target.def_id(cache) {
1600 if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) {
1601 if did == type_did || !derefs.insert(did) {
1603 return;
1605 }
1606 }
1607 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
1608 } else if let Some(prim) = target.primitive_type()
1609 && let Some(&did) = cache.primitive_locations.get(&prim)
1610 {
1611 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
1612 }
1613}
1614
1615fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool {
1616 let self_type_opt = match item.kind {
1617 clean::MethodItem(ref method, _) => method.decl.receiver_type(),
1618 clean::RequiredMethodItem(ref method) => method.decl.receiver_type(),
1619 _ => None,
1620 };
1621
1622 if let Some(self_ty) = self_type_opt {
1623 let (by_mut_ref, by_box, by_value) = match *self_ty {
1624 clean::Type::BorrowedRef { mutability, .. } => {
1625 (mutability == Mutability::Mut, false, false)
1626 }
1627 clean::Type::Path { ref path } => {
1628 (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false)
1629 }
1630 clean::Type::SelfTy => (false, false, true),
1631 _ => (false, false, false),
1632 };
1633
1634 (deref_mut_ || !by_mut_ref) && !by_box && !by_value
1635 } else {
1636 false
1637 }
1638}
1639
1640fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option<impl fmt::Display> {
1641 if ty.is_unit() {
1642 return None;
1644 }
1645
1646 let did = ty.def_id(cx.cache())?;
1647
1648 if Some(did) == cx.tcx().lang_items().owned_box()
1653 || Some(did) == cx.tcx().lang_items().pin_type()
1654 {
1655 return None;
1656 }
1657
1658 let impls = cx.cache().impls.get(&did)?;
1659 let has_notable_trait = impls
1660 .iter()
1661 .map(Impl::inner_impl)
1662 .filter(|impl_| {
1663 impl_.polarity == ty::ImplPolarity::Positive
1664 && ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1667 })
1668 .filter_map(|impl_| impl_.trait_.as_ref())
1669 .filter_map(|trait_| cx.cache().traits.get(&trait_.def_id()))
1670 .any(|t| t.is_notable_trait(cx.tcx()));
1671
1672 has_notable_trait.then(|| {
1673 cx.types_with_notable_traits.borrow_mut().insert(ty.clone());
1674 fmt::from_fn(|f| {
1675 write!(
1676 f,
1677 " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>",
1678 ty = Escape(&format!("{:#}", ty.print(cx))),
1679 )
1680 })
1681 })
1682}
1683
1684fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
1685 let mut out = String::new();
1686
1687 let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
1688
1689 let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");
1690
1691 for i in impls {
1692 let impl_ = i.inner_impl();
1693 if impl_.polarity != ty::ImplPolarity::Positive {
1694 continue;
1695 }
1696
1697 if !ty.is_doc_subtype_of(&impl_.for_, cx.cache()) {
1698 continue;
1701 }
1702 if let Some(trait_) = &impl_.trait_ {
1703 let trait_did = trait_.def_id();
1704
1705 if cx.cache().traits.get(&trait_did).is_some_and(|t| t.is_notable_trait(cx.tcx())) {
1706 if out.is_empty() {
1707 write_str(
1708 &mut out,
1709 format_args!(
1710 "<h3>Notable traits for <code>{}</code></h3>\
1711 <pre><code>",
1712 impl_.for_.print(cx)
1713 ),
1714 );
1715 }
1716
1717 write_str(
1718 &mut out,
1719 format_args!("<div class=\"where\">{}</div>", impl_.print(false, cx)),
1720 );
1721 for it in &impl_.items {
1722 if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
1723 let empty_set = FxIndexSet::default();
1724 let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
1725 write_str(
1726 &mut out,
1727 format_args!(
1728 "<div class=\"where\"> {};</div>",
1729 assoc_type(
1730 it,
1731 &tydef.generics,
1732 &[], Some(&tydef.type_),
1734 src_link,
1735 0,
1736 cx,
1737 )
1738 ),
1739 );
1740 }
1741 }
1742 }
1743 }
1744 }
1745 if out.is_empty() {
1746 out.push_str("</code></pre>");
1747 }
1748
1749 (format!("{:#}", ty.print(cx)), out)
1750}
1751
1752fn notable_traits_json<'a>(tys: impl Iterator<Item = &'a clean::Type>, cx: &Context<'_>) -> String {
1753 let mut mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect();
1754 mp.sort_by(|(name1, _html1), (name2, _html2)| name1.cmp(name2));
1755 struct NotableTraitsMap(Vec<(String, String)>);
1756 impl Serialize for NotableTraitsMap {
1757 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1758 where
1759 S: Serializer,
1760 {
1761 let mut map = serializer.serialize_map(Some(self.0.len()))?;
1762 for item in &self.0 {
1763 map.serialize_entry(&item.0, &item.1)?;
1764 }
1765 map.end()
1766 }
1767 }
1768 serde_json::to_string(&NotableTraitsMap(mp))
1769 .expect("serialize (string, string) -> json object cannot fail")
1770}
1771
1772#[derive(Clone, Copy, Debug)]
1773struct ImplRenderingParameters {
1774 show_def_docs: bool,
1775 show_default_items: bool,
1776 show_non_assoc_items: bool,
1778 toggle_open_by_default: bool,
1779}
1780
1781fn render_impl(
1782 cx: &Context<'_>,
1783 i: &Impl,
1784 parent: &clean::Item,
1785 link: AssocItemLink<'_>,
1786 render_mode: RenderMode,
1787 use_absolute: Option<bool>,
1788 aliases: &[String],
1789 rendering_params: ImplRenderingParameters,
1790) -> impl fmt::Display {
1791 fmt::from_fn(move |w| {
1792 let cache = &cx.shared.cache;
1793 let traits = &cache.traits;
1794 let trait_ = i.trait_did().map(|did| &traits[&did]);
1795 let mut close_tags = <Vec<&str>>::with_capacity(2);
1796
1797 fn doc_impl_item(
1803 boring: impl fmt::Write,
1804 interesting: impl fmt::Write,
1805 cx: &Context<'_>,
1806 item: &clean::Item,
1807 parent: &clean::Item,
1808 link: AssocItemLink<'_>,
1809 render_mode: RenderMode,
1810 is_default_item: bool,
1811 trait_: Option<&clean::Trait>,
1812 rendering_params: ImplRenderingParameters,
1813 ) -> fmt::Result {
1814 let item_type = item.type_();
1815 let name = item.name.as_ref().unwrap();
1816
1817 let render_method_item = rendering_params.show_non_assoc_items
1818 && match render_mode {
1819 RenderMode::Normal => true,
1820 RenderMode::ForDeref { mut_: deref_mut_ } => {
1821 should_render_item(item, deref_mut_, cx.tcx())
1822 }
1823 };
1824
1825 let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
1826
1827 let mut doc_buffer = String::new();
1828 let mut info_buffer = String::new();
1829 let mut short_documented = true;
1830
1831 if render_method_item {
1832 if !is_default_item {
1833 if let Some(t) = trait_ {
1834 if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1837 if !item.doc_value().is_empty() {
1840 document_item_info(cx, it, Some(parent))
1841 .render_into(&mut info_buffer)
1842 .unwrap();
1843 write_str(
1844 &mut doc_buffer,
1845 format_args!("{}", document_full(item, cx, HeadingOffset::H5)),
1846 );
1847 short_documented = false;
1848 } else {
1849 write_str(
1852 &mut doc_buffer,
1853 format_args!(
1854 "{}",
1855 document_short(
1856 it,
1857 cx,
1858 link,
1859 parent,
1860 rendering_params.show_def_docs,
1861 )
1862 ),
1863 );
1864 }
1865 }
1866 } else {
1867 document_item_info(cx, item, Some(parent))
1868 .render_into(&mut info_buffer)
1869 .unwrap();
1870 if rendering_params.show_def_docs {
1871 write_str(
1872 &mut doc_buffer,
1873 format_args!("{}", document_full(item, cx, HeadingOffset::H5)),
1874 );
1875 short_documented = false;
1876 }
1877 }
1878 } else {
1879 write_str(
1880 &mut doc_buffer,
1881 format_args!(
1882 "{}",
1883 document_short(item, cx, link, parent, rendering_params.show_def_docs)
1884 ),
1885 );
1886 }
1887 }
1888 let mut w = if short_documented && trait_.is_some() {
1889 Either::Left(interesting)
1890 } else {
1891 Either::Right(boring)
1892 };
1893
1894 let toggled = !doc_buffer.is_empty();
1895 if toggled {
1896 let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
1897 write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>")?;
1898 }
1899 match &item.kind {
1900 clean::MethodItem(..) | clean::RequiredMethodItem(_) => {
1901 if render_method_item {
1903 let id = cx.derive_id(format!("{item_type}.{name}"));
1904 let source_id = trait_
1905 .and_then(|trait_| {
1906 trait_
1907 .items
1908 .iter()
1909 .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
1910 })
1911 .map(|item| format!("{}.{name}", item.type_()));
1912 write!(
1913 w,
1914 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1915 {}",
1916 render_rightside(cx, item, render_mode)
1917 )?;
1918 if trait_.is_some() {
1919 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1921 }
1922 write!(
1923 w,
1924 "<h4 class=\"code-header\">{}</h4></section>",
1925 render_assoc_item(
1926 item,
1927 link.anchor(source_id.as_ref().unwrap_or(&id)),
1928 ItemType::Impl,
1929 cx,
1930 render_mode,
1931 ),
1932 )?;
1933 }
1934 }
1935 clean::RequiredAssocConstItem(generics, ty) => {
1936 let source_id = format!("{item_type}.{name}");
1937 let id = cx.derive_id(&source_id);
1938 write!(
1939 w,
1940 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1941 {}",
1942 render_rightside(cx, item, render_mode)
1943 )?;
1944 if trait_.is_some() {
1945 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1947 }
1948 write!(
1949 w,
1950 "<h4 class=\"code-header\">{}</h4></section>",
1951 assoc_const(
1952 item,
1953 generics,
1954 ty,
1955 AssocConstValue::None,
1956 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1957 0,
1958 cx,
1959 ),
1960 )?;
1961 }
1962 clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
1963 let source_id = format!("{item_type}.{name}");
1964 let id = cx.derive_id(&source_id);
1965 write!(
1966 w,
1967 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1968 {}",
1969 render_rightside(cx, item, render_mode),
1970 )?;
1971 if trait_.is_some() {
1972 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1974 }
1975 write!(
1976 w,
1977 "<h4 class=\"code-header\">{}</h4></section>",
1978 assoc_const(
1979 item,
1980 &ci.generics,
1981 &ci.type_,
1982 match item.kind {
1983 clean::ProvidedAssocConstItem(_) =>
1984 AssocConstValue::TraitDefault(&ci.kind),
1985 clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
1986 _ => unreachable!(),
1987 },
1988 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1989 0,
1990 cx,
1991 ),
1992 )?;
1993 }
1994 clean::RequiredAssocTypeItem(generics, bounds) => {
1995 let source_id = format!("{item_type}.{name}");
1996 let id = cx.derive_id(&source_id);
1997 write!(
1998 w,
1999 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
2000 {}",
2001 render_rightside(cx, item, render_mode),
2002 )?;
2003 if trait_.is_some() {
2004 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
2006 }
2007 write!(
2008 w,
2009 "<h4 class=\"code-header\">{}</h4></section>",
2010 assoc_type(
2011 item,
2012 generics,
2013 bounds,
2014 None,
2015 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2016 0,
2017 cx,
2018 ),
2019 )?;
2020 }
2021 clean::AssocTypeItem(tydef, _bounds) => {
2022 let source_id = format!("{item_type}.{name}");
2023 let id = cx.derive_id(&source_id);
2024 write!(
2025 w,
2026 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
2027 {}",
2028 render_rightside(cx, item, render_mode),
2029 )?;
2030 if trait_.is_some() {
2031 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
2033 }
2034 write!(
2035 w,
2036 "<h4 class=\"code-header\">{}</h4></section>",
2037 assoc_type(
2038 item,
2039 &tydef.generics,
2040 &[], Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
2042 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2043 0,
2044 cx,
2045 ),
2046 )?;
2047 }
2048 clean::StrippedItem(..) => return Ok(()),
2049 _ => panic!("can't make docs for trait item with name {:?}", item.name),
2050 }
2051
2052 w.write_str(&info_buffer)?;
2053 if toggled {
2054 write!(w, "</summary>{doc_buffer}</details>")?;
2055 }
2056 Ok(())
2057 }
2058
2059 let mut impl_items = String::new();
2060 let mut default_impl_items = String::new();
2061 let impl_ = i.inner_impl();
2062
2063 let mut assoc_types = Vec::new();
2073 let mut methods = Vec::new();
2074
2075 if !impl_.is_negative_trait_impl() {
2076 for trait_item in &impl_.items {
2077 match trait_item.kind {
2078 clean::MethodItem(..) | clean::RequiredMethodItem(_) => {
2079 methods.push(trait_item)
2080 }
2081 clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => {
2082 assoc_types.push(trait_item)
2083 }
2084 clean::RequiredAssocConstItem(..)
2085 | clean::ProvidedAssocConstItem(_)
2086 | clean::ImplAssocConstItem(_) => {
2087 doc_impl_item(
2089 &mut default_impl_items,
2090 &mut impl_items,
2091 cx,
2092 trait_item,
2093 if trait_.is_some() { &i.impl_item } else { parent },
2094 link,
2095 render_mode,
2096 false,
2097 trait_,
2098 rendering_params,
2099 )?;
2100 }
2101 _ => {}
2102 }
2103 }
2104
2105 for assoc_type in assoc_types {
2106 doc_impl_item(
2107 &mut default_impl_items,
2108 &mut impl_items,
2109 cx,
2110 assoc_type,
2111 if trait_.is_some() { &i.impl_item } else { parent },
2112 link,
2113 render_mode,
2114 false,
2115 trait_,
2116 rendering_params,
2117 )?;
2118 }
2119 for method in methods {
2120 doc_impl_item(
2121 &mut default_impl_items,
2122 &mut impl_items,
2123 cx,
2124 method,
2125 if trait_.is_some() { &i.impl_item } else { parent },
2126 link,
2127 render_mode,
2128 false,
2129 trait_,
2130 rendering_params,
2131 )?;
2132 }
2133 }
2134
2135 fn render_default_items(
2136 mut boring: impl fmt::Write,
2137 mut interesting: impl fmt::Write,
2138 cx: &Context<'_>,
2139 t: &clean::Trait,
2140 i: &clean::Impl,
2141 parent: &clean::Item,
2142 render_mode: RenderMode,
2143 rendering_params: ImplRenderingParameters,
2144 ) -> fmt::Result {
2145 for trait_item in &t.items {
2146 if let Some(impl_def_id) = parent.item_id.as_def_id()
2149 && let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
2150 && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id))
2151 {
2152 continue;
2153 }
2154
2155 let n = trait_item.name;
2156 if i.items.iter().any(|m| m.name == n) {
2157 continue;
2158 }
2159 let did = i.trait_.as_ref().unwrap().def_id();
2160 let provided_methods = i.provided_trait_methods(cx.tcx());
2161 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
2162
2163 doc_impl_item(
2164 &mut boring,
2165 &mut interesting,
2166 cx,
2167 trait_item,
2168 parent,
2169 assoc_link,
2170 render_mode,
2171 true,
2172 Some(t),
2173 rendering_params,
2174 )?;
2175 }
2176 Ok(())
2177 }
2178
2179 if rendering_params.show_default_items
2184 && let Some(t) = trait_
2185 && !impl_.is_negative_trait_impl()
2186 {
2187 render_default_items(
2188 &mut default_impl_items,
2189 &mut impl_items,
2190 cx,
2191 t,
2192 impl_,
2193 &i.impl_item,
2194 render_mode,
2195 rendering_params,
2196 )?;
2197 }
2198 if render_mode == RenderMode::Normal {
2199 let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
2200 if toggled {
2201 close_tags.push("</details>");
2202 write!(
2203 w,
2204 "<details class=\"toggle implementors-toggle\"{}>\
2205 <summary>",
2206 if rendering_params.toggle_open_by_default { " open" } else { "" }
2207 )?;
2208 }
2209
2210 let (before_dox, after_dox) = i
2211 .impl_item
2212 .opt_doc_value()
2213 .map(|dox| {
2214 Markdown {
2215 content: &dox,
2216 links: &i.impl_item.links(cx),
2217 ids: &mut cx.id_map.borrow_mut(),
2218 error_codes: cx.shared.codes,
2219 edition: cx.shared.edition(),
2220 playground: &cx.shared.playground,
2221 heading_offset: HeadingOffset::H4,
2222 }
2223 .split_summary_and_content()
2224 })
2225 .unwrap_or((None, None));
2226
2227 write!(
2228 w,
2229 "{}",
2230 render_impl_summary(
2231 cx,
2232 i,
2233 parent,
2234 rendering_params.show_def_docs,
2235 use_absolute,
2236 aliases,
2237 before_dox.as_deref(),
2238 trait_.is_none() && impl_.items.is_empty(),
2239 )
2240 )?;
2241 if toggled {
2242 w.write_str("</summary>")?;
2243 }
2244
2245 if before_dox.is_some()
2246 && let Some(after_dox) = after_dox
2247 {
2248 write!(w, "<div class=\"docblock\">{after_dox}</div>")?;
2249 }
2250
2251 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2252 w.write_str("<div class=\"impl-items\">")?;
2253 close_tags.push("</div>");
2254 }
2255 }
2256 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2257 w.write_str(&default_impl_items)?;
2258 w.write_str(&impl_items)?;
2259 }
2260 for tag in close_tags.into_iter().rev() {
2261 w.write_str(tag)?;
2262 }
2263 Ok(())
2264 })
2265}
2266
2267fn render_rightside(
2270 cx: &Context<'_>,
2271 item: &clean::Item,
2272 render_mode: RenderMode,
2273) -> impl fmt::Display {
2274 let tcx = cx.tcx();
2275
2276 fmt::from_fn(move |w| {
2277 let const_stability = match render_mode {
2280 RenderMode::Normal => item.const_stability(tcx),
2281 RenderMode::ForDeref { .. } => None,
2282 };
2283 let src_href = cx.src_href(item);
2284 let stability = render_stability_since_raw_with_extra(
2285 item.stable_since(tcx),
2286 const_stability,
2287 if src_href.is_some() { "" } else { " rightside" },
2288 );
2289
2290 match (stability, src_href) {
2291 (Some(stability), Some(link)) => {
2292 write!(
2293 w,
2294 "<span class=\"rightside\">{stability} · <a class=\"src\" href=\"{link}\">Source</a></span>",
2295 )
2296 }
2297 (Some(stability), None) => {
2298 write!(w, "{stability}")
2299 }
2300 (None, Some(link)) => {
2301 write!(w, "<a class=\"src rightside\" href=\"{link}\">Source</a>")
2302 }
2303 (None, None) => Ok(()),
2304 }
2305 })
2306}
2307
2308fn render_impl_summary(
2309 cx: &Context<'_>,
2310 i: &Impl,
2311 parent: &clean::Item,
2312 show_def_docs: bool,
2313 use_absolute: Option<bool>,
2314 aliases: &[String],
2317 doc: Option<&str>,
2318 impl_is_empty: bool,
2319) -> impl fmt::Display {
2320 fmt::from_fn(move |w| {
2321 let inner_impl = i.inner_impl();
2322 let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
2323 let aliases = (!aliases.is_empty())
2324 .then_some(fmt::from_fn(|f| {
2325 write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f)))
2326 }))
2327 .maybe_display();
2328 write!(
2329 w,
2330 "<section id=\"{id}\" class=\"impl\"{aliases}>\
2331 {}\
2332 <a href=\"#{id}\" class=\"anchor\">§</a>\
2333 <h3 class=\"code-header\">",
2334 render_rightside(cx, &i.impl_item, RenderMode::Normal)
2335 )?;
2336
2337 if let Some(use_absolute) = use_absolute {
2338 write!(w, "{}", inner_impl.print(use_absolute, cx))?;
2339 if show_def_docs {
2340 for it in &inner_impl.items {
2341 if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
2342 write!(
2343 w,
2344 "<div class=\"where\"> {};</div>",
2345 assoc_type(
2346 it,
2347 &tydef.generics,
2348 &[], Some(&tydef.type_),
2350 AssocItemLink::Anchor(None),
2351 0,
2352 cx,
2353 )
2354 )?;
2355 }
2356 }
2357 }
2358 } else {
2359 write!(w, "{}", inner_impl.print(false, cx))?;
2360 }
2361 w.write_str("</h3>")?;
2362
2363 let is_trait = inner_impl.trait_.is_some();
2364 if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) {
2365 write!(
2366 w,
2367 "<span class=\"item-info\">\
2368 <div class=\"stab portability\">{portability}</div>\
2369 </span>",
2370 )?;
2371 }
2372
2373 if let Some(doc) = doc {
2374 if impl_is_empty {
2375 w.write_str(
2376 "<div class=\"item-info\">\
2377 <div class=\"stab empty-impl\">This impl block contains no items.</div>\
2378 </div>",
2379 )?;
2380 }
2381 write!(w, "<div class=\"docblock\">{doc}</div>")?;
2382 }
2383
2384 w.write_str("</section>")
2385 })
2386}
2387
2388pub(crate) fn small_url_encode(s: String) -> String {
2389 fn dont_escape(c: u8) -> bool {
2394 c.is_ascii_alphanumeric()
2395 || c == b'-'
2396 || c == b'_'
2397 || c == b'.'
2398 || c == b','
2399 || c == b'~'
2400 || c == b'!'
2401 || c == b'\''
2402 || c == b'('
2403 || c == b')'
2404 || c == b'*'
2405 || c == b'/'
2406 || c == b';'
2407 || c == b':'
2408 || c == b'?'
2409 || c == b'='
2413 }
2414 let mut st = String::new();
2415 let mut last_match = 0;
2416 for (idx, b) in s.bytes().enumerate() {
2417 if dont_escape(b) {
2418 continue;
2419 }
2420
2421 if last_match != idx {
2422 st += &s[last_match..idx];
2424 }
2425 if b == b' ' {
2426 st += "+";
2430 } else {
2431 write!(st, "%{b:02X}").unwrap();
2432 }
2433 last_match = idx + 1;
2439 }
2440
2441 if last_match != 0 {
2442 st += &s[last_match..];
2443 st
2444 } else {
2445 s
2446 }
2447}
2448
2449fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String {
2450 use rustc_middle::ty::print::with_forced_trimmed_paths;
2451 let (type_, trait_) = match impl_id {
2452 ItemId::Auto { trait_, for_ } => {
2453 let ty = tcx.type_of(for_).skip_binder();
2454 (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
2455 }
2456 ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
2457 match tcx.impl_subject(impl_id).skip_binder() {
2458 ty::ImplSubject::Trait(trait_ref) => {
2459 (trait_ref.args[0].expect_ty(), Some(trait_ref))
2460 }
2461 ty::ImplSubject::Inherent(ty) => (ty, None),
2462 }
2463 }
2464 };
2465 with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
2466 format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
2467 } else {
2468 format!("impl-{type_}")
2469 }))
2470}
2471
2472fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2473 match item.kind {
2474 clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
2475 Some((format!("{:#}", i.for_.print(cx)), get_id_for_impl(cx.tcx(), item.item_id)))
2478 }
2479 _ => None,
2480 }
2481}
2482
2483pub(crate) fn get_filtered_impls_for_reference<'a>(
2487 shared: &'a SharedContext<'_>,
2488 it: &clean::Item,
2489) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
2490 let def_id = it.item_id.expect_def_id();
2491 let Some(v) = shared.cache.impls.get(&def_id) else {
2493 return (Vec::new(), Vec::new(), Vec::new());
2494 };
2495 let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
2498 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
2499 traits.partition(|t| t.inner_impl().kind.is_auto());
2500
2501 let (blanket_impl, concrete): (Vec<&Impl>, _) =
2502 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
2503 let concrete: Vec<_> = concrete
2505 .into_iter()
2506 .filter(|t| match t.inner_impl().for_ {
2507 clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
2508 _ => false,
2509 })
2510 .collect();
2511
2512 (concrete, synthetic, blanket_impl)
2513}
2514
2515#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2516pub(crate) enum ItemSection {
2517 Reexports,
2518 PrimitiveTypes,
2519 Modules,
2520 Macros,
2521 Structs,
2522 Enums,
2523 Constants,
2524 Statics,
2525 Traits,
2526 Functions,
2527 TypeAliases,
2528 Unions,
2529 Implementations,
2530 TypeMethods,
2531 Methods,
2532 StructFields,
2533 Variants,
2534 AssociatedTypes,
2535 AssociatedConstants,
2536 ForeignTypes,
2537 Keywords,
2538 AttributeMacros,
2539 DeriveMacros,
2540 TraitAliases,
2541}
2542
2543impl ItemSection {
2544 const ALL: &'static [Self] = {
2545 use ItemSection::*;
2546 &[
2549 Reexports,
2550 PrimitiveTypes,
2551 Modules,
2552 Macros,
2553 Structs,
2554 Enums,
2555 Constants,
2556 Statics,
2557 Traits,
2558 Functions,
2559 TypeAliases,
2560 Unions,
2561 Implementations,
2562 TypeMethods,
2563 Methods,
2564 StructFields,
2565 Variants,
2566 AssociatedTypes,
2567 AssociatedConstants,
2568 ForeignTypes,
2569 Keywords,
2570 AttributeMacros,
2571 DeriveMacros,
2572 TraitAliases,
2573 ]
2574 };
2575
2576 fn id(self) -> &'static str {
2577 match self {
2578 Self::Reexports => "reexports",
2579 Self::Modules => "modules",
2580 Self::Structs => "structs",
2581 Self::Unions => "unions",
2582 Self::Enums => "enums",
2583 Self::Functions => "functions",
2584 Self::TypeAliases => "types",
2585 Self::Statics => "statics",
2586 Self::Constants => "constants",
2587 Self::Traits => "traits",
2588 Self::Implementations => "impls",
2589 Self::TypeMethods => "tymethods",
2590 Self::Methods => "methods",
2591 Self::StructFields => "fields",
2592 Self::Variants => "variants",
2593 Self::Macros => "macros",
2594 Self::PrimitiveTypes => "primitives",
2595 Self::AssociatedTypes => "associated-types",
2596 Self::AssociatedConstants => "associated-consts",
2597 Self::ForeignTypes => "foreign-types",
2598 Self::Keywords => "keywords",
2599 Self::AttributeMacros => "attributes",
2600 Self::DeriveMacros => "derives",
2601 Self::TraitAliases => "trait-aliases",
2602 }
2603 }
2604
2605 fn name(self) -> &'static str {
2606 match self {
2607 Self::Reexports => "Re-exports",
2608 Self::Modules => "Modules",
2609 Self::Structs => "Structs",
2610 Self::Unions => "Unions",
2611 Self::Enums => "Enums",
2612 Self::Functions => "Functions",
2613 Self::TypeAliases => "Type Aliases",
2614 Self::Statics => "Statics",
2615 Self::Constants => "Constants",
2616 Self::Traits => "Traits",
2617 Self::Implementations => "Implementations",
2618 Self::TypeMethods => "Type Methods",
2619 Self::Methods => "Methods",
2620 Self::StructFields => "Struct Fields",
2621 Self::Variants => "Variants",
2622 Self::Macros => "Macros",
2623 Self::PrimitiveTypes => "Primitive Types",
2624 Self::AssociatedTypes => "Associated Types",
2625 Self::AssociatedConstants => "Associated Constants",
2626 Self::ForeignTypes => "Foreign Types",
2627 Self::Keywords => "Keywords",
2628 Self::AttributeMacros => "Attribute Macros",
2629 Self::DeriveMacros => "Derive Macros",
2630 Self::TraitAliases => "Trait Aliases",
2631 }
2632 }
2633}
2634
2635fn item_ty_to_section(ty: ItemType) -> ItemSection {
2636 match ty {
2637 ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
2638 ItemType::Module => ItemSection::Modules,
2639 ItemType::Struct => ItemSection::Structs,
2640 ItemType::Union => ItemSection::Unions,
2641 ItemType::Enum => ItemSection::Enums,
2642 ItemType::Function => ItemSection::Functions,
2643 ItemType::TypeAlias => ItemSection::TypeAliases,
2644 ItemType::Static => ItemSection::Statics,
2645 ItemType::Constant => ItemSection::Constants,
2646 ItemType::Trait => ItemSection::Traits,
2647 ItemType::Impl => ItemSection::Implementations,
2648 ItemType::TyMethod => ItemSection::TypeMethods,
2649 ItemType::Method => ItemSection::Methods,
2650 ItemType::StructField => ItemSection::StructFields,
2651 ItemType::Variant => ItemSection::Variants,
2652 ItemType::Macro => ItemSection::Macros,
2653 ItemType::Primitive => ItemSection::PrimitiveTypes,
2654 ItemType::AssocType => ItemSection::AssociatedTypes,
2655 ItemType::AssocConst => ItemSection::AssociatedConstants,
2656 ItemType::ForeignType => ItemSection::ForeignTypes,
2657 ItemType::Keyword => ItemSection::Keywords,
2658 ItemType::ProcAttribute => ItemSection::AttributeMacros,
2659 ItemType::ProcDerive => ItemSection::DeriveMacros,
2660 ItemType::TraitAlias => ItemSection::TraitAliases,
2661 }
2662}
2663
2664fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec<String> {
2671 let mut out = Vec::new();
2672 let mut visited = FxHashSet::default();
2673 let mut work = VecDeque::new();
2674
2675 let mut process_path = |did: DefId| {
2676 let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
2677 let fqp = cache.exact_paths.get(&did).or_else(get_extern);
2678
2679 if let Some(path) = fqp {
2680 out.push(join_path_syms(path));
2681 }
2682 };
2683
2684 work.push_back(first_ty);
2685
2686 while let Some(ty) = work.pop_front() {
2687 if !visited.insert(ty) {
2688 continue;
2689 }
2690
2691 match ty {
2692 clean::Type::Path { path } => process_path(path.def_id()),
2693 clean::Type::Tuple(tys) => {
2694 work.extend(tys.iter());
2695 }
2696 clean::Type::Slice(ty) => {
2697 work.push_back(ty);
2698 }
2699 clean::Type::Array(ty, _) => {
2700 work.push_back(ty);
2701 }
2702 clean::Type::RawPointer(_, ty) => {
2703 work.push_back(ty);
2704 }
2705 clean::Type::BorrowedRef { type_, .. } => {
2706 work.push_back(type_);
2707 }
2708 clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
2709 work.push_back(self_type);
2710 if let Some(trait_) = trait_ {
2711 process_path(trait_.def_id());
2712 }
2713 }
2714 _ => {}
2715 }
2716 }
2717 out
2718}
2719
2720const MAX_FULL_EXAMPLES: usize = 5;
2721const NUM_VISIBLE_LINES: usize = 10;
2722
2723fn render_call_locations<W: fmt::Write>(
2725 mut w: W,
2726 cx: &Context<'_>,
2727 item: &clean::Item,
2728) -> fmt::Result {
2729 let tcx = cx.tcx();
2730 let def_id = item.item_id.expect_def_id();
2731 let key = tcx.def_path_hash(def_id);
2732 let Some(call_locations) = cx.shared.call_locations.get(&key) else { return Ok(()) };
2733
2734 let id = cx.derive_id("scraped-examples");
2736 write!(
2737 &mut w,
2738 "<div class=\"docblock scraped-example-list\">\
2739 <span></span>\
2740 <h5 id=\"{id}\">\
2741 <a href=\"#{id}\">Examples found in repository</a>\
2742 <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
2743 </h5>",
2744 root_path = cx.root_path(),
2745 id = id
2746 )?;
2747
2748 let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
2750 let (line_lo, line_hi) = loc.call_expr.line_span;
2751 let (anchor, title) = if line_lo == line_hi {
2752 ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
2753 } else {
2754 (
2755 format!("{}-{}", line_lo + 1, line_hi + 1),
2756 format!("lines {}-{}", line_lo + 1, line_hi + 1),
2757 )
2758 };
2759 let url = format!("{}{}#{anchor}", cx.root_path(), call_data.url);
2760 (url, title)
2761 };
2762
2763 let write_example = |w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
2765 let contents = match fs::read_to_string(path) {
2766 Ok(contents) => contents,
2767 Err(err) => {
2768 let span = item.span(tcx).map_or(DUMMY_SP, |span| span.inner());
2769 tcx.dcx().span_err(span, format!("failed to read file {}: {err}", path.display()));
2770 return false;
2771 }
2772 };
2773
2774 assert!(!call_data.locations.is_empty());
2777 let min_loc =
2778 call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2779 let byte_min = min_loc.enclosing_item.byte_span.0;
2780 let line_min = min_loc.enclosing_item.line_span.0;
2781 let max_loc =
2782 call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
2783 let byte_max = max_loc.enclosing_item.byte_span.1;
2784 let line_max = max_loc.enclosing_item.line_span.1;
2785
2786 let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
2788
2789 let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
2792 .locations
2793 .iter()
2794 .map(|loc| {
2795 let (byte_lo, byte_hi) = loc.call_ident.byte_span;
2796 let (line_lo, line_hi) = loc.call_expr.line_span;
2797 let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
2798
2799 let line_range = (line_lo - line_min, line_hi - line_min);
2800 let (line_url, line_title) = link_to_loc(call_data, loc);
2801
2802 (byte_range, (line_range, line_url, line_title))
2803 })
2804 .unzip();
2805
2806 let (_, init_url, init_title) = &line_ranges[0];
2807 let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
2808 let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
2809
2810 let file_span = (|| {
2812 let source_map = tcx.sess.source_map();
2813 let crate_src = tcx.sess.local_crate_source_file()?.into_local_path()?;
2814 let abs_crate_src = crate_src.canonicalize().ok()?;
2815 let crate_root = abs_crate_src.parent()?.parent()?;
2816 let rel_path = path.strip_prefix(crate_root).ok()?;
2817 let files = source_map.files();
2818 let file = files.iter().find(|file| match &file.name {
2819 FileName::Real(RealFileName::LocalPath(other_path)) => rel_path == other_path,
2820 _ => false,
2821 })?;
2822 Some(rustc_span::Span::with_root_ctxt(
2823 file.start_pos + BytePos(byte_min),
2824 file.start_pos + BytePos(byte_max),
2825 ))
2826 })()
2827 .unwrap_or(DUMMY_SP);
2828
2829 let mut decoration_info = FxIndexMap::default();
2830 decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
2831 decoration_info.insert("highlight", byte_ranges);
2832
2833 sources::print_src(
2834 w,
2835 contents_subset,
2836 file_span,
2837 cx,
2838 &cx.root_path(),
2839 &highlight::DecorationInfo(decoration_info),
2840 &sources::SourceContext::Embedded(sources::ScrapedInfo {
2841 needs_expansion,
2842 offset: line_min,
2843 name: &call_data.display_name,
2844 url: init_url,
2845 title: init_title,
2846 locations: locations_encoded,
2847 }),
2848 )
2849 .unwrap();
2850
2851 true
2852 };
2853
2854 let ordered_locations = {
2866 fn sort_criterion<'a>(
2867 (_, call_data): &(&PathBuf, &'a CallData),
2868 ) -> (bool, u32, &'a String) {
2869 let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2871 (!call_data.is_bin, hi - lo, &call_data.display_name)
2872 }
2873
2874 let mut locs = call_locations.iter().collect::<Vec<_>>();
2875 locs.sort_by_key(sort_criterion);
2876 locs
2877 };
2878
2879 let mut it = ordered_locations.into_iter().peekable();
2880
2881 let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
2884 for example in it.by_ref() {
2885 if write_example(&mut *w, example) {
2886 break;
2887 }
2888 }
2889 };
2890
2891 write_and_skip_failure(&mut w, &mut it);
2893
2894 if it.peek().is_some() {
2896 write!(
2897 w,
2898 "<details class=\"toggle more-examples-toggle\">\
2899 <summary class=\"hideme\">\
2900 <span>More examples</span>\
2901 </summary>\
2902 <div class=\"hide-more\">Hide additional examples</div>\
2903 <div class=\"more-scraped-examples\">\
2904 <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
2905 )?;
2906
2907 for _ in 0..MAX_FULL_EXAMPLES {
2910 write_and_skip_failure(&mut w, &mut it);
2911 }
2912
2913 if it.peek().is_some() {
2915 w.write_str(
2916 r#"<div class="example-links">Additional examples can be found in:<br><ul>"#,
2917 )?;
2918 it.try_for_each(|(_, call_data)| {
2919 let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
2920 write!(
2921 w,
2922 r#"<li><a href="{url}">{name}</a></li>"#,
2923 url = url,
2924 name = call_data.display_name
2925 )
2926 })?;
2927 w.write_str("</ul></div>")?;
2928 }
2929
2930 w.write_str("</div></details>")?;
2931 }
2932
2933 w.write_str("</div>")
2934}