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