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