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