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 render_attributes_in_code(w, it, &" ".repeat(indent), cx);
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 render_attributes_in_code(w, 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
1313struct CodeAttribute(String);
1314
1315fn render_code_attribute(prefix: &str, code_attr: CodeAttribute, w: &mut impl fmt::Write) {
1316 write!(
1317 w,
1318 "<div class=\"code-attribute\">{prefix}{attr}</div>",
1319 prefix = prefix,
1320 attr = code_attr.0
1321 )
1322 .unwrap();
1323}
1324
1325fn render_attributes_in_code(
1328 w: &mut impl fmt::Write,
1329 it: &clean::Item,
1330 prefix: &str,
1331 cx: &Context<'_>,
1332) {
1333 for attr in it.attributes(cx.tcx(), cx.cache()) {
1334 render_code_attribute(prefix, 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}