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};
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 links = item.links(cx);
881 let html = MarkdownItemInfo::new(note, &links, &mut id_map);
882 message.push_str(": ");
883 html.write_into(&mut message).unwrap();
884 }
885 extra_info.push(ShortItemInfo::Deprecation { message });
886 }
887
888 if let Some((StabilityLevel::Unstable { reason: _, issue, .. }, feature)) = item
891 .stability(cx.tcx())
892 .as_ref()
893 .filter(|stab| stab.feature != sym::rustc_private)
894 .map(|stab| (stab.level, stab.feature))
895 {
896 let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue)
897 {
898 Some((url.clone(), issue.get()))
899 } else {
900 None
901 };
902 extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking });
903 }
904
905 if let Some(message) = portability(item, parent) {
906 extra_info.push(ShortItemInfo::Portability { message });
907 }
908
909 extra_info
910}
911
912fn render_impls(
915 cx: &Context<'_>,
916 mut w: impl Write,
917 impls: &[&Impl],
918 containing_item: &clean::Item,
919 toggle_open_by_default: bool,
920) -> fmt::Result {
921 let mut rendered_impls = impls
922 .iter()
923 .map(|i| {
924 let did = i.trait_did().unwrap();
925 let provided_trait_methods = i.inner_impl().provided_trait_methods(cx.tcx());
926 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods);
927 let imp = render_impl(
928 cx,
929 i,
930 containing_item,
931 assoc_link,
932 RenderMode::Normal,
933 None,
934 &[],
935 ImplRenderingParameters {
936 show_def_docs: true,
937 show_default_items: true,
938 show_non_assoc_items: true,
939 toggle_open_by_default,
940 },
941 );
942 imp.to_string()
943 })
944 .collect::<Vec<_>>();
945 rendered_impls.sort();
946 w.write_str(&rendered_impls.join(""))
947}
948
949fn assoc_href_attr(
951 it: &clean::Item,
952 link: AssocItemLink<'_>,
953 cx: &Context<'_>,
954) -> Option<impl fmt::Display> {
955 let name = it.name.unwrap();
956 let item_type = it.type_();
957
958 enum Href<'a> {
959 AnchorId(&'a str),
960 Anchor(ItemType),
961 Url(String, ItemType),
962 }
963
964 let href = match link {
965 AssocItemLink::Anchor(Some(id)) => Href::AnchorId(id),
966 AssocItemLink::Anchor(None) => Href::Anchor(item_type),
967 AssocItemLink::GotoSource(did, provided_methods) => {
968 let item_type = match item_type {
971 ItemType::Method | ItemType::TyMethod => {
975 if provided_methods.contains(&name) {
976 ItemType::Method
977 } else {
978 ItemType::TyMethod
979 }
980 }
981 item_type => item_type,
983 };
984
985 match href(did.expect_def_id(), cx) {
986 Ok(HrefInfo { url, .. }) => Href::Url(url, item_type),
987 Err(HrefError::DocumentationNotBuilt) => return None,
999 Err(_) => Href::Anchor(item_type),
1000 }
1001 }
1002 };
1003
1004 let href = fmt::from_fn(move |f| match &href {
1005 Href::AnchorId(id) => write!(f, "#{id}"),
1006 Href::Url(url, item_type) => {
1007 write!(f, "{url}#{item_type}.{name}")
1008 }
1009 Href::Anchor(item_type) => {
1010 write!(f, "#{item_type}.{name}")
1011 }
1012 });
1013
1014 Some(fmt::from_fn(move |f| write!(f, " href=\"{href}\"")))
1017}
1018
1019#[derive(Debug)]
1020enum AssocConstValue<'a> {
1021 TraitDefault(&'a clean::ConstantKind),
1025 Impl(&'a clean::ConstantKind),
1027 None,
1028}
1029
1030fn assoc_const(
1031 it: &clean::Item,
1032 generics: &clean::Generics,
1033 ty: &clean::Type,
1034 value: AssocConstValue<'_>,
1035 link: AssocItemLink<'_>,
1036 indent: usize,
1037 cx: &Context<'_>,
1038) -> impl fmt::Display {
1039 let tcx = cx.tcx();
1040 fmt::from_fn(move |w| {
1041 render_attributes_in_code(w, it, &" ".repeat(indent), cx)?;
1042 write!(
1043 w,
1044 "{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
1045 indent = " ".repeat(indent),
1046 vis = visibility_print_with_space(it, cx),
1047 href = assoc_href_attr(it, link, cx).maybe_display(),
1048 name = it.name.as_ref().unwrap(),
1049 generics = print_generics(generics, cx),
1050 ty = print_type(ty, cx),
1051 )?;
1052 if let AssocConstValue::TraitDefault(konst) | AssocConstValue::Impl(konst) = value {
1053 let repr = konst.expr(tcx);
1054 if match value {
1055 AssocConstValue::TraitDefault(_) => true, AssocConstValue::Impl(_) => repr != "_", AssocConstValue::None => unreachable!(),
1060 } {
1061 write!(w, " = {}", Escape(&repr))?;
1062 }
1063 }
1064 write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display())
1065 })
1066}
1067
1068fn assoc_type(
1069 it: &clean::Item,
1070 generics: &clean::Generics,
1071 bounds: &[clean::GenericBound],
1072 default: Option<&clean::Type>,
1073 link: AssocItemLink<'_>,
1074 indent: usize,
1075 cx: &Context<'_>,
1076) -> impl fmt::Display {
1077 fmt::from_fn(move |w| {
1078 write!(
1079 w,
1080 "{indent}{vis}type <a{href} class=\"associatedtype\">{name}</a>{generics}",
1081 indent = " ".repeat(indent),
1082 vis = visibility_print_with_space(it, cx),
1083 href = assoc_href_attr(it, link, cx).maybe_display(),
1084 name = it.name.as_ref().unwrap(),
1085 generics = print_generics(generics, cx),
1086 )?;
1087 if !bounds.is_empty() {
1088 write!(w, ": {}", print_generic_bounds(bounds, cx))?;
1089 }
1090 if let Some(default) = default {
1092 write!(w, " = {}", print_type(default, cx))?;
1093 }
1094 write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display())
1095 })
1096}
1097
1098fn assoc_method(
1099 meth: &clean::Item,
1100 g: &clean::Generics,
1101 d: &clean::FnDecl,
1102 link: AssocItemLink<'_>,
1103 parent: ItemType,
1104 cx: &Context<'_>,
1105 render_mode: RenderMode,
1106) -> impl fmt::Display {
1107 let tcx = cx.tcx();
1108 let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item");
1109 let name = meth.name.as_ref().unwrap();
1110 let vis = visibility_print_with_space(meth, cx).to_string();
1111 let defaultness = print_default_space(meth.is_default());
1112 let constness = match render_mode {
1115 RenderMode::Normal => print_constness_with_space(
1116 &header.constness,
1117 meth.stable_since(tcx),
1118 meth.const_stability(tcx),
1119 ),
1120 RenderMode::ForDeref { .. } => "",
1121 };
1122
1123 fmt::from_fn(move |w| {
1124 let asyncness = header.asyncness.print_with_space();
1125 let safety = header.safety.print_with_space();
1126 let abi = print_abi_with_space(header.abi).to_string();
1127 let href = assoc_href_attr(meth, link, cx).maybe_display();
1128
1129 let generics_len = format!("{:#}", print_generics(g, cx)).len();
1131 let mut header_len = "fn ".len()
1132 + vis.len()
1133 + defaultness.len()
1134 + constness.len()
1135 + asyncness.len()
1136 + safety.len()
1137 + abi.len()
1138 + name.as_str().len()
1139 + generics_len;
1140
1141 let notable_traits = notable_traits_button(&d.output, cx).maybe_display();
1142
1143 let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
1144 header_len += 4;
1145 let indent_str = " ";
1146 render_attributes_in_code(w, meth, indent_str, cx)?;
1147 (4, indent_str, Ending::NoNewline)
1148 } else {
1149 render_attributes_in_code(w, meth, "", cx)?;
1150 (0, "", Ending::Newline)
1151 };
1152 write!(
1153 w,
1154 "{indent}{vis}{defaultness}{constness}{asyncness}{safety}{abi}fn \
1155 <a{href} class=\"fn\">{name}</a>{generics}{decl}{notable_traits}{where_clause}",
1156 indent = indent_str,
1157 generics = print_generics(g, cx),
1158 decl = full_print_fn_decl(d, header_len, indent, cx),
1159 where_clause = print_where_clause(g, cx, indent, end_newline).maybe_display(),
1160 )
1161 })
1162}
1163
1164fn render_stability_since_raw_with_extra(
1179 stable_version: Option<StableSince>,
1180 const_stability: Option<ConstStability>,
1181 extra_class: &str,
1182) -> Option<impl fmt::Display> {
1183 let mut title = String::new();
1184 let mut stability = String::new();
1185
1186 if let Some(version) = stable_version.and_then(|version| since_to_string(&version)) {
1187 stability.push_str(&version);
1188 title.push_str(&format!("Stable since Rust version {version}"));
1189 }
1190
1191 let const_title_and_stability = match const_stability {
1192 Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) => {
1193 since_to_string(&since)
1194 .map(|since| (format!("const since {since}"), format!("const: {since}")))
1195 }
1196 Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => {
1197 if stable_version.is_none() {
1198 None
1200 } else {
1201 let unstable = if let Some(n) = issue {
1202 format!(
1203 "<a \
1204 href=\"https://github.com/rust-lang/rust/issues/{n}\" \
1205 title=\"Tracking issue for {feature}\"\
1206 >unstable</a>"
1207 )
1208 } else {
1209 String::from("unstable")
1210 };
1211
1212 Some((String::from("const unstable"), format!("const: {unstable}")))
1213 }
1214 }
1215 _ => None,
1216 };
1217
1218 if let Some((const_title, const_stability)) = const_title_and_stability {
1219 if !title.is_empty() {
1220 title.push_str(&format!(", {const_title}"));
1221 } else {
1222 title.push_str(&const_title);
1223 }
1224
1225 if !stability.is_empty() {
1226 stability.push_str(&format!(" ({const_stability})"));
1227 } else {
1228 stability.push_str(&const_stability);
1229 }
1230 }
1231
1232 (!stability.is_empty()).then_some(fmt::from_fn(move |w| {
1233 write!(w, r#"<span class="since{extra_class}" title="{title}">{stability}</span>"#)
1234 }))
1235}
1236
1237fn since_to_string(since: &StableSince) -> Option<String> {
1238 match since {
1239 StableSince::Version(since) => Some(since.to_string()),
1240 StableSince::Current => Some(RustcVersion::CURRENT.to_string()),
1241 StableSince::Err(_) => None,
1242 }
1243}
1244
1245#[inline]
1246fn render_stability_since_raw(
1247 ver: Option<StableSince>,
1248 const_stability: Option<ConstStability>,
1249) -> Option<impl fmt::Display> {
1250 render_stability_since_raw_with_extra(ver, const_stability, "")
1251}
1252
1253fn render_assoc_item(
1254 item: &clean::Item,
1255 link: AssocItemLink<'_>,
1256 parent: ItemType,
1257 cx: &Context<'_>,
1258 render_mode: RenderMode,
1259) -> impl fmt::Display {
1260 fmt::from_fn(move |f| match &item.kind {
1261 clean::StrippedItem(..) => Ok(()),
1262 clean::RequiredMethodItem(m) | clean::MethodItem(m, _) => {
1263 assoc_method(item, &m.generics, &m.decl, link, parent, cx, render_mode).fmt(f)
1264 }
1265 clean::RequiredAssocConstItem(generics, ty) => assoc_const(
1266 item,
1267 generics,
1268 ty,
1269 AssocConstValue::None,
1270 link,
1271 if parent == ItemType::Trait { 4 } else { 0 },
1272 cx,
1273 )
1274 .fmt(f),
1275 clean::ProvidedAssocConstItem(ci) => assoc_const(
1276 item,
1277 &ci.generics,
1278 &ci.type_,
1279 AssocConstValue::TraitDefault(&ci.kind),
1280 link,
1281 if parent == ItemType::Trait { 4 } else { 0 },
1282 cx,
1283 )
1284 .fmt(f),
1285 clean::ImplAssocConstItem(ci) => assoc_const(
1286 item,
1287 &ci.generics,
1288 &ci.type_,
1289 AssocConstValue::Impl(&ci.kind),
1290 link,
1291 if parent == ItemType::Trait { 4 } else { 0 },
1292 cx,
1293 )
1294 .fmt(f),
1295 clean::RequiredAssocTypeItem(generics, bounds) => assoc_type(
1296 item,
1297 generics,
1298 bounds,
1299 None,
1300 link,
1301 if parent == ItemType::Trait { 4 } else { 0 },
1302 cx,
1303 )
1304 .fmt(f),
1305 clean::AssocTypeItem(ty, bounds) => assoc_type(
1306 item,
1307 &ty.generics,
1308 bounds,
1309 Some(ty.item_type.as_ref().unwrap_or(&ty.type_)),
1310 link,
1311 if parent == ItemType::Trait { 4 } else { 0 },
1312 cx,
1313 )
1314 .fmt(f),
1315 _ => panic!("render_assoc_item called on non-associated-item"),
1316 })
1317}
1318
1319#[derive(Copy, Clone)]
1320enum AssocItemLink<'a> {
1321 Anchor(Option<&'a str>),
1322 GotoSource(ItemId, &'a FxIndexSet<Symbol>),
1323}
1324
1325impl<'a> AssocItemLink<'a> {
1326 fn anchor(&self, id: &'a str) -> Self {
1327 match *self {
1328 AssocItemLink::Anchor(_) => AssocItemLink::Anchor(Some(id)),
1329 ref other => *other,
1330 }
1331 }
1332}
1333
1334fn write_section_heading(
1335 title: impl fmt::Display,
1336 id: &str,
1337 extra_class: Option<&str>,
1338 extra: impl fmt::Display,
1339) -> impl fmt::Display {
1340 fmt::from_fn(move |w| {
1341 let (extra_class, whitespace) = match extra_class {
1342 Some(extra) => (extra, " "),
1343 None => ("", ""),
1344 };
1345 write!(
1346 w,
1347 "<h2 id=\"{id}\" class=\"{extra_class}{whitespace}section-header\">\
1348 {title}\
1349 <a href=\"#{id}\" class=\"anchor\">§</a>\
1350 </h2>{extra}",
1351 )
1352 })
1353}
1354
1355fn write_impl_section_heading(title: impl fmt::Display, id: &str) -> impl fmt::Display {
1356 write_section_heading(title, id, None, "")
1357}
1358
1359fn render_all_impls(
1360 mut w: impl Write,
1361 cx: &Context<'_>,
1362 containing_item: &clean::Item,
1363 concrete: &[&Impl],
1364 synthetic: &[&Impl],
1365 blanket_impl: &[&Impl],
1366) -> fmt::Result {
1367 let impls = {
1368 let mut buf = String::new();
1369 render_impls(cx, &mut buf, concrete, containing_item, true)?;
1370 buf
1371 };
1372 if !impls.is_empty() {
1373 write!(
1374 w,
1375 "{}<div id=\"trait-implementations-list\">{impls}</div>",
1376 write_impl_section_heading("Trait Implementations", "trait-implementations")
1377 )?;
1378 }
1379
1380 if !synthetic.is_empty() {
1381 write!(
1382 w,
1383 "{}<div id=\"synthetic-implementations-list\">",
1384 write_impl_section_heading("Auto Trait Implementations", "synthetic-implementations",)
1385 )?;
1386 render_impls(cx, &mut w, synthetic, containing_item, false)?;
1387 w.write_str("</div>")?;
1388 }
1389
1390 if !blanket_impl.is_empty() {
1391 write!(
1392 w,
1393 "{}<div id=\"blanket-implementations-list\">",
1394 write_impl_section_heading("Blanket Implementations", "blanket-implementations")
1395 )?;
1396 render_impls(cx, &mut w, blanket_impl, containing_item, false)?;
1397 w.write_str("</div>")?;
1398 }
1399 Ok(())
1400}
1401
1402fn render_assoc_items(
1403 cx: &Context<'_>,
1404 containing_item: &clean::Item,
1405 it: DefId,
1406 what: AssocItemRender<'_>,
1407) -> impl fmt::Display {
1408 fmt::from_fn(move |f| {
1409 let mut derefs = DefIdSet::default();
1410 derefs.insert(it);
1411 render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs)
1412 })
1413}
1414
1415fn render_assoc_items_inner(
1416 mut w: &mut dyn fmt::Write,
1417 cx: &Context<'_>,
1418 containing_item: &clean::Item,
1419 it: DefId,
1420 what: AssocItemRender<'_>,
1421 derefs: &mut DefIdSet,
1422) -> fmt::Result {
1423 info!("Documenting associated items of {:?}", containing_item.name);
1424 let cache = &cx.shared.cache;
1425 let Some(v) = cache.impls.get(&it) else { return Ok(()) };
1426 let (mut non_trait, traits): (Vec<_>, _) =
1427 v.iter().partition(|i| i.inner_impl().trait_.is_none());
1428 if !non_trait.is_empty() {
1429 let render_mode = what.render_mode();
1430 let class_html = what
1431 .class()
1432 .map(|class| fmt::from_fn(move |f| write!(f, r#" class="{class}""#)))
1433 .maybe_display();
1434 let (section_heading, id) = match what {
1435 AssocItemRender::All => (
1436 Either::Left(write_impl_section_heading("Implementations", "implementations")),
1437 Cow::Borrowed("implementations-list"),
1438 ),
1439 AssocItemRender::DerefFor { trait_, type_, .. } => {
1440 let id = cx.derive_id(small_url_encode(format!(
1441 "deref-methods-{:#}",
1442 print_type(type_, cx)
1443 )));
1444 non_trait.retain(|impl_| {
1452 type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache)
1453 });
1454 let derived_id = cx.derive_id(&id);
1455 if let Some(def_id) = type_.def_id(cx.cache()) {
1456 cx.deref_id_map.borrow_mut().insert(def_id, id.clone());
1457 }
1458 (
1459 Either::Right(fmt::from_fn(move |f| {
1460 write!(
1461 f,
1462 "<details class=\"toggle big-toggle\" open><summary>{}</summary>",
1463 write_impl_section_heading(
1464 fmt::from_fn(|f| write!(
1465 f,
1466 "<span>Methods from {trait_}<Target = {type_}></span>",
1467 trait_ = print_path(trait_, cx),
1468 type_ = print_type(type_, cx),
1469 )),
1470 &id,
1471 )
1472 )
1473 })),
1474 Cow::Owned(derived_id),
1475 )
1476 }
1477 };
1478 let impls_buf = fmt::from_fn(|f| {
1479 non_trait
1480 .iter()
1481 .map(|i| {
1482 render_impl(
1483 cx,
1484 i,
1485 containing_item,
1486 AssocItemLink::Anchor(None),
1487 render_mode,
1488 None,
1489 &[],
1490 ImplRenderingParameters {
1491 show_def_docs: true,
1492 show_default_items: true,
1493 show_non_assoc_items: true,
1494 toggle_open_by_default: true,
1495 },
1496 )
1497 })
1498 .joined("", f)
1499 })
1500 .to_string();
1501
1502 if !impls_buf.is_empty() {
1503 write!(
1504 w,
1505 "{section_heading}<div id=\"{id}\"{class_html}>{impls_buf}</div>{}",
1506 matches!(what, AssocItemRender::DerefFor { .. })
1507 .then_some("</details>")
1508 .maybe_display(),
1509 )?;
1510 }
1511 }
1512
1513 if !traits.is_empty() {
1514 let deref_impl =
1515 traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait());
1516 if let Some(impl_) = deref_impl {
1517 let has_deref_mut =
1518 traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
1519 render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs)?;
1520 }
1521
1522 if let AssocItemRender::DerefFor { .. } = what {
1525 return Ok(());
1526 }
1527
1528 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
1529 traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
1530 let (blanket_impl, concrete): (Vec<&Impl>, _) =
1531 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
1532
1533 render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl)?;
1534 }
1535 Ok(())
1536}
1537
1538fn render_deref_methods(
1540 mut w: impl Write,
1541 cx: &Context<'_>,
1542 impl_: &Impl,
1543 container_item: &clean::Item,
1544 deref_mut: bool,
1545 derefs: &mut DefIdSet,
1546) -> fmt::Result {
1547 let cache = cx.cache();
1548 let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
1549 let (target, real_target) = impl_
1550 .inner_impl()
1551 .items
1552 .iter()
1553 .find_map(|item| match item.kind {
1554 clean::AssocTypeItem(box ref t, _) => Some(match *t {
1555 clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
1556 _ => (&t.type_, &t.type_),
1557 }),
1558 _ => None,
1559 })
1560 .expect("Expected associated type binding");
1561 debug!(
1562 "Render deref methods for {for_:#?}, target {target:#?}",
1563 for_ = impl_.inner_impl().for_
1564 );
1565 let what =
1566 AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
1567 if let Some(did) = target.def_id(cache) {
1568 if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) {
1569 if did == type_did || !derefs.insert(did) {
1571 return Ok(());
1573 }
1574 }
1575 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1576 } else if let Some(prim) = target.primitive_type()
1577 && let Some(&did) = cache.primitive_locations.get(&prim)
1578 {
1579 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1580 }
1581 Ok(())
1582}
1583
1584fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool {
1585 let self_type_opt = match item.kind {
1586 clean::MethodItem(ref method, _) => method.decl.receiver_type(),
1587 clean::RequiredMethodItem(ref method) => method.decl.receiver_type(),
1588 _ => None,
1589 };
1590
1591 if let Some(self_ty) = self_type_opt {
1592 let (by_mut_ref, by_box, by_value) = match *self_ty {
1593 clean::Type::BorrowedRef { mutability, .. } => {
1594 (mutability == Mutability::Mut, false, false)
1595 }
1596 clean::Type::Path { ref path } => {
1597 (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false)
1598 }
1599 clean::Type::SelfTy => (false, false, true),
1600 _ => (false, false, false),
1601 };
1602
1603 (deref_mut_ || !by_mut_ref) && !by_box && !by_value
1604 } else {
1605 false
1606 }
1607}
1608
1609fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option<impl fmt::Display> {
1610 if ty.is_unit() {
1611 return None;
1613 }
1614
1615 let did = ty.def_id(cx.cache())?;
1616
1617 if Some(did) == cx.tcx().lang_items().owned_box()
1622 || Some(did) == cx.tcx().lang_items().pin_type()
1623 {
1624 return None;
1625 }
1626
1627 let impls = cx.cache().impls.get(&did)?;
1628 let has_notable_trait = impls
1629 .iter()
1630 .map(Impl::inner_impl)
1631 .filter(|impl_| {
1632 impl_.polarity == ty::ImplPolarity::Positive
1633 && ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1636 })
1637 .filter_map(|impl_| impl_.trait_.as_ref())
1638 .filter_map(|trait_| cx.cache().traits.get(&trait_.def_id()))
1639 .any(|t| t.is_notable_trait(cx.tcx()));
1640
1641 has_notable_trait.then(|| {
1642 cx.types_with_notable_traits.borrow_mut().insert(ty.clone());
1643 fmt::from_fn(|f| {
1644 write!(
1645 f,
1646 " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>",
1647 ty = Escape(&format!("{:#}", print_type(ty, cx))),
1648 )
1649 })
1650 })
1651}
1652
1653fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
1654 let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
1655
1656 let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");
1657
1658 let out = fmt::from_fn(|f| {
1659 let mut notable_impls = impls
1660 .iter()
1661 .map(|impl_| impl_.inner_impl())
1662 .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive)
1663 .filter(|impl_| {
1664 ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1666 })
1667 .filter_map(|impl_| {
1668 if let Some(trait_) = &impl_.trait_
1669 && let trait_did = trait_.def_id()
1670 && let Some(trait_) = cx.cache().traits.get(&trait_did)
1671 && trait_.is_notable_trait(cx.tcx())
1672 {
1673 Some((impl_, trait_did))
1674 } else {
1675 None
1676 }
1677 })
1678 .peekable();
1679
1680 let has_notable_impl = if let Some((impl_, _)) = notable_impls.peek() {
1681 write!(
1682 f,
1683 "<h3>Notable traits for <code>{}</code></h3>\
1684 <pre><code>",
1685 print_type(&impl_.for_, cx),
1686 )?;
1687 true
1688 } else {
1689 false
1690 };
1691
1692 for (impl_, trait_did) in notable_impls {
1693 write!(f, "<div class=\"where\">{}</div>", print_impl(impl_, false, cx))?;
1694 for it in &impl_.items {
1695 let clean::AssocTypeItem(tydef, ..) = &it.kind else {
1696 continue;
1697 };
1698
1699 let empty_set = FxIndexSet::default();
1700 let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
1701
1702 write!(
1703 f,
1704 "<div class=\"where\"> {};</div>",
1705 assoc_type(
1706 it,
1707 &tydef.generics,
1708 &[], Some(&tydef.type_),
1710 src_link,
1711 0,
1712 cx,
1713 )
1714 )?;
1715 }
1716 }
1717
1718 if !has_notable_impl {
1719 f.write_str("</code></pre>")?;
1720 }
1721
1722 Ok(())
1723 })
1724 .to_string();
1725
1726 (format!("{:#}", print_type(ty, cx)), out)
1727}
1728
1729fn notable_traits_json<'a>(tys: impl Iterator<Item = &'a clean::Type>, cx: &Context<'_>) -> String {
1730 let mut mp = tys.map(|ty| notable_traits_decl(ty, cx)).collect::<IndexMap<_, _>>();
1731 mp.sort_unstable_keys();
1732 serde_json::to_string(&mp).expect("serialize (string, string) -> json object cannot fail")
1733}
1734
1735#[derive(Clone, Copy, Debug)]
1736struct ImplRenderingParameters {
1737 show_def_docs: bool,
1738 show_default_items: bool,
1739 show_non_assoc_items: bool,
1741 toggle_open_by_default: bool,
1742}
1743
1744fn render_impl(
1745 cx: &Context<'_>,
1746 i: &Impl,
1747 parent: &clean::Item,
1748 link: AssocItemLink<'_>,
1749 render_mode: RenderMode,
1750 use_absolute: Option<bool>,
1751 aliases: &[String],
1752 rendering_params: ImplRenderingParameters,
1753) -> impl fmt::Display {
1754 fmt::from_fn(move |w| {
1755 let cache = &cx.shared.cache;
1756 let traits = &cache.traits;
1757 let trait_ = i.trait_did().map(|did| &traits[&did]);
1758 let mut close_tags = <Vec<&str>>::with_capacity(2);
1759
1760 fn doc_impl_item(
1766 boring: impl fmt::Write,
1767 interesting: impl fmt::Write,
1768 cx: &Context<'_>,
1769 item: &clean::Item,
1770 parent: &clean::Item,
1771 link: AssocItemLink<'_>,
1772 render_mode: RenderMode,
1773 is_default_item: bool,
1774 trait_: Option<&clean::Trait>,
1775 rendering_params: ImplRenderingParameters,
1776 ) -> fmt::Result {
1777 let item_type = item.type_();
1778 let name = item.name.as_ref().unwrap();
1779
1780 let render_method_item = rendering_params.show_non_assoc_items
1781 && match render_mode {
1782 RenderMode::Normal => true,
1783 RenderMode::ForDeref { mut_: deref_mut_ } => {
1784 should_render_item(item, deref_mut_, cx.tcx())
1785 }
1786 };
1787
1788 let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
1789
1790 let mut doc_buffer = String::new();
1791 let mut info_buffer = String::new();
1792 let mut short_documented = true;
1793
1794 if render_method_item {
1795 if !is_default_item {
1796 if let Some(t) = trait_ {
1797 if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1800 if !item.doc_value().is_empty() {
1803 document_item_info(cx, it, Some(parent))
1804 .render_into(&mut info_buffer)?;
1805 doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1806 short_documented = false;
1807 } else {
1808 doc_buffer = document_short(
1811 it,
1812 cx,
1813 link,
1814 parent,
1815 rendering_params.show_def_docs,
1816 )
1817 .to_string();
1818 }
1819 }
1820 } else {
1821 document_item_info(cx, item, Some(parent)).render_into(&mut info_buffer)?;
1822 if rendering_params.show_def_docs {
1823 doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1824 short_documented = false;
1825 }
1826 }
1827 } else {
1828 doc_buffer =
1829 document_short(item, cx, link, parent, rendering_params.show_def_docs)
1830 .to_string();
1831 }
1832 }
1833 let mut w = if short_documented && trait_.is_some() {
1834 Either::Left(interesting)
1835 } else {
1836 Either::Right(boring)
1837 };
1838
1839 let toggled = !doc_buffer.is_empty();
1840 if toggled {
1841 let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
1842 write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>")?;
1843 }
1844 match &item.kind {
1845 clean::MethodItem(..) | clean::RequiredMethodItem(_) => {
1846 if render_method_item {
1848 let id = cx.derive_id(format!("{item_type}.{name}"));
1849 let source_id = trait_
1850 .and_then(|trait_| {
1851 trait_
1852 .items
1853 .iter()
1854 .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
1855 })
1856 .map(|item| format!("{}.{name}", item.type_()));
1857 write!(
1858 w,
1859 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1860 {}",
1861 render_rightside(cx, item, render_mode)
1862 )?;
1863 if trait_.is_some() {
1864 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1866 }
1867 write!(
1868 w,
1869 "<h4 class=\"code-header\">{}</h4></section>",
1870 render_assoc_item(
1871 item,
1872 link.anchor(source_id.as_ref().unwrap_or(&id)),
1873 ItemType::Impl,
1874 cx,
1875 render_mode,
1876 ),
1877 )?;
1878 }
1879 }
1880 clean::RequiredAssocConstItem(generics, ty) => {
1881 let source_id = format!("{item_type}.{name}");
1882 let id = cx.derive_id(&source_id);
1883 write!(
1884 w,
1885 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1886 {}",
1887 render_rightside(cx, item, render_mode)
1888 )?;
1889 if trait_.is_some() {
1890 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1892 }
1893 write!(
1894 w,
1895 "<h4 class=\"code-header\">{}</h4></section>",
1896 assoc_const(
1897 item,
1898 generics,
1899 ty,
1900 AssocConstValue::None,
1901 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1902 0,
1903 cx,
1904 ),
1905 )?;
1906 }
1907 clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
1908 let source_id = format!("{item_type}.{name}");
1909 let id = cx.derive_id(&source_id);
1910 write!(
1911 w,
1912 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1913 {}",
1914 render_rightside(cx, item, render_mode),
1915 )?;
1916 if trait_.is_some() {
1917 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1919 }
1920 write!(
1921 w,
1922 "<h4 class=\"code-header\">{}</h4></section>",
1923 assoc_const(
1924 item,
1925 &ci.generics,
1926 &ci.type_,
1927 match item.kind {
1928 clean::ProvidedAssocConstItem(_) =>
1929 AssocConstValue::TraitDefault(&ci.kind),
1930 clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
1931 _ => unreachable!(),
1932 },
1933 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1934 0,
1935 cx,
1936 ),
1937 )?;
1938 }
1939 clean::RequiredAssocTypeItem(generics, bounds) => {
1940 let source_id = format!("{item_type}.{name}");
1941 let id = cx.derive_id(&source_id);
1942 write!(
1943 w,
1944 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1945 {}",
1946 render_rightside(cx, item, render_mode),
1947 )?;
1948 if trait_.is_some() {
1949 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1951 }
1952 write!(
1953 w,
1954 "<h4 class=\"code-header\">{}</h4></section>",
1955 assoc_type(
1956 item,
1957 generics,
1958 bounds,
1959 None,
1960 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1961 0,
1962 cx,
1963 ),
1964 )?;
1965 }
1966 clean::AssocTypeItem(tydef, _bounds) => {
1967 let source_id = format!("{item_type}.{name}");
1968 let id = cx.derive_id(&source_id);
1969 write!(
1970 w,
1971 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
1972 {}",
1973 render_rightside(cx, item, render_mode),
1974 )?;
1975 if trait_.is_some() {
1976 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1978 }
1979 write!(
1980 w,
1981 "<h4 class=\"code-header\">{}</h4></section>",
1982 assoc_type(
1983 item,
1984 &tydef.generics,
1985 &[], Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
1987 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1988 0,
1989 cx,
1990 ),
1991 )?;
1992 }
1993 clean::StrippedItem(..) => return Ok(()),
1994 _ => panic!("can't make docs for trait item with name {:?}", item.name),
1995 }
1996
1997 w.write_str(&info_buffer)?;
1998 if toggled {
1999 write!(w, "</summary>{doc_buffer}</details>")?;
2000 }
2001 Ok(())
2002 }
2003
2004 let mut impl_items = String::new();
2005 let mut default_impl_items = String::new();
2006 let impl_ = i.inner_impl();
2007
2008 let mut assoc_types = Vec::new();
2018 let mut methods = Vec::new();
2019
2020 if !impl_.is_negative_trait_impl() {
2021 for impl_item in &impl_.items {
2022 match impl_item.kind {
2023 clean::MethodItem(..) | clean::RequiredMethodItem(_) => methods.push(impl_item),
2024 clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => {
2025 assoc_types.push(impl_item)
2026 }
2027 clean::RequiredAssocConstItem(..)
2028 | clean::ProvidedAssocConstItem(_)
2029 | clean::ImplAssocConstItem(_) => {
2030 doc_impl_item(
2032 &mut default_impl_items,
2033 &mut impl_items,
2034 cx,
2035 impl_item,
2036 if trait_.is_some() { &i.impl_item } else { parent },
2037 link,
2038 render_mode,
2039 false,
2040 trait_,
2041 rendering_params,
2042 )?;
2043 }
2044 _ => {}
2045 }
2046 }
2047
2048 for assoc_type in assoc_types {
2049 doc_impl_item(
2050 &mut default_impl_items,
2051 &mut impl_items,
2052 cx,
2053 assoc_type,
2054 if trait_.is_some() { &i.impl_item } else { parent },
2055 link,
2056 render_mode,
2057 false,
2058 trait_,
2059 rendering_params,
2060 )?;
2061 }
2062 for method in methods {
2063 doc_impl_item(
2064 &mut default_impl_items,
2065 &mut impl_items,
2066 cx,
2067 method,
2068 if trait_.is_some() { &i.impl_item } else { parent },
2069 link,
2070 render_mode,
2071 false,
2072 trait_,
2073 rendering_params,
2074 )?;
2075 }
2076 }
2077
2078 fn render_default_items(
2079 mut boring: impl fmt::Write,
2080 mut interesting: impl fmt::Write,
2081 cx: &Context<'_>,
2082 t: &clean::Trait,
2083 i: &clean::Impl,
2084 parent: &clean::Item,
2085 render_mode: RenderMode,
2086 rendering_params: ImplRenderingParameters,
2087 ) -> fmt::Result {
2088 for trait_item in &t.items {
2089 if let Some(impl_def_id) = parent.item_id.as_def_id()
2092 && let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
2093 && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id))
2094 {
2095 continue;
2096 }
2097
2098 let n = trait_item.name;
2099 if i.items.iter().any(|m| m.name == n) {
2100 continue;
2101 }
2102 let did = i.trait_.as_ref().unwrap().def_id();
2103 let provided_methods = i.provided_trait_methods(cx.tcx());
2104 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
2105
2106 doc_impl_item(
2107 &mut boring,
2108 &mut interesting,
2109 cx,
2110 trait_item,
2111 parent,
2112 assoc_link,
2113 render_mode,
2114 true,
2115 Some(t),
2116 rendering_params,
2117 )?;
2118 }
2119 Ok(())
2120 }
2121
2122 if rendering_params.show_default_items
2127 && let Some(t) = trait_
2128 && !impl_.is_negative_trait_impl()
2129 {
2130 render_default_items(
2131 &mut default_impl_items,
2132 &mut impl_items,
2133 cx,
2134 t,
2135 impl_,
2136 &i.impl_item,
2137 render_mode,
2138 rendering_params,
2139 )?;
2140 }
2141 if render_mode == RenderMode::Normal {
2142 let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
2143 if toggled {
2144 close_tags.push("</details>");
2145 write!(
2146 w,
2147 "<details class=\"toggle implementors-toggle\"{}>\
2148 <summary>",
2149 if rendering_params.toggle_open_by_default { " open" } else { "" }
2150 )?;
2151 }
2152
2153 let (before_dox, after_dox) = i
2154 .impl_item
2155 .opt_doc_value()
2156 .map(|dox| {
2157 Markdown {
2158 content: &dox,
2159 links: &i.impl_item.links(cx),
2160 ids: &mut cx.id_map.borrow_mut(),
2161 error_codes: cx.shared.codes,
2162 edition: cx.shared.edition(),
2163 playground: &cx.shared.playground,
2164 heading_offset: HeadingOffset::H4,
2165 }
2166 .split_summary_and_content()
2167 })
2168 .unwrap_or((None, None));
2169
2170 write!(
2171 w,
2172 "{}",
2173 render_impl_summary(
2174 cx,
2175 i,
2176 parent,
2177 rendering_params.show_def_docs,
2178 use_absolute,
2179 aliases,
2180 before_dox.as_deref(),
2181 trait_.is_none() && impl_.items.is_empty(),
2182 )
2183 )?;
2184 if toggled {
2185 w.write_str("</summary>")?;
2186 }
2187
2188 if before_dox.is_some()
2189 && let Some(after_dox) = after_dox
2190 {
2191 write!(w, "<div class=\"docblock\">{after_dox}</div>")?;
2192 }
2193
2194 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2195 w.write_str("<div class=\"impl-items\">")?;
2196 close_tags.push("</div>");
2197 }
2198 }
2199 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2200 w.write_str(&default_impl_items)?;
2201 w.write_str(&impl_items)?;
2202 }
2203 for tag in close_tags.into_iter().rev() {
2204 w.write_str(tag)?;
2205 }
2206 Ok(())
2207 })
2208}
2209
2210fn render_rightside(
2213 cx: &Context<'_>,
2214 item: &clean::Item,
2215 render_mode: RenderMode,
2216) -> impl fmt::Display {
2217 let tcx = cx.tcx();
2218
2219 fmt::from_fn(move |w| {
2220 let const_stability = match render_mode {
2223 RenderMode::Normal => item.const_stability(tcx),
2224 RenderMode::ForDeref { .. } => None,
2225 };
2226 let src_href = cx.src_href(item);
2227 let stability = render_stability_since_raw_with_extra(
2228 item.stable_since(tcx),
2229 const_stability,
2230 if src_href.is_some() { "" } else { " rightside" },
2231 );
2232
2233 match (stability, src_href) {
2234 (Some(stability), Some(link)) => {
2235 write!(
2236 w,
2237 "<span class=\"rightside\">{stability} · <a class=\"src\" href=\"{link}\">Source</a></span>",
2238 )
2239 }
2240 (Some(stability), None) => {
2241 write!(w, "{stability}")
2242 }
2243 (None, Some(link)) => {
2244 write!(w, "<a class=\"src rightside\" href=\"{link}\">Source</a>")
2245 }
2246 (None, None) => Ok(()),
2247 }
2248 })
2249}
2250
2251fn render_impl_summary(
2252 cx: &Context<'_>,
2253 i: &Impl,
2254 parent: &clean::Item,
2255 show_def_docs: bool,
2256 use_absolute: Option<bool>,
2257 aliases: &[String],
2260 doc: Option<&str>,
2261 impl_is_empty: bool,
2262) -> impl fmt::Display {
2263 fmt::from_fn(move |w| {
2264 let inner_impl = i.inner_impl();
2265 let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
2266 let aliases = (!aliases.is_empty())
2267 .then_some(fmt::from_fn(|f| {
2268 write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f)))
2269 }))
2270 .maybe_display();
2271 write!(
2272 w,
2273 "<section id=\"{id}\" class=\"impl\"{aliases}>\
2274 {}\
2275 <a href=\"#{id}\" class=\"anchor\">§</a>\
2276 <h3 class=\"code-header\">",
2277 render_rightside(cx, &i.impl_item, RenderMode::Normal)
2278 )?;
2279
2280 if let Some(use_absolute) = use_absolute {
2281 write!(w, "{}", print_impl(inner_impl, use_absolute, cx))?;
2282 if show_def_docs {
2283 for it in &inner_impl.items {
2284 if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
2285 write!(
2286 w,
2287 "<div class=\"where\"> {};</div>",
2288 assoc_type(
2289 it,
2290 &tydef.generics,
2291 &[], Some(&tydef.type_),
2293 AssocItemLink::Anchor(None),
2294 0,
2295 cx,
2296 )
2297 )?;
2298 }
2299 }
2300 }
2301 } else {
2302 write!(w, "{}", print_impl(inner_impl, false, cx))?;
2303 }
2304 w.write_str("</h3>")?;
2305
2306 let is_trait = inner_impl.trait_.is_some();
2307 if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) {
2308 write!(
2309 w,
2310 "<span class=\"item-info\">\
2311 <div class=\"stab portability\">{portability}</div>\
2312 </span>",
2313 )?;
2314 }
2315
2316 if let Some(doc) = doc {
2317 if impl_is_empty {
2318 w.write_str(
2319 "<div class=\"item-info\">\
2320 <div class=\"stab empty-impl\">This impl block contains no items.</div>\
2321 </div>",
2322 )?;
2323 }
2324 write!(w, "<div class=\"docblock\">{doc}</div>")?;
2325 }
2326
2327 w.write_str("</section>")
2328 })
2329}
2330
2331pub(crate) fn small_url_encode(s: String) -> String {
2332 fn dont_escape(c: u8) -> bool {
2337 c.is_ascii_alphanumeric()
2338 || c == b'-'
2339 || c == b'_'
2340 || c == b'.'
2341 || c == b','
2342 || c == b'~'
2343 || c == b'!'
2344 || c == b'\''
2345 || c == b'('
2346 || c == b')'
2347 || c == b'*'
2348 || c == b'/'
2349 || c == b';'
2350 || c == b':'
2351 || c == b'?'
2352 || c == b'='
2356 }
2357 let mut st = String::new();
2358 let mut last_match = 0;
2359 for (idx, b) in s.bytes().enumerate() {
2360 if dont_escape(b) {
2361 continue;
2362 }
2363
2364 if last_match != idx {
2365 st += &s[last_match..idx];
2367 }
2368 if b == b' ' {
2369 st += "+";
2373 } else {
2374 write!(st, "%{b:02X}").unwrap();
2375 }
2376 last_match = idx + 1;
2382 }
2383
2384 if last_match != 0 {
2385 st += &s[last_match..];
2386 st
2387 } else {
2388 s
2389 }
2390}
2391
2392fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String {
2393 use rustc_middle::ty::print::with_forced_trimmed_paths;
2394 let (type_, trait_) = match impl_id {
2395 ItemId::Auto { trait_, for_ } => {
2396 let ty = tcx.type_of(for_).skip_binder();
2397 (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
2398 }
2399 ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
2400 if let Some(trait_ref) = tcx.impl_opt_trait_ref(impl_id) {
2401 let trait_ref = trait_ref.skip_binder();
2402 (trait_ref.self_ty(), Some(trait_ref))
2403 } else {
2404 (tcx.type_of(impl_id).skip_binder(), None)
2405 }
2406 }
2407 };
2408 with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
2409 format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
2410 } else {
2411 format!("impl-{type_}")
2412 }))
2413}
2414
2415fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2416 match item.kind {
2417 clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
2418 Some((
2421 format!("{:#}", print_type(&i.for_, cx)),
2422 get_id_for_impl(cx.tcx(), item.item_id),
2423 ))
2424 }
2425 _ => None,
2426 }
2427}
2428
2429pub(crate) fn get_filtered_impls_for_reference<'a>(
2433 shared: &'a SharedContext<'_>,
2434 it: &clean::Item,
2435) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
2436 let def_id = it.item_id.expect_def_id();
2437 let Some(v) = shared.cache.impls.get(&def_id) else {
2439 return (Vec::new(), Vec::new(), Vec::new());
2440 };
2441 let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
2444 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
2445 traits.partition(|t| t.inner_impl().kind.is_auto());
2446
2447 let (blanket_impl, concrete): (Vec<&Impl>, _) =
2448 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
2449 let concrete: Vec<_> = concrete
2451 .into_iter()
2452 .filter(|t| match t.inner_impl().for_ {
2453 clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
2454 _ => false,
2455 })
2456 .collect();
2457
2458 (concrete, synthetic, blanket_impl)
2459}
2460
2461#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2462pub(crate) enum ItemSection {
2463 Reexports,
2464 PrimitiveTypes,
2465 Modules,
2466 Macros,
2467 Structs,
2468 Enums,
2469 Constants,
2470 Statics,
2471 Traits,
2472 Functions,
2473 TypeAliases,
2474 Unions,
2475 Implementations,
2476 TypeMethods,
2477 Methods,
2478 StructFields,
2479 Variants,
2480 AssociatedTypes,
2481 AssociatedConstants,
2482 ForeignTypes,
2483 Keywords,
2484 Attributes,
2485 AttributeMacros,
2486 DeriveMacros,
2487 TraitAliases,
2488}
2489
2490impl ItemSection {
2491 const ALL: &'static [Self] = {
2492 use ItemSection::*;
2493 &[
2496 Reexports,
2497 PrimitiveTypes,
2498 Modules,
2499 Macros,
2500 Structs,
2501 Enums,
2502 Constants,
2503 Statics,
2504 Traits,
2505 Functions,
2506 TypeAliases,
2507 Unions,
2508 Implementations,
2509 TypeMethods,
2510 Methods,
2511 StructFields,
2512 Variants,
2513 AssociatedTypes,
2514 AssociatedConstants,
2515 ForeignTypes,
2516 Keywords,
2517 Attributes,
2518 AttributeMacros,
2519 DeriveMacros,
2520 TraitAliases,
2521 ]
2522 };
2523
2524 fn id(self) -> &'static str {
2525 match self {
2526 Self::Reexports => "reexports",
2527 Self::Modules => "modules",
2528 Self::Structs => "structs",
2529 Self::Unions => "unions",
2530 Self::Enums => "enums",
2531 Self::Functions => "functions",
2532 Self::TypeAliases => "types",
2533 Self::Statics => "statics",
2534 Self::Constants => "constants",
2535 Self::Traits => "traits",
2536 Self::Implementations => "impls",
2537 Self::TypeMethods => "tymethods",
2538 Self::Methods => "methods",
2539 Self::StructFields => "fields",
2540 Self::Variants => "variants",
2541 Self::Macros => "macros",
2542 Self::PrimitiveTypes => "primitives",
2543 Self::AssociatedTypes => "associated-types",
2544 Self::AssociatedConstants => "associated-consts",
2545 Self::ForeignTypes => "foreign-types",
2546 Self::Keywords => "keywords",
2547 Self::Attributes => "attributes",
2548 Self::AttributeMacros => "attributes",
2549 Self::DeriveMacros => "derives",
2550 Self::TraitAliases => "trait-aliases",
2551 }
2552 }
2553
2554 fn name(self) -> &'static str {
2555 match self {
2556 Self::Reexports => "Re-exports",
2557 Self::Modules => "Modules",
2558 Self::Structs => "Structs",
2559 Self::Unions => "Unions",
2560 Self::Enums => "Enums",
2561 Self::Functions => "Functions",
2562 Self::TypeAliases => "Type Aliases",
2563 Self::Statics => "Statics",
2564 Self::Constants => "Constants",
2565 Self::Traits => "Traits",
2566 Self::Implementations => "Implementations",
2567 Self::TypeMethods => "Type Methods",
2568 Self::Methods => "Methods",
2569 Self::StructFields => "Struct Fields",
2570 Self::Variants => "Variants",
2571 Self::Macros => "Macros",
2572 Self::PrimitiveTypes => "Primitive Types",
2573 Self::AssociatedTypes => "Associated Types",
2574 Self::AssociatedConstants => "Associated Constants",
2575 Self::ForeignTypes => "Foreign Types",
2576 Self::Keywords => "Keywords",
2577 Self::Attributes => "Attributes",
2578 Self::AttributeMacros => "Attribute Macros",
2579 Self::DeriveMacros => "Derive Macros",
2580 Self::TraitAliases => "Trait Aliases",
2581 }
2582 }
2583}
2584
2585fn item_ty_to_section(ty: ItemType) -> ItemSection {
2586 match ty {
2587 ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
2588 ItemType::Module => ItemSection::Modules,
2589 ItemType::Struct => ItemSection::Structs,
2590 ItemType::Union => ItemSection::Unions,
2591 ItemType::Enum => ItemSection::Enums,
2592 ItemType::Function => ItemSection::Functions,
2593 ItemType::TypeAlias => ItemSection::TypeAliases,
2594 ItemType::Static => ItemSection::Statics,
2595 ItemType::Constant => ItemSection::Constants,
2596 ItemType::Trait => ItemSection::Traits,
2597 ItemType::Impl => ItemSection::Implementations,
2598 ItemType::TyMethod => ItemSection::TypeMethods,
2599 ItemType::Method => ItemSection::Methods,
2600 ItemType::StructField => ItemSection::StructFields,
2601 ItemType::Variant => ItemSection::Variants,
2602 ItemType::Macro => ItemSection::Macros,
2603 ItemType::Primitive => ItemSection::PrimitiveTypes,
2604 ItemType::AssocType => ItemSection::AssociatedTypes,
2605 ItemType::AssocConst => ItemSection::AssociatedConstants,
2606 ItemType::ForeignType => ItemSection::ForeignTypes,
2607 ItemType::Keyword => ItemSection::Keywords,
2608 ItemType::Attribute => ItemSection::Attributes,
2609 ItemType::ProcAttribute => ItemSection::AttributeMacros,
2610 ItemType::ProcDerive => ItemSection::DeriveMacros,
2611 ItemType::TraitAlias => ItemSection::TraitAliases,
2612 }
2613}
2614
2615fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec<String> {
2622 let mut out = Vec::new();
2623 let mut visited = FxHashSet::default();
2624 let mut work = VecDeque::new();
2625
2626 let mut process_path = |did: DefId| {
2627 let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
2628 let fqp = cache.exact_paths.get(&did).or_else(get_extern);
2629
2630 if let Some(path) = fqp {
2631 out.push(join_path_syms(path));
2632 }
2633 };
2634
2635 work.push_back(first_ty);
2636
2637 while let Some(ty) = work.pop_front() {
2638 if !visited.insert(ty) {
2639 continue;
2640 }
2641
2642 match ty {
2643 clean::Type::Path { path } => process_path(path.def_id()),
2644 clean::Type::Tuple(tys) => {
2645 work.extend(tys.iter());
2646 }
2647 clean::Type::Slice(ty) => {
2648 work.push_back(ty);
2649 }
2650 clean::Type::Array(ty, _) => {
2651 work.push_back(ty);
2652 }
2653 clean::Type::RawPointer(_, ty) => {
2654 work.push_back(ty);
2655 }
2656 clean::Type::BorrowedRef { type_, .. } => {
2657 work.push_back(type_);
2658 }
2659 clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
2660 work.push_back(self_type);
2661 if let Some(trait_) = trait_ {
2662 process_path(trait_.def_id());
2663 }
2664 }
2665 _ => {}
2666 }
2667 }
2668 out
2669}
2670
2671const MAX_FULL_EXAMPLES: usize = 5;
2672const NUM_VISIBLE_LINES: usize = 10;
2673
2674fn render_call_locations<W: fmt::Write>(
2676 mut w: W,
2677 cx: &Context<'_>,
2678 item: &clean::Item,
2679) -> fmt::Result {
2680 let tcx = cx.tcx();
2681 let def_id = item.item_id.expect_def_id();
2682 let key = tcx.def_path_hash(def_id);
2683 let Some(call_locations) = cx.shared.call_locations.get(&key) else { return Ok(()) };
2684
2685 let id = cx.derive_id("scraped-examples");
2687 write!(
2688 &mut w,
2689 "<div class=\"docblock scraped-example-list\">\
2690 <span></span>\
2691 <h5 id=\"{id}\">\
2692 <a href=\"#{id}\">Examples found in repository</a>\
2693 <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
2694 </h5>",
2695 root_path = cx.root_path(),
2696 id = id
2697 )?;
2698
2699 let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
2701 let (line_lo, line_hi) = loc.call_expr.line_span;
2702 let (anchor, title) = if line_lo == line_hi {
2703 ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
2704 } else {
2705 (
2706 format!("{}-{}", line_lo + 1, line_hi + 1),
2707 format!("lines {}-{}", line_lo + 1, line_hi + 1),
2708 )
2709 };
2710 let url = format!("{}{}#{anchor}", cx.root_path(), call_data.url);
2711 (url, title)
2712 };
2713
2714 let write_example = |w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
2716 let contents = match fs::read_to_string(path) {
2717 Ok(contents) => contents,
2718 Err(err) => {
2719 let span = item.span(tcx).map_or(DUMMY_SP, |span| span.inner());
2720 tcx.dcx().span_err(span, format!("failed to read file {}: {err}", path.display()));
2721 return false;
2722 }
2723 };
2724
2725 assert!(!call_data.locations.is_empty());
2728 let min_loc =
2729 call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2730 let byte_min = min_loc.enclosing_item.byte_span.0;
2731 let line_min = min_loc.enclosing_item.line_span.0;
2732 let max_loc =
2733 call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
2734 let byte_max = max_loc.enclosing_item.byte_span.1;
2735 let line_max = max_loc.enclosing_item.line_span.1;
2736
2737 let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
2739
2740 let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
2743 .locations
2744 .iter()
2745 .map(|loc| {
2746 let (byte_lo, byte_hi) = loc.call_ident.byte_span;
2747 let (line_lo, line_hi) = loc.call_expr.line_span;
2748 let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
2749
2750 let line_range = (line_lo - line_min, line_hi - line_min);
2751 let (line_url, line_title) = link_to_loc(call_data, loc);
2752
2753 (byte_range, (line_range, line_url, line_title))
2754 })
2755 .unzip();
2756
2757 let (_, init_url, init_title) = &line_ranges[0];
2758 let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
2759 let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
2760
2761 let source_map = tcx.sess.source_map();
2762 let files = source_map.files();
2763 let local = tcx.sess.local_crate_source_file().unwrap();
2764
2765 let get_file_start_pos = || {
2766 let crate_src = local.clone().into_local_path()?;
2767 let abs_crate_src = crate_src.canonicalize().ok()?;
2768 let crate_root = abs_crate_src.parent()?.parent()?;
2769 let rel_path = path.strip_prefix(crate_root).ok()?;
2770 files
2771 .iter()
2772 .find(|file| match &file.name {
2773 FileName::Real(real) => real.local_path().map_or(false, |p| p == rel_path),
2774 _ => false,
2775 })
2776 .map(|file| file.start_pos)
2777 };
2778
2779 let Some(file_span) = get_file_start_pos()
2782 .or_else(|| {
2783 files
2784 .iter()
2785 .find(|file| match &file.name {
2786 FileName::Real(file_name) => file_name == &local,
2787 _ => false,
2788 })
2789 .map(|file| file.start_pos)
2790 })
2791 .map(|start_pos| {
2792 rustc_span::Span::with_root_ctxt(
2793 start_pos + BytePos(byte_min),
2794 start_pos + BytePos(byte_max),
2795 )
2796 })
2797 else {
2798 return false;
2800 };
2801
2802 let mut decoration_info = FxIndexMap::default();
2803 decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
2804 decoration_info.insert("highlight", byte_ranges);
2805
2806 sources::print_src(
2807 w,
2808 contents_subset,
2809 file_span,
2810 cx,
2811 &cx.root_path(),
2812 &highlight::DecorationInfo(decoration_info),
2813 &sources::SourceContext::Embedded(sources::ScrapedInfo {
2814 needs_expansion,
2815 offset: line_min,
2816 name: &call_data.display_name,
2817 url: init_url,
2818 title: init_title,
2819 locations: locations_encoded,
2820 }),
2821 )
2822 .unwrap();
2823
2824 true
2825 };
2826
2827 let ordered_locations = {
2839 fn sort_criterion<'a>(
2840 (_, call_data): &(&PathBuf, &'a CallData),
2841 ) -> (bool, u32, &'a String) {
2842 let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2844 (!call_data.is_bin, hi - lo, &call_data.display_name)
2845 }
2846
2847 let mut locs = call_locations.iter().collect::<Vec<_>>();
2848 locs.sort_by_key(sort_criterion);
2849 locs
2850 };
2851
2852 let mut it = ordered_locations.into_iter().peekable();
2853
2854 let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
2857 for example in it.by_ref() {
2858 if write_example(&mut *w, example) {
2859 break;
2860 }
2861 }
2862 };
2863
2864 write_and_skip_failure(&mut w, &mut it);
2866
2867 if it.peek().is_some() {
2869 write!(
2870 w,
2871 "<details class=\"toggle more-examples-toggle\">\
2872 <summary class=\"hideme\">\
2873 <span>More examples</span>\
2874 </summary>\
2875 <div class=\"hide-more\">Hide additional examples</div>\
2876 <div class=\"more-scraped-examples\">\
2877 <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
2878 )?;
2879
2880 for _ in 0..MAX_FULL_EXAMPLES {
2883 write_and_skip_failure(&mut w, &mut it);
2884 }
2885
2886 if it.peek().is_some() {
2888 w.write_str(
2889 r#"<div class="example-links">Additional examples can be found in:<br><ul>"#,
2890 )?;
2891 it.try_for_each(|(_, call_data)| {
2892 let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
2893 write!(
2894 w,
2895 r#"<li><a href="{url}">{name}</a></li>"#,
2896 url = url,
2897 name = call_data.display_name
2898 )
2899 })?;
2900 w.write_str("</ul></div>")?;
2901 }
2902
2903 w.write_str("</div></details>")?;
2904 }
2905
2906 w.write_str("</div>")
2907}
2908
2909fn render_attributes_in_code(
2910 w: &mut impl fmt::Write,
2911 item: &clean::Item,
2912 prefix: &str,
2913 cx: &Context<'_>,
2914) -> fmt::Result {
2915 for attr in &item.attrs.other_attrs {
2916 let hir::Attribute::Parsed(kind) = attr else { continue };
2917 let attr = match kind {
2918 AttributeKind::LinkSection { name, .. } => {
2919 Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}"))))
2920 }
2921 AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"),
2922 AttributeKind::ExportName { name, .. } => {
2923 Cow::Owned(format!("#[unsafe(export_name = {})]", Escape(&format!("{name:?}"))))
2924 }
2925 AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"),
2926 _ => continue,
2927 };
2928 render_code_attribute(prefix, attr.as_ref(), w)?;
2929 }
2930
2931 if let Some(def_id) = item.def_id()
2932 && let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id)
2933 {
2934 render_code_attribute(prefix, &repr, w)?;
2935 }
2936 Ok(())
2937}
2938
2939fn render_repr_attribute_in_code(
2940 w: &mut impl fmt::Write,
2941 cx: &Context<'_>,
2942 def_id: DefId,
2943) -> fmt::Result {
2944 if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) {
2945 render_code_attribute("", &repr, w)?;
2946 }
2947 Ok(())
2948}
2949
2950fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) -> fmt::Result {
2951 write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>")
2952}
2953
2954fn repr_attribute<'tcx>(
2959 tcx: TyCtxt<'tcx>,
2960 cache: &Cache,
2961 def_id: DefId,
2962) -> Option<Cow<'static, str>> {
2963 let adt = match tcx.def_kind(def_id) {
2964 DefKind::Struct | DefKind::Enum | DefKind::Union => tcx.adt_def(def_id),
2965 _ => return None,
2966 };
2967 let repr = adt.repr();
2968
2969 let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id);
2970 let is_public_field = |field: &ty::FieldDef| {
2971 (cache.document_private || field.vis.is_public()) && is_visible(field.did)
2972 };
2973
2974 if repr.transparent() {
2975 let is_public = 'is_public: {
2978 let var = adt.variant(rustc_abi::FIRST_VARIANT); if !is_visible(var.def_id) {
2982 break 'is_public false;
2983 }
2984
2985 let non_1zst_field = var.fields.iter().find(|field| {
2987 let ty = ty::TypingEnv::post_analysis(tcx, field.did)
2988 .as_query_input(tcx.type_of(field.did).instantiate_identity());
2989 tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst())
2990 });
2991
2992 match non_1zst_field {
2993 Some(field) => is_public_field(field),
2994 None => var.fields.is_empty() || var.fields.iter().any(is_public_field),
2995 }
2996 };
2997
2998 return is_public.then(|| "#[repr(transparent)]".into());
3001 }
3002
3003 if !repr.c()
3007 && !repr.simd()
3008 && repr.int.is_none()
3009 && repr.pack.is_none()
3010 && repr.align.is_none()
3011 {
3012 return None;
3013 }
3014
3015 let is_public = adt
3017 .variants()
3018 .iter()
3019 .all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field));
3020 if !is_public {
3021 return None;
3022 }
3023
3024 let mut result = Vec::<Cow<'_, _>>::new();
3025
3026 if repr.c() {
3027 result.push("C".into());
3028 }
3029 if repr.simd() {
3030 result.push("simd".into());
3031 }
3032 if let Some(int) = repr.int {
3033 let prefix = if int.is_signed() { 'i' } else { 'u' };
3034 let int = match int {
3035 rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"),
3036 rustc_abi::IntegerType::Fixed(int, _) => {
3037 format!("{prefix}{}", int.size().bytes() * 8)
3038 }
3039 };
3040 result.push(int.into());
3041 }
3042
3043 if let Some(pack) = repr.pack {
3045 result.push(format!("packed({})", pack.bytes()).into());
3046 }
3047 if let Some(align) = repr.align {
3048 result.push(format!("align({})", align.bytes()).into());
3049 }
3050
3051 (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into())
3052}