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