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 = traits.iter().find(|t| {
1521 t.trait_did() == cx.tcx().lang_items().deref_trait() && !t.is_negative_trait_impl()
1522 });
1523 if let Some(impl_) = deref_impl {
1524 let has_deref_mut =
1525 traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
1526 render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs)?;
1527 }
1528
1529 if let AssocItemRender::DerefFor { .. } = what {
1532 return Ok(());
1533 }
1534
1535 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
1536 traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
1537 let (blanket_impl, concrete): (Vec<&Impl>, _) =
1538 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
1539
1540 render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl)?;
1541 }
1542 Ok(())
1543}
1544
1545fn render_deref_methods(
1547 mut w: impl Write,
1548 cx: &Context<'_>,
1549 impl_: &Impl,
1550 container_item: &clean::Item,
1551 deref_mut: bool,
1552 derefs: &mut DefIdSet,
1553) -> fmt::Result {
1554 let cache = cx.cache();
1555 let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
1556 let (target, real_target) = impl_
1557 .inner_impl()
1558 .items
1559 .iter()
1560 .find_map(|item| match item.kind {
1561 clean::AssocTypeItem(box ref t, _) => Some(match *t {
1562 clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
1563 _ => (&t.type_, &t.type_),
1564 }),
1565 _ => None,
1566 })
1567 .expect("Expected associated type binding");
1568 debug!(
1569 "Render deref methods for {for_:#?}, target {target:#?}",
1570 for_ = impl_.inner_impl().for_
1571 );
1572 let what =
1573 AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
1574 if let Some(did) = target.def_id(cache) {
1575 if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) {
1576 if did == type_did || !derefs.insert(did) {
1578 return Ok(());
1580 }
1581 }
1582 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1583 } else if let Some(prim) = target.primitive_type()
1584 && let Some(&did) = cache.primitive_locations.get(&prim)
1585 {
1586 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1587 }
1588 Ok(())
1589}
1590
1591fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool {
1592 let self_type_opt = match item.kind {
1593 clean::MethodItem(ref method, _) => method.decl.receiver_type(),
1594 clean::RequiredMethodItem(ref method, _) => method.decl.receiver_type(),
1595 _ => None,
1596 };
1597
1598 if let Some(self_ty) = self_type_opt {
1599 let (by_mut_ref, by_box, by_value) = match *self_ty {
1600 clean::Type::BorrowedRef { mutability, .. } => {
1601 (mutability == Mutability::Mut, false, false)
1602 }
1603 clean::Type::Path { ref path } => {
1604 (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false)
1605 }
1606 clean::Type::SelfTy => (false, false, true),
1607 _ => (false, false, false),
1608 };
1609
1610 (deref_mut_ || !by_mut_ref) && !by_box && !by_value
1611 } else {
1612 false
1613 }
1614}
1615
1616fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option<impl fmt::Display> {
1617 if ty.is_unit() {
1618 return None;
1620 }
1621
1622 let did = ty.def_id(cx.cache())?;
1623
1624 if Some(did) == cx.tcx().lang_items().owned_box()
1629 || Some(did) == cx.tcx().lang_items().pin_type()
1630 {
1631 return None;
1632 }
1633
1634 let impls = cx.cache().impls.get(&did)?;
1635 let has_notable_trait = impls
1636 .iter()
1637 .map(Impl::inner_impl)
1638 .filter(|impl_| {
1639 impl_.polarity == ty::ImplPolarity::Positive
1640 && ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1643 })
1644 .filter_map(|impl_| impl_.trait_.as_ref())
1645 .filter_map(|trait_| cx.cache().traits.get(&trait_.def_id()))
1646 .any(|t| t.is_notable_trait(cx.tcx()));
1647
1648 has_notable_trait.then(|| {
1649 cx.types_with_notable_traits.borrow_mut().insert(ty.clone());
1650 fmt::from_fn(|f| {
1651 write!(
1652 f,
1653 " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>",
1654 ty = Escape(&format!("{:#}", print_type(ty, cx))),
1655 )
1656 })
1657 })
1658}
1659
1660fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
1661 let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
1662
1663 let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");
1664
1665 let out = fmt::from_fn(|f| {
1666 let mut notable_impls = impls
1667 .iter()
1668 .map(|impl_| impl_.inner_impl())
1669 .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive)
1670 .filter(|impl_| {
1671 ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1673 })
1674 .filter_map(|impl_| {
1675 if let Some(trait_) = &impl_.trait_
1676 && let trait_did = trait_.def_id()
1677 && let Some(trait_) = cx.cache().traits.get(&trait_did)
1678 && trait_.is_notable_trait(cx.tcx())
1679 {
1680 Some((impl_, trait_did))
1681 } else {
1682 None
1683 }
1684 })
1685 .peekable();
1686
1687 let has_notable_impl = if let Some((impl_, _)) = notable_impls.peek() {
1688 write!(
1689 f,
1690 "<h3>Notable traits for <code>{}</code></h3>\
1691 <pre><code>",
1692 print_type(&impl_.for_, cx),
1693 )?;
1694 true
1695 } else {
1696 false
1697 };
1698
1699 for (impl_, trait_did) in notable_impls {
1700 write!(f, "<div class=\"where\">{}</div>", print_impl(impl_, false, cx))?;
1701 for it in &impl_.items {
1702 let clean::AssocTypeItem(tydef, ..) = &it.kind else {
1703 continue;
1704 };
1705
1706 let empty_set = FxIndexSet::default();
1707 let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
1708
1709 write!(
1710 f,
1711 "<div class=\"where\"> {};</div>",
1712 assoc_type(
1713 it,
1714 &tydef.generics,
1715 &[], Some(&tydef.type_),
1717 src_link,
1718 0,
1719 cx,
1720 )
1721 )?;
1722 }
1723 }
1724
1725 if !has_notable_impl {
1726 f.write_str("</code></pre>")?;
1727 }
1728
1729 Ok(())
1730 })
1731 .to_string();
1732
1733 (format!("{:#}", print_type(ty, cx)), out)
1734}
1735
1736fn notable_traits_json<'a>(tys: impl Iterator<Item = &'a clean::Type>, cx: &Context<'_>) -> String {
1737 let mut mp = tys.map(|ty| notable_traits_decl(ty, cx)).collect::<IndexMap<_, _>>();
1738 mp.sort_unstable_keys();
1739 serde_json::to_string(&mp).expect("serialize (string, string) -> json object cannot fail")
1740}
1741
1742#[derive(Clone, Copy, Debug)]
1743struct ImplRenderingParameters {
1744 show_def_docs: bool,
1745 show_default_items: bool,
1746 show_non_assoc_items: bool,
1748 toggle_open_by_default: bool,
1749}
1750
1751fn render_impl(
1752 cx: &Context<'_>,
1753 i: &Impl,
1754 parent: &clean::Item,
1755 link: AssocItemLink<'_>,
1756 render_mode: RenderMode,
1757 use_absolute: Option<bool>,
1758 aliases: &[String],
1759 rendering_params: ImplRenderingParameters,
1760) -> impl fmt::Display {
1761 fmt::from_fn(move |w| {
1762 let cache = &cx.shared.cache;
1763 let traits = &cache.traits;
1764 let trait_ = i.trait_did().map(|did| &traits[&did]);
1765 let mut close_tags = <Vec<&str>>::with_capacity(2);
1766
1767 fn doc_impl_item(
1773 boring: impl fmt::Write,
1774 interesting: impl fmt::Write,
1775 cx: &Context<'_>,
1776 item: &clean::Item,
1777 parent: &clean::Item,
1778 link: AssocItemLink<'_>,
1779 render_mode: RenderMode,
1780 is_default_item: bool,
1781 trait_: Option<&clean::Trait>,
1782 rendering_params: ImplRenderingParameters,
1783 ) -> fmt::Result {
1784 let item_type = item.type_();
1785 let name = item.name.as_ref().unwrap();
1786
1787 let render_method_item = rendering_params.show_non_assoc_items
1788 && match render_mode {
1789 RenderMode::Normal => true,
1790 RenderMode::ForDeref { mut_: deref_mut_ } => {
1791 should_render_item(item, deref_mut_, cx.tcx())
1792 }
1793 };
1794
1795 let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
1796
1797 let mut doc_buffer = String::new();
1798 let mut info_buffer = String::new();
1799 let mut short_documented = true;
1800
1801 let mut trait_item_deprecated = false;
1802 if render_method_item {
1803 if !is_default_item {
1804 if let Some(t) = trait_ {
1805 if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1808 trait_item_deprecated = it.is_deprecated(cx.tcx());
1809 if !item.doc_value().is_empty() {
1812 document_item_info(cx, it, Some(parent))
1813 .render_into(&mut info_buffer)?;
1814 doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1815 short_documented = false;
1816 } else {
1817 doc_buffer = document_short(
1820 it,
1821 cx,
1822 link,
1823 parent,
1824 rendering_params.show_def_docs,
1825 )
1826 .to_string();
1827 }
1828 }
1829 } else {
1830 document_item_info(cx, item, Some(parent)).render_into(&mut info_buffer)?;
1831 if rendering_params.show_def_docs {
1832 doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1833 short_documented = false;
1834 }
1835 }
1836 } else {
1837 doc_buffer =
1838 document_short(item, cx, link, parent, rendering_params.show_def_docs)
1839 .to_string();
1840 }
1841 }
1842 let mut w = if short_documented && trait_.is_some() {
1843 Either::Left(interesting)
1844 } else {
1845 Either::Right(boring)
1846 };
1847
1848 let mut deprecation_class = if trait_item_deprecated || item.is_deprecated(cx.tcx()) {
1849 " deprecated"
1850 } else {
1851 ""
1852 };
1853
1854 let toggled = !doc_buffer.is_empty();
1855 if toggled {
1856 let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
1857 write!(
1858 w,
1859 "<details class=\"toggle{method_toggle_class}{deprecation_class}\" open><summary>"
1860 )?;
1861 deprecation_class = "";
1862 }
1863 match &item.kind {
1864 clean::MethodItem(..) | clean::RequiredMethodItem(..) => {
1865 if render_method_item {
1867 let id = cx.derive_id(format!("{item_type}.{name}"));
1868 let source_id = trait_
1869 .and_then(|trait_| {
1870 trait_
1871 .items
1872 .iter()
1873 .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
1874 })
1875 .map(|item| format!("{}.{name}", item.type_()));
1876 write!(
1877 w,
1878 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1879 {}",
1880 render_rightside(cx, item, render_mode)
1881 )?;
1882 if trait_.is_some() {
1883 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1885 }
1886 write!(
1887 w,
1888 "<h4 class=\"code-header\">{}</h4></section>",
1889 render_assoc_item(
1890 item,
1891 link.anchor(source_id.as_ref().unwrap_or(&id)),
1892 ItemType::Impl,
1893 cx,
1894 render_mode,
1895 ),
1896 )?;
1897 }
1898 }
1899 clean::RequiredAssocConstItem(generics, ty) => {
1900 let source_id = format!("{item_type}.{name}");
1901 let id = cx.derive_id(&source_id);
1902 write!(
1903 w,
1904 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1905 {}",
1906 render_rightside(cx, item, render_mode)
1907 )?;
1908 if trait_.is_some() {
1909 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1911 }
1912 write!(
1913 w,
1914 "<h4 class=\"code-header\">{}</h4></section>",
1915 assoc_const(
1916 item,
1917 generics,
1918 ty,
1919 AssocConstValue::None,
1920 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1921 0,
1922 cx,
1923 ),
1924 )?;
1925 }
1926 clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
1927 let source_id = format!("{item_type}.{name}");
1928 let id = cx.derive_id(&source_id);
1929 write!(
1930 w,
1931 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1932 {}",
1933 render_rightside(cx, item, render_mode),
1934 )?;
1935 if trait_.is_some() {
1936 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1938 }
1939 write!(
1940 w,
1941 "<h4 class=\"code-header\">{}</h4></section>",
1942 assoc_const(
1943 item,
1944 &ci.generics,
1945 &ci.type_,
1946 match item.kind {
1947 clean::ProvidedAssocConstItem(_) =>
1948 AssocConstValue::TraitDefault(&ci.kind),
1949 clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
1950 _ => unreachable!(),
1951 },
1952 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1953 0,
1954 cx,
1955 ),
1956 )?;
1957 }
1958 clean::RequiredAssocTypeItem(generics, bounds) => {
1959 let source_id = format!("{item_type}.{name}");
1960 let id = cx.derive_id(&source_id);
1961 write!(
1962 w,
1963 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1964 {}",
1965 render_rightside(cx, item, render_mode),
1966 )?;
1967 if trait_.is_some() {
1968 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1970 }
1971 write!(
1972 w,
1973 "<h4 class=\"code-header\">{}</h4></section>",
1974 assoc_type(
1975 item,
1976 generics,
1977 bounds,
1978 None,
1979 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1980 0,
1981 cx,
1982 ),
1983 )?;
1984 }
1985 clean::AssocTypeItem(tydef, _bounds) => {
1986 let source_id = format!("{item_type}.{name}");
1987 let id = cx.derive_id(&source_id);
1988 write!(
1989 w,
1990 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1991 {}",
1992 render_rightside(cx, item, render_mode),
1993 )?;
1994 if trait_.is_some() {
1995 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1997 }
1998 write!(
1999 w,
2000 "<h4 class=\"code-header\">{}</h4></section>",
2001 assoc_type(
2002 item,
2003 &tydef.generics,
2004 &[], Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
2006 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2007 0,
2008 cx,
2009 ),
2010 )?;
2011 }
2012 clean::StrippedItem(..) => return Ok(()),
2013 _ => panic!("can't make docs for trait item with name {:?}", item.name),
2014 }
2015
2016 w.write_str(&info_buffer)?;
2017 if toggled {
2018 write!(w, "</summary>{doc_buffer}</details>")?;
2019 }
2020 Ok(())
2021 }
2022
2023 let mut impl_items = String::new();
2024 let mut default_impl_items = String::new();
2025 let impl_ = i.inner_impl();
2026
2027 let mut assoc_types = Vec::new();
2037 let mut methods = Vec::new();
2038
2039 if !impl_.is_negative_trait_impl() {
2040 for impl_item in &impl_.items {
2041 match impl_item.kind {
2042 clean::MethodItem(..) | clean::RequiredMethodItem(..) => {
2043 methods.push(impl_item)
2044 }
2045 clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => {
2046 assoc_types.push(impl_item)
2047 }
2048 clean::RequiredAssocConstItem(..)
2049 | clean::ProvidedAssocConstItem(_)
2050 | clean::ImplAssocConstItem(_) => {
2051 doc_impl_item(
2053 &mut default_impl_items,
2054 &mut impl_items,
2055 cx,
2056 impl_item,
2057 if trait_.is_some() { &i.impl_item } else { parent },
2058 link,
2059 render_mode,
2060 false,
2061 trait_,
2062 rendering_params,
2063 )?;
2064 }
2065 _ => {}
2066 }
2067 }
2068
2069 for assoc_type in assoc_types {
2070 doc_impl_item(
2071 &mut default_impl_items,
2072 &mut impl_items,
2073 cx,
2074 assoc_type,
2075 if trait_.is_some() { &i.impl_item } else { parent },
2076 link,
2077 render_mode,
2078 false,
2079 trait_,
2080 rendering_params,
2081 )?;
2082 }
2083 for method in methods {
2084 doc_impl_item(
2085 &mut default_impl_items,
2086 &mut impl_items,
2087 cx,
2088 method,
2089 if trait_.is_some() { &i.impl_item } else { parent },
2090 link,
2091 render_mode,
2092 false,
2093 trait_,
2094 rendering_params,
2095 )?;
2096 }
2097 }
2098
2099 fn render_default_items(
2100 mut boring: impl fmt::Write,
2101 mut interesting: impl fmt::Write,
2102 cx: &Context<'_>,
2103 t: &clean::Trait,
2104 i: &clean::Impl,
2105 parent: &clean::Item,
2106 render_mode: RenderMode,
2107 rendering_params: ImplRenderingParameters,
2108 ) -> fmt::Result {
2109 for trait_item in &t.items {
2110 if let Some(impl_def_id) = parent.item_id.as_def_id()
2113 && let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
2114 && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id))
2115 {
2116 continue;
2117 }
2118
2119 let n = trait_item.name;
2120 if i.items.iter().any(|m| m.name == n) {
2121 continue;
2122 }
2123 let did = i.trait_.as_ref().unwrap().def_id();
2124 let provided_methods = i.provided_trait_methods(cx.tcx());
2125 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
2126
2127 doc_impl_item(
2128 &mut boring,
2129 &mut interesting,
2130 cx,
2131 trait_item,
2132 parent,
2133 assoc_link,
2134 render_mode,
2135 true,
2136 Some(t),
2137 rendering_params,
2138 )?;
2139 }
2140 Ok(())
2141 }
2142
2143 if rendering_params.show_default_items
2148 && let Some(t) = trait_
2149 && !impl_.is_negative_trait_impl()
2150 {
2151 render_default_items(
2152 &mut default_impl_items,
2153 &mut impl_items,
2154 cx,
2155 t,
2156 impl_,
2157 &i.impl_item,
2158 render_mode,
2159 rendering_params,
2160 )?;
2161 }
2162 if render_mode == RenderMode::Normal {
2163 let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
2164 let deprecation_attr = if impl_.is_deprecated
2165 || trait_.is_some_and(|trait_| trait_.is_deprecated(cx.tcx()))
2166 {
2167 " deprecated"
2168 } else {
2169 ""
2170 };
2171 if toggled {
2172 close_tags.push("</details>");
2173 write!(
2174 w,
2175 "<details class=\"toggle implementors-toggle{deprecation_attr}\"{}>\
2176 <summary>",
2177 if rendering_params.toggle_open_by_default { " open" } else { "" }
2178 )?;
2179 }
2180
2181 let (before_dox, after_dox) = i
2182 .impl_item
2183 .opt_doc_value()
2184 .map(|dox| {
2185 Markdown {
2186 content: &dox,
2187 links: &i.impl_item.links(cx),
2188 ids: &mut cx.id_map.borrow_mut(),
2189 error_codes: cx.shared.codes,
2190 edition: cx.shared.edition(),
2191 playground: &cx.shared.playground,
2192 heading_offset: HeadingOffset::H4,
2193 }
2194 .split_summary_and_content()
2195 })
2196 .unwrap_or((None, None));
2197
2198 write!(
2199 w,
2200 "{}",
2201 render_impl_summary(
2202 cx,
2203 i,
2204 parent,
2205 rendering_params.show_def_docs,
2206 use_absolute,
2207 aliases,
2208 before_dox.as_deref(),
2209 trait_.is_none() && impl_.items.is_empty(),
2210 )
2211 )?;
2212 if toggled {
2213 w.write_str("</summary>")?;
2214 }
2215
2216 if before_dox.is_some()
2217 && let Some(after_dox) = after_dox
2218 {
2219 write!(w, "<div class=\"docblock\">{after_dox}</div>")?;
2220 }
2221
2222 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2223 w.write_str("<div class=\"impl-items\">")?;
2224 close_tags.push("</div>");
2225 }
2226 }
2227 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2228 w.write_str(&default_impl_items)?;
2229 w.write_str(&impl_items)?;
2230 }
2231 for tag in close_tags.into_iter().rev() {
2232 w.write_str(tag)?;
2233 }
2234 Ok(())
2235 })
2236}
2237
2238fn render_rightside(
2241 cx: &Context<'_>,
2242 item: &clean::Item,
2243 render_mode: RenderMode,
2244) -> impl fmt::Display {
2245 let tcx = cx.tcx();
2246
2247 fmt::from_fn(move |w| {
2248 let const_stability = match render_mode {
2251 RenderMode::Normal => item.const_stability(tcx),
2252 RenderMode::ForDeref { .. } => None,
2253 };
2254 let src_href = cx.src_href(item);
2255 let stability = render_stability_since_raw_with_extra(
2256 item.stable_since(tcx),
2257 const_stability,
2258 if src_href.is_some() { "" } else { " rightside" },
2259 );
2260
2261 match (stability, src_href) {
2262 (Some(stability), Some(link)) => {
2263 write!(
2264 w,
2265 "<span class=\"rightside\">{stability} · <a class=\"src\" href=\"{link}\">Source</a></span>",
2266 )
2267 }
2268 (Some(stability), None) => {
2269 write!(w, "{stability}")
2270 }
2271 (None, Some(link)) => {
2272 write!(w, "<a class=\"src rightside\" href=\"{link}\">Source</a>")
2273 }
2274 (None, None) => Ok(()),
2275 }
2276 })
2277}
2278
2279fn render_impl_summary(
2280 cx: &Context<'_>,
2281 i: &Impl,
2282 parent: &clean::Item,
2283 show_def_docs: bool,
2284 use_absolute: Option<bool>,
2285 aliases: &[String],
2288 doc: Option<&str>,
2289 impl_is_empty: bool,
2290) -> impl fmt::Display {
2291 fmt::from_fn(move |w| {
2292 let inner_impl = i.inner_impl();
2293 let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
2294 let aliases = (!aliases.is_empty())
2295 .then_some(fmt::from_fn(|f| {
2296 write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f)))
2297 }))
2298 .maybe_display();
2299 write!(
2300 w,
2301 "<section id=\"{id}\" class=\"impl\"{aliases}>\
2302 {}\
2303 <a href=\"#{id}\" class=\"anchor\">§</a>\
2304 <h3 class=\"code-header\">",
2305 render_rightside(cx, &i.impl_item, RenderMode::Normal)
2306 )?;
2307
2308 if let Some(use_absolute) = use_absolute {
2309 write!(w, "{}", print_impl(inner_impl, use_absolute, cx))?;
2310 if show_def_docs {
2311 for it in &inner_impl.items {
2312 if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
2313 write!(
2314 w,
2315 "<div class=\"where\"> {};</div>",
2316 assoc_type(
2317 it,
2318 &tydef.generics,
2319 &[], Some(&tydef.type_),
2321 AssocItemLink::Anchor(None),
2322 0,
2323 cx,
2324 )
2325 )?;
2326 }
2327 }
2328 }
2329 } else {
2330 write!(w, "{}", print_impl(inner_impl, false, cx))?;
2331 }
2332 w.write_str("</h3>")?;
2333
2334 let is_trait = inner_impl.trait_.is_some();
2335 if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) {
2336 write!(
2337 w,
2338 "<span class=\"item-info\">\
2339 <div class=\"stab portability\">{portability}</div>\
2340 </span>",
2341 )?;
2342 }
2343
2344 if let Some(doc) = doc {
2345 if impl_is_empty {
2346 w.write_str(
2347 "\
2348<div class=\"item-info\">\
2349 <div class=\"stab empty-impl\">This impl block contains no public items.</div>\
2350</div>",
2351 )?;
2352 }
2353 write!(w, "<div class=\"docblock\">{doc}</div>")?;
2354 }
2355
2356 w.write_str("</section>")
2357 })
2358}
2359
2360pub(crate) fn small_url_encode(s: String) -> String {
2361 fn dont_escape(c: u8) -> bool {
2366 c.is_ascii_alphanumeric()
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'?'
2381 || c == b'='
2385 }
2386 let mut st = String::new();
2387 let mut last_match = 0;
2388 for (idx, b) in s.bytes().enumerate() {
2389 if dont_escape(b) {
2390 continue;
2391 }
2392
2393 if last_match != idx {
2394 st += &s[last_match..idx];
2396 }
2397 if b == b' ' {
2398 st += "+";
2402 } else {
2403 write!(st, "%{b:02X}").unwrap();
2404 }
2405 last_match = idx + 1;
2411 }
2412
2413 if last_match != 0 {
2414 st += &s[last_match..];
2415 st
2416 } else {
2417 s
2418 }
2419}
2420
2421fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String {
2422 use rustc_middle::ty::print::with_forced_trimmed_paths;
2423 let (type_, trait_) = match impl_id {
2424 ItemId::Auto { trait_, for_ } => {
2425 let ty = tcx.type_of(for_).skip_binder();
2426 (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
2427 }
2428 ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
2429 if let Some(trait_ref) = tcx.impl_opt_trait_ref(impl_id) {
2430 let trait_ref = trait_ref.skip_binder();
2431 (trait_ref.self_ty(), Some(trait_ref))
2432 } else {
2433 (tcx.type_of(impl_id).skip_binder(), None)
2434 }
2435 }
2436 };
2437 with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
2438 format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
2439 } else {
2440 format!("impl-{type_}")
2441 }))
2442}
2443
2444fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2445 match item.kind {
2446 clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
2447 Some((
2450 format!("{:#}", print_type(&i.for_, cx)),
2451 get_id_for_impl(cx.tcx(), item.item_id),
2452 ))
2453 }
2454 _ => None,
2455 }
2456}
2457
2458pub(crate) fn get_filtered_impls_for_reference<'a>(
2462 shared: &'a SharedContext<'_>,
2463 it: &clean::Item,
2464) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
2465 let def_id = it.item_id.expect_def_id();
2466 let Some(v) = shared.cache.impls.get(&def_id) else {
2468 return (Vec::new(), Vec::new(), Vec::new());
2469 };
2470 let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
2473 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
2474 traits.partition(|t| t.inner_impl().kind.is_auto());
2475
2476 let (blanket_impl, concrete): (Vec<&Impl>, _) =
2477 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
2478 let concrete: Vec<_> = concrete
2480 .into_iter()
2481 .filter(|t| match t.inner_impl().for_ {
2482 clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
2483 _ => false,
2484 })
2485 .collect();
2486
2487 (concrete, synthetic, blanket_impl)
2488}
2489
2490#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2491pub(crate) enum ItemSection {
2492 Reexports,
2493 PrimitiveTypes,
2494 Modules,
2495 Macros,
2496 Structs,
2497 Enums,
2498 Constants,
2499 Statics,
2500 Traits,
2501 Functions,
2502 TypeAliases,
2503 Unions,
2504 Implementations,
2505 TypeMethods,
2506 Methods,
2507 StructFields,
2508 Variants,
2509 AssociatedTypes,
2510 AssociatedConstants,
2511 ForeignTypes,
2512 Keywords,
2513 Attributes,
2514 AttributeMacros,
2515 DeriveMacros,
2516 TraitAliases,
2517}
2518
2519impl ItemSection {
2520 const ALL: &'static [Self] = {
2521 use ItemSection::*;
2522 &[
2525 Reexports,
2526 PrimitiveTypes,
2527 Modules,
2528 Macros,
2529 Structs,
2530 Enums,
2531 Constants,
2532 Statics,
2533 Traits,
2534 Functions,
2535 TypeAliases,
2536 Unions,
2537 Implementations,
2538 TypeMethods,
2539 Methods,
2540 StructFields,
2541 Variants,
2542 AssociatedTypes,
2543 AssociatedConstants,
2544 ForeignTypes,
2545 Keywords,
2546 Attributes,
2547 AttributeMacros,
2548 DeriveMacros,
2549 TraitAliases,
2550 ]
2551 };
2552
2553 fn id(self) -> &'static str {
2554 match self {
2555 Self::Reexports => "reexports",
2556 Self::Modules => "modules",
2557 Self::Structs => "structs",
2558 Self::Unions => "unions",
2559 Self::Enums => "enums",
2560 Self::Functions => "functions",
2561 Self::TypeAliases => "types",
2562 Self::Statics => "statics",
2563 Self::Constants => "constants",
2564 Self::Traits => "traits",
2565 Self::Implementations => "impls",
2566 Self::TypeMethods => "tymethods",
2567 Self::Methods => "methods",
2568 Self::StructFields => "fields",
2569 Self::Variants => "variants",
2570 Self::Macros => "macros",
2571 Self::PrimitiveTypes => "primitives",
2572 Self::AssociatedTypes => "associated-types",
2573 Self::AssociatedConstants => "associated-consts",
2574 Self::ForeignTypes => "foreign-types",
2575 Self::Keywords => "keywords",
2576 Self::Attributes => "attributes",
2577 Self::AttributeMacros => "attributes",
2578 Self::DeriveMacros => "derives",
2579 Self::TraitAliases => "trait-aliases",
2580 }
2581 }
2582
2583 fn name(self) -> &'static str {
2584 match self {
2585 Self::Reexports => "Re-exports",
2586 Self::Modules => "Modules",
2587 Self::Structs => "Structs",
2588 Self::Unions => "Unions",
2589 Self::Enums => "Enums",
2590 Self::Functions => "Functions",
2591 Self::TypeAliases => "Type Aliases",
2592 Self::Statics => "Statics",
2593 Self::Constants => "Constants",
2594 Self::Traits => "Traits",
2595 Self::Implementations => "Implementations",
2596 Self::TypeMethods => "Type Methods",
2597 Self::Methods => "Methods",
2598 Self::StructFields => "Struct Fields",
2599 Self::Variants => "Variants",
2600 Self::Macros => "Macros",
2601 Self::PrimitiveTypes => "Primitive Types",
2602 Self::AssociatedTypes => "Associated Types",
2603 Self::AssociatedConstants => "Associated Constants",
2604 Self::ForeignTypes => "Foreign Types",
2605 Self::Keywords => "Keywords",
2606 Self::Attributes => "Attributes",
2607 Self::AttributeMacros => "Attribute Macros",
2608 Self::DeriveMacros => "Derive Macros",
2609 Self::TraitAliases => "Trait Aliases",
2610 }
2611 }
2612}
2613
2614fn item_ty_to_section(ty: ItemType) -> ItemSection {
2615 match ty {
2616 ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
2617 ItemType::Module => ItemSection::Modules,
2618 ItemType::Struct => ItemSection::Structs,
2619 ItemType::Union => ItemSection::Unions,
2620 ItemType::Enum => ItemSection::Enums,
2621 ItemType::Function => ItemSection::Functions,
2622 ItemType::TypeAlias => ItemSection::TypeAliases,
2623 ItemType::Static => ItemSection::Statics,
2624 ItemType::Constant => ItemSection::Constants,
2625 ItemType::Trait => ItemSection::Traits,
2626 ItemType::Impl => ItemSection::Implementations,
2627 ItemType::TyMethod => ItemSection::TypeMethods,
2628 ItemType::Method => ItemSection::Methods,
2629 ItemType::StructField => ItemSection::StructFields,
2630 ItemType::Variant => ItemSection::Variants,
2631 ItemType::Macro => ItemSection::Macros,
2632 ItemType::Primitive => ItemSection::PrimitiveTypes,
2633 ItemType::AssocType => ItemSection::AssociatedTypes,
2634 ItemType::AssocConst => ItemSection::AssociatedConstants,
2635 ItemType::ForeignType => ItemSection::ForeignTypes,
2636 ItemType::Keyword => ItemSection::Keywords,
2637 ItemType::Attribute => ItemSection::Attributes,
2638 ItemType::ProcAttribute => ItemSection::AttributeMacros,
2639 ItemType::ProcDerive => ItemSection::DeriveMacros,
2640 ItemType::TraitAlias => ItemSection::TraitAliases,
2641 }
2642}
2643
2644fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec<String> {
2651 let mut out = Vec::new();
2652 let mut visited = FxHashSet::default();
2653 let mut work = VecDeque::new();
2654
2655 let mut process_path = |did: DefId| {
2656 let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
2657 let fqp = cache.exact_paths.get(&did).or_else(get_extern);
2658
2659 if let Some(path) = fqp {
2660 out.push(join_path_syms(path));
2661 }
2662 };
2663
2664 work.push_back(first_ty);
2665
2666 while let Some(ty) = work.pop_front() {
2667 if !visited.insert(ty) {
2668 continue;
2669 }
2670
2671 match ty {
2672 clean::Type::Path { path } => process_path(path.def_id()),
2673 clean::Type::Tuple(tys) => {
2674 work.extend(tys.iter());
2675 }
2676 clean::Type::Slice(ty) => {
2677 work.push_back(ty);
2678 }
2679 clean::Type::Array(ty, _) => {
2680 work.push_back(ty);
2681 }
2682 clean::Type::RawPointer(_, ty) => {
2683 work.push_back(ty);
2684 }
2685 clean::Type::BorrowedRef { type_, .. } => {
2686 work.push_back(type_);
2687 }
2688 clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
2689 work.push_back(self_type);
2690 if let Some(trait_) = trait_ {
2691 process_path(trait_.def_id());
2692 }
2693 }
2694 _ => {}
2695 }
2696 }
2697 out
2698}
2699
2700const MAX_FULL_EXAMPLES: usize = 5;
2701const NUM_VISIBLE_LINES: usize = 10;
2702
2703fn render_call_locations<W: fmt::Write>(
2705 mut w: W,
2706 cx: &Context<'_>,
2707 item: &clean::Item,
2708) -> fmt::Result {
2709 let tcx = cx.tcx();
2710 let def_id = item.item_id.expect_def_id();
2711 let key = tcx.def_path_hash(def_id);
2712 let Some(call_locations) = cx.shared.call_locations.get(&key) else { return Ok(()) };
2713
2714 let id = cx.derive_id("scraped-examples");
2716 write!(
2717 &mut w,
2718 "<div class=\"docblock scraped-example-list\">\
2719 <span></span>\
2720 <h5 id=\"{id}\">\
2721 <a href=\"#{id}\">Examples found in repository</a>\
2722 <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
2723 </h5>",
2724 root_path = cx.root_path(),
2725 id = id
2726 )?;
2727
2728 let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
2730 let (line_lo, line_hi) = loc.call_expr.line_span;
2731 let (anchor, title) = if line_lo == line_hi {
2732 ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
2733 } else {
2734 (
2735 format!("{}-{}", line_lo + 1, line_hi + 1),
2736 format!("lines {}-{}", line_lo + 1, line_hi + 1),
2737 )
2738 };
2739 let url = format!("{}{}#{anchor}", cx.root_path(), call_data.url);
2740 (url, title)
2741 };
2742
2743 let write_example = |w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
2745 let contents = match fs::read_to_string(path) {
2746 Ok(contents) => contents,
2747 Err(err) => {
2748 let span = item.span(tcx).map_or(DUMMY_SP, |span| span.inner());
2749 tcx.dcx().span_err(span, format!("failed to read file {}: {err}", path.display()));
2750 return false;
2751 }
2752 };
2753
2754 assert!(!call_data.locations.is_empty());
2757 let min_loc =
2758 call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2759 let byte_min = min_loc.enclosing_item.byte_span.0;
2760 let line_min = min_loc.enclosing_item.line_span.0;
2761 let max_loc =
2762 call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
2763 let byte_max = max_loc.enclosing_item.byte_span.1;
2764 let line_max = max_loc.enclosing_item.line_span.1;
2765
2766 let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
2768
2769 let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
2772 .locations
2773 .iter()
2774 .map(|loc| {
2775 let (byte_lo, byte_hi) = loc.call_ident.byte_span;
2776 let (line_lo, line_hi) = loc.call_expr.line_span;
2777 let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
2778
2779 let line_range = (line_lo - line_min, line_hi - line_min);
2780 let (line_url, line_title) = link_to_loc(call_data, loc);
2781
2782 (byte_range, (line_range, line_url, line_title))
2783 })
2784 .unzip();
2785
2786 let (_, init_url, init_title) = &line_ranges[0];
2787 let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
2788 let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
2789
2790 let file_span = rustc_span::DUMMY_SP;
2796
2797 let mut decoration_info = FxIndexMap::default();
2798 decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
2799 decoration_info.insert("highlight", byte_ranges);
2800
2801 sources::print_src(
2802 w,
2803 contents_subset,
2804 file_span,
2805 cx,
2806 &cx.root_path(),
2807 &highlight::DecorationInfo(decoration_info),
2808 &sources::SourceContext::Embedded(sources::ScrapedInfo {
2809 needs_expansion,
2810 offset: line_min,
2811 name: &call_data.display_name,
2812 url: init_url,
2813 title: init_title,
2814 locations: locations_encoded,
2815 }),
2816 )
2817 .unwrap();
2818
2819 true
2820 };
2821
2822 let ordered_locations = {
2834 fn sort_criterion<'a>(
2835 (_, call_data): &(&PathBuf, &'a CallData),
2836 ) -> (bool, u32, &'a String) {
2837 let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2839 (!call_data.is_bin, hi - lo, &call_data.display_name)
2840 }
2841
2842 let mut locs = call_locations.iter().collect::<Vec<_>>();
2843 locs.sort_by_key(sort_criterion);
2844 locs
2845 };
2846
2847 let mut it = ordered_locations.into_iter().peekable();
2848
2849 let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
2852 for example in it.by_ref() {
2853 if write_example(&mut *w, example) {
2854 break;
2855 }
2856 }
2857 };
2858
2859 write_and_skip_failure(&mut w, &mut it);
2861
2862 if it.peek().is_some() {
2864 write!(
2865 w,
2866 "<details class=\"toggle more-examples-toggle\">\
2867 <summary class=\"hideme\">\
2868 <span>More examples</span>\
2869 </summary>\
2870 <div class=\"hide-more\">Hide additional examples</div>\
2871 <div class=\"more-scraped-examples\">\
2872 <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
2873 )?;
2874
2875 for _ in 0..MAX_FULL_EXAMPLES {
2878 write_and_skip_failure(&mut w, &mut it);
2879 }
2880
2881 if it.peek().is_some() {
2883 w.write_str(
2884 r#"<div class="example-links">Additional examples can be found in:<br><ul>"#,
2885 )?;
2886 it.try_for_each(|(_, call_data)| {
2887 let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
2888 write!(
2889 w,
2890 r#"<li><a href="{url}">{name}</a></li>"#,
2891 url = url,
2892 name = call_data.display_name
2893 )
2894 })?;
2895 w.write_str("</ul></div>")?;
2896 }
2897
2898 w.write_str("</div></details>")?;
2899 }
2900
2901 w.write_str("</div>")
2902}
2903
2904fn render_attributes_in_code(
2905 w: &mut impl fmt::Write,
2906 item: &clean::Item,
2907 prefix: &str,
2908 cx: &Context<'_>,
2909) -> fmt::Result {
2910 render_attributes_in_code_with_options(w, item, prefix, cx, true, "")
2911}
2912
2913pub(super) fn render_attributes_in_code_with_options(
2914 w: &mut impl fmt::Write,
2915 item: &clean::Item,
2916 prefix: &str,
2917 cx: &Context<'_>,
2918 render_doc_hidden: bool,
2919 open_tag: &str,
2920) -> fmt::Result {
2921 w.write_str(open_tag)?;
2922 if render_doc_hidden && item.is_doc_hidden() {
2923 render_code_attribute(prefix, "#[doc(hidden)]", w)?;
2924 }
2925 for attr in &item.attrs.other_attrs {
2926 let hir::Attribute::Parsed(kind) = attr else { continue };
2927 let attr = match kind {
2928 AttributeKind::LinkSection { name, .. } => {
2929 Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}"))))
2930 }
2931 AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"),
2932 AttributeKind::ExportName { name, .. } => {
2933 Cow::Owned(format!("#[unsafe(export_name = {})]", Escape(&format!("{name:?}"))))
2934 }
2935 AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"),
2936 _ => continue,
2937 };
2938 render_code_attribute(prefix, attr.as_ref(), w)?;
2939 }
2940
2941 if let Some(def_id) = item.def_id()
2942 && let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id)
2943 {
2944 render_code_attribute(prefix, &repr, w)?;
2945 }
2946 Ok(())
2947}
2948
2949fn render_repr_attribute_in_code(
2950 w: &mut impl fmt::Write,
2951 cx: &Context<'_>,
2952 def_id: DefId,
2953) -> fmt::Result {
2954 if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) {
2955 render_code_attribute("", &repr, w)?;
2956 }
2957 Ok(())
2958}
2959
2960fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) -> fmt::Result {
2961 write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>")
2962}
2963
2964fn repr_attribute<'tcx>(
2969 tcx: TyCtxt<'tcx>,
2970 cache: &Cache,
2971 def_id: DefId,
2972) -> Option<Cow<'static, str>> {
2973 let adt = match tcx.def_kind(def_id) {
2974 DefKind::Struct | DefKind::Enum | DefKind::Union => tcx.adt_def(def_id),
2975 _ => return None,
2976 };
2977 let repr = adt.repr();
2978
2979 let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id);
2980 let is_public_field = |field: &ty::FieldDef| {
2981 (cache.document_private || field.vis.is_public()) && is_visible(field.did)
2982 };
2983
2984 if repr.transparent() {
2985 let is_public = 'is_public: {
2988 let var = adt.variant(rustc_abi::FIRST_VARIANT); if !is_visible(var.def_id) {
2992 break 'is_public false;
2993 }
2994
2995 let non_1zst_field = var.fields.iter().find(|field| {
2997 let ty = ty::TypingEnv::post_analysis(tcx, field.did)
2998 .as_query_input(tcx.type_of(field.did).instantiate_identity());
2999 tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst())
3000 });
3001
3002 match non_1zst_field {
3003 Some(field) => is_public_field(field),
3004 None => var.fields.is_empty() || var.fields.iter().any(is_public_field),
3005 }
3006 };
3007
3008 return is_public.then(|| "#[repr(transparent)]".into());
3011 }
3012
3013 if !repr.c()
3017 && !repr.simd()
3018 && repr.int.is_none()
3019 && repr.pack.is_none()
3020 && repr.align.is_none()
3021 {
3022 return None;
3023 }
3024
3025 let is_public = adt
3027 .variants()
3028 .iter()
3029 .all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field));
3030 if !is_public {
3031 return None;
3032 }
3033
3034 let mut result = Vec::<Cow<'_, _>>::new();
3035
3036 if repr.c() {
3037 result.push("C".into());
3038 }
3039 if repr.simd() {
3040 result.push("simd".into());
3041 }
3042 if let Some(int) = repr.int {
3043 let prefix = if int.is_signed() { 'i' } else { 'u' };
3044 let int = match int {
3045 rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"),
3046 rustc_abi::IntegerType::Fixed(int, _) => {
3047 format!("{prefix}{}", int.size().bytes() * 8)
3048 }
3049 };
3050 result.push(int.into());
3051 }
3052
3053 if let Some(pack) = repr.pack {
3055 result.push(format!("packed({})", pack.bytes()).into());
3056 }
3057 if let Some(align) = repr.align {
3058 result.push(format!("align({})", align.bytes()).into());
3059 }
3060
3061 (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into())
3062}