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