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