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