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