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}
146
147#[derive(Clone, Debug, Eq, PartialEq)]
149struct RenderType {
150 id: Option<RenderTypeId>,
151 generics: Option<Vec<RenderType>>,
152 bindings: Option<Vec<(RenderTypeId, Vec<RenderType>)>>,
153}
154
155impl RenderType {
156 fn size(&self) -> usize {
157 let mut size = 1;
158 if let Some(generics) = &self.generics {
159 size += generics.iter().map(RenderType::size).sum::<usize>();
160 }
161 if let Some(bindings) = &self.bindings {
162 for (_, constraints) in bindings.iter() {
163 size += 1;
164 size += constraints.iter().map(RenderType::size).sum::<usize>();
165 }
166 }
167 size
168 }
169 fn write_to_string(&self, string: &mut String) {
174 fn write_optional_id(id: Option<RenderTypeId>, string: &mut String) {
175 match id {
177 Some(id) => id.write_to_string(string),
178 None => string.push('`'),
179 }
180 }
181 if self.generics.is_some() || self.bindings.is_some() {
185 string.push('{');
186 write_optional_id(self.id, string);
187 string.push('{');
188 for generic in self.generics.as_deref().unwrap_or_default() {
189 generic.write_to_string(string);
190 }
191 string.push('}');
192 if self.bindings.is_some() {
193 string.push('{');
194 for binding in self.bindings.as_deref().unwrap_or_default() {
195 string.push('{');
196 binding.0.write_to_string(string);
197 string.push('{');
198 for constraint in &binding.1[..] {
199 constraint.write_to_string(string);
200 }
201 string.push_str("}}");
202 }
203 string.push('}');
204 }
205 string.push('}');
206 } else {
207 write_optional_id(self.id, string);
208 }
209 }
210 fn read_from_bytes(string: &[u8]) -> (RenderType, usize) {
211 let mut i = 0;
212 if string[i] == b'{' {
213 i += 1;
214 let (id, offset) = RenderTypeId::read_from_bytes(&string[i..]);
215 i += offset;
216 let generics = if string[i] == b'{' {
217 i += 1;
218 let mut generics = Vec::new();
219 while string[i] != b'}' {
220 let (ty, offset) = RenderType::read_from_bytes(&string[i..]);
221 i += offset;
222 generics.push(ty);
223 }
224 assert!(string[i] == b'}');
225 i += 1;
226 Some(generics)
227 } else {
228 None
229 };
230 let bindings = if string[i] == b'{' {
231 i += 1;
232 let mut bindings = Vec::new();
233 while string[i] == b'{' {
234 i += 1;
235 let (binding, boffset) = RenderTypeId::read_from_bytes(&string[i..]);
236 i += boffset;
237 let mut bconstraints = Vec::new();
238 assert!(string[i] == b'{');
239 i += 1;
240 while string[i] != b'}' {
241 let (constraint, coffset) = RenderType::read_from_bytes(&string[i..]);
242 i += coffset;
243 bconstraints.push(constraint);
244 }
245 assert!(string[i] == b'}');
246 i += 1;
247 bindings.push((binding.unwrap(), bconstraints));
248 assert!(string[i] == b'}');
249 i += 1;
250 }
251 assert!(string[i] == b'}');
252 i += 1;
253 Some(bindings)
254 } else {
255 None
256 };
257 assert!(string[i] == b'}');
258 i += 1;
259 (RenderType { id, generics, bindings }, i)
260 } else {
261 let (id, offset) = RenderTypeId::read_from_bytes(string);
262 i += offset;
263 (RenderType { id, generics: None, bindings: None }, i)
264 }
265 }
266}
267
268#[derive(Clone, Copy, Debug, Eq, PartialEq)]
269enum RenderTypeId {
270 DefId(DefId),
271 Primitive(clean::PrimitiveType),
272 AssociatedType(Symbol),
273 Index(isize),
274 Mut,
275}
276
277impl RenderTypeId {
278 fn write_to_string(&self, string: &mut String) {
279 let id: i32 = match &self {
280 RenderTypeId::Index(idx) if *idx >= 0 => (idx + 1isize).try_into().unwrap(),
283 RenderTypeId::Index(idx) => (*idx).try_into().unwrap(),
285 _ => panic!("must convert render types to indexes before serializing"),
286 };
287 search_index::encode::write_signed_vlqhex_to_string(id, string);
288 }
289 fn read_from_bytes(string: &[u8]) -> (Option<RenderTypeId>, usize) {
290 let Some((value, offset)) = search_index::encode::read_signed_vlqhex_from_string(string)
291 else {
292 return (None, 0);
293 };
294 let value = isize::try_from(value).unwrap();
295 let ty = match value {
296 ..0 => Some(RenderTypeId::Index(value)),
297 0 => None,
298 1.. => Some(RenderTypeId::Index(value - 1)),
299 };
300 (ty, offset)
301 }
302}
303
304#[derive(Clone, Debug, Eq, PartialEq)]
306pub(crate) struct IndexItemFunctionType {
307 inputs: Vec<RenderType>,
308 output: Vec<RenderType>,
309 where_clause: Vec<Vec<RenderType>>,
310 param_names: Vec<Option<Symbol>>,
311}
312
313impl IndexItemFunctionType {
314 fn size(&self) -> usize {
315 self.inputs.iter().map(RenderType::size).sum::<usize>()
316 + self.output.iter().map(RenderType::size).sum::<usize>()
317 + self
318 .where_clause
319 .iter()
320 .map(|constraints| constraints.iter().map(RenderType::size).sum::<usize>())
321 .sum::<usize>()
322 }
323 fn read_from_string_without_param_names(string: &[u8]) -> (IndexItemFunctionType, usize) {
324 let mut i = 0;
325 if string[i] == b'`' {
326 return (
327 IndexItemFunctionType {
328 inputs: Vec::new(),
329 output: Vec::new(),
330 where_clause: Vec::new(),
331 param_names: Vec::new(),
332 },
333 1,
334 );
335 }
336 assert_eq!(b'{', string[i]);
337 i += 1;
338 fn read_args_from_string(string: &[u8]) -> (Vec<RenderType>, usize) {
339 let mut i = 0;
340 let mut params = Vec::new();
341 if string[i] == b'{' {
342 i += 1;
344 while string[i] != b'}' {
345 let (ty, offset) = RenderType::read_from_bytes(&string[i..]);
346 i += offset;
347 params.push(ty);
348 }
349 i += 1;
350 } else if string[i] != b'}' {
351 let (tyid, offset) = RenderTypeId::read_from_bytes(&string[i..]);
352 params.push(RenderType { id: tyid, generics: None, bindings: None });
353 i += offset;
354 }
355 (params, i)
356 }
357 let (inputs, offset) = read_args_from_string(&string[i..]);
358 i += offset;
359 let (output, offset) = read_args_from_string(&string[i..]);
360 i += offset;
361 let mut where_clause = Vec::new();
362 while string[i] != b'}' {
363 let (constraint, offset) = read_args_from_string(&string[i..]);
364 i += offset;
365 where_clause.push(constraint);
366 }
367 assert_eq!(b'}', string[i], "{} {}", String::from_utf8_lossy(&string), i);
368 i += 1;
369 (IndexItemFunctionType { inputs, output, where_clause, param_names: Vec::new() }, i)
370 }
371 fn write_to_string_without_param_names<'a>(&'a self, string: &mut String) {
372 let has_missing = self
375 .inputs
376 .iter()
377 .chain(self.output.iter())
378 .any(|i| i.id.is_none() && i.generics.is_none());
379 if has_missing {
380 string.push('`');
381 } else {
382 string.push('{');
383 match &self.inputs[..] {
384 [one] if one.generics.is_none() && one.bindings.is_none() => {
385 one.write_to_string(string);
386 }
387 _ => {
388 string.push('{');
389 for item in &self.inputs[..] {
390 item.write_to_string(string);
391 }
392 string.push('}');
393 }
394 }
395 match &self.output[..] {
396 [] if self.where_clause.is_empty() => {}
397 [one] if one.generics.is_none() && one.bindings.is_none() => {
398 one.write_to_string(string);
399 }
400 _ => {
401 string.push('{');
402 for item in &self.output[..] {
403 item.write_to_string(string);
404 }
405 string.push('}');
406 }
407 }
408 for constraint in &self.where_clause {
409 if let [one] = &constraint[..]
410 && one.generics.is_none()
411 && one.bindings.is_none()
412 {
413 one.write_to_string(string);
414 } else {
415 string.push('{');
416 for item in &constraint[..] {
417 item.write_to_string(string);
418 }
419 string.push('}');
420 }
421 }
422 string.push('}');
423 }
424 }
425}
426
427#[derive(Debug, Clone)]
428pub(crate) struct StylePath {
429 pub(crate) path: PathBuf,
431}
432
433impl StylePath {
434 pub(crate) fn basename(&self) -> Result<String, Error> {
435 Ok(try_none!(try_none!(self.path.file_stem(), &self.path).to_str(), &self.path).to_string())
436 }
437}
438
439#[derive(Debug, Eq, PartialEq, Hash)]
440struct ItemEntry {
441 url: String,
442 name: String,
443}
444
445impl ItemEntry {
446 fn new(mut url: String, name: String) -> ItemEntry {
447 while url.starts_with('/') {
448 url.remove(0);
449 }
450 ItemEntry { url, name }
451 }
452}
453
454impl ItemEntry {
455 fn print(&self) -> impl fmt::Display {
456 fmt::from_fn(move |f| write!(f, "<a href=\"{}\">{}</a>", self.url, Escape(&self.name)))
457 }
458}
459
460impl PartialOrd for ItemEntry {
461 fn partial_cmp(&self, other: &ItemEntry) -> Option<::std::cmp::Ordering> {
462 Some(self.cmp(other))
463 }
464}
465
466impl Ord for ItemEntry {
467 fn cmp(&self, other: &ItemEntry) -> ::std::cmp::Ordering {
468 self.name.cmp(&other.name)
469 }
470}
471
472#[derive(Debug)]
473struct AllTypes {
474 structs: FxIndexSet<ItemEntry>,
475 enums: FxIndexSet<ItemEntry>,
476 unions: FxIndexSet<ItemEntry>,
477 primitives: FxIndexSet<ItemEntry>,
478 traits: FxIndexSet<ItemEntry>,
479 macros: FxIndexSet<ItemEntry>,
480 functions: FxIndexSet<ItemEntry>,
481 type_aliases: FxIndexSet<ItemEntry>,
482 statics: FxIndexSet<ItemEntry>,
483 constants: FxIndexSet<ItemEntry>,
484 attribute_macros: FxIndexSet<ItemEntry>,
485 derive_macros: FxIndexSet<ItemEntry>,
486 trait_aliases: FxIndexSet<ItemEntry>,
487}
488
489impl AllTypes {
490 fn new() -> AllTypes {
491 let new_set = |cap| FxIndexSet::with_capacity_and_hasher(cap, Default::default());
492 AllTypes {
493 structs: new_set(100),
494 enums: new_set(100),
495 unions: new_set(100),
496 primitives: new_set(26),
497 traits: new_set(100),
498 macros: new_set(100),
499 functions: new_set(100),
500 type_aliases: new_set(100),
501 statics: new_set(100),
502 constants: new_set(100),
503 attribute_macros: new_set(100),
504 derive_macros: new_set(100),
505 trait_aliases: new_set(100),
506 }
507 }
508
509 fn append(&mut self, item_name: String, item_type: &ItemType) {
510 let mut url: Vec<_> = item_name.split("::").skip(1).collect();
511 if let Some(name) = url.pop() {
512 let new_url = format!("{}/{item_type}.{name}.html", url.join("/"));
513 url.push(name);
514 let name = url.join("::");
515 match *item_type {
516 ItemType::Struct => self.structs.insert(ItemEntry::new(new_url, name)),
517 ItemType::Enum => self.enums.insert(ItemEntry::new(new_url, name)),
518 ItemType::Union => self.unions.insert(ItemEntry::new(new_url, name)),
519 ItemType::Primitive => self.primitives.insert(ItemEntry::new(new_url, name)),
520 ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)),
521 ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)),
522 ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)),
523 ItemType::TypeAlias => self.type_aliases.insert(ItemEntry::new(new_url, name)),
524 ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)),
525 ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)),
526 ItemType::ProcAttribute => {
527 self.attribute_macros.insert(ItemEntry::new(new_url, name))
528 }
529 ItemType::ProcDerive => self.derive_macros.insert(ItemEntry::new(new_url, name)),
530 ItemType::TraitAlias => self.trait_aliases.insert(ItemEntry::new(new_url, name)),
531 _ => true,
532 };
533 }
534 }
535
536 fn item_sections(&self) -> FxHashSet<ItemSection> {
537 let mut sections = FxHashSet::default();
538
539 if !self.structs.is_empty() {
540 sections.insert(ItemSection::Structs);
541 }
542 if !self.enums.is_empty() {
543 sections.insert(ItemSection::Enums);
544 }
545 if !self.unions.is_empty() {
546 sections.insert(ItemSection::Unions);
547 }
548 if !self.primitives.is_empty() {
549 sections.insert(ItemSection::PrimitiveTypes);
550 }
551 if !self.traits.is_empty() {
552 sections.insert(ItemSection::Traits);
553 }
554 if !self.macros.is_empty() {
555 sections.insert(ItemSection::Macros);
556 }
557 if !self.functions.is_empty() {
558 sections.insert(ItemSection::Functions);
559 }
560 if !self.type_aliases.is_empty() {
561 sections.insert(ItemSection::TypeAliases);
562 }
563 if !self.statics.is_empty() {
564 sections.insert(ItemSection::Statics);
565 }
566 if !self.constants.is_empty() {
567 sections.insert(ItemSection::Constants);
568 }
569 if !self.attribute_macros.is_empty() {
570 sections.insert(ItemSection::AttributeMacros);
571 }
572 if !self.derive_macros.is_empty() {
573 sections.insert(ItemSection::DeriveMacros);
574 }
575 if !self.trait_aliases.is_empty() {
576 sections.insert(ItemSection::TraitAliases);
577 }
578
579 sections
580 }
581
582 fn print(&self) -> impl fmt::Display {
583 fn print_entries(e: &FxIndexSet<ItemEntry>, kind: ItemSection) -> impl fmt::Display {
584 fmt::from_fn(move |f| {
585 if e.is_empty() {
586 return Ok(());
587 }
588
589 let mut e: Vec<&ItemEntry> = e.iter().collect();
590 e.sort();
591 write!(
592 f,
593 "<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">",
594 id = kind.id(),
595 title = kind.name(),
596 )?;
597
598 for s in e.iter() {
599 write!(f, "<li>{}</li>", s.print())?;
600 }
601
602 f.write_str("</ul>")
603 })
604 }
605
606 fmt::from_fn(|f| {
607 f.write_str(
608 "<div class=\"main-heading\">\
609 <h1>List of all items</h1>\
610 <rustdoc-toolbar></rustdoc-toolbar>\
611 </div>",
612 )?;
613 print_entries(&self.structs, ItemSection::Structs).fmt(f)?;
616 print_entries(&self.enums, ItemSection::Enums).fmt(f)?;
617 print_entries(&self.unions, ItemSection::Unions).fmt(f)?;
618 print_entries(&self.primitives, ItemSection::PrimitiveTypes).fmt(f)?;
619 print_entries(&self.traits, ItemSection::Traits).fmt(f)?;
620 print_entries(&self.macros, ItemSection::Macros).fmt(f)?;
621 print_entries(&self.attribute_macros, ItemSection::AttributeMacros).fmt(f)?;
622 print_entries(&self.derive_macros, ItemSection::DeriveMacros).fmt(f)?;
623 print_entries(&self.functions, ItemSection::Functions).fmt(f)?;
624 print_entries(&self.type_aliases, ItemSection::TypeAliases).fmt(f)?;
625 print_entries(&self.trait_aliases, ItemSection::TraitAliases).fmt(f)?;
626 print_entries(&self.statics, ItemSection::Statics).fmt(f)?;
627 print_entries(&self.constants, ItemSection::Constants).fmt(f)?;
628 Ok(())
629 })
630 }
631}
632
633fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
634 let mut content = SCRAPE_EXAMPLES_HELP_MD.to_owned();
635 content.push_str(&format!(
636 "## More information\n\n\
637 If you want more information about this feature, please read the [corresponding chapter in \
638 the Rustdoc book]({DOC_RUST_LANG_ORG_VERSION}/rustdoc/scraped-examples.html)."
639 ));
640
641 format!(
642 "<div class=\"main-heading\">\
643 <h1>About scraped examples</h1>\
644 </div>\
645 <div>{}</div>",
646 fmt::from_fn(|f| Markdown {
647 content: &content,
648 links: &[],
649 ids: &mut IdMap::default(),
650 error_codes: shared.codes,
651 edition: shared.edition(),
652 playground: &shared.playground,
653 heading_offset: HeadingOffset::H1,
654 }
655 .write_into(f))
656 )
657}
658
659fn document(
660 cx: &Context<'_>,
661 item: &clean::Item,
662 parent: Option<&clean::Item>,
663 heading_offset: HeadingOffset,
664) -> impl fmt::Display {
665 if let Some(ref name) = item.name {
666 info!("Documenting {name}");
667 }
668
669 fmt::from_fn(move |f| {
670 document_item_info(cx, item, parent).render_into(f)?;
671 if parent.is_none() {
672 write!(f, "{}", document_full_collapsible(item, cx, heading_offset))
673 } else {
674 write!(f, "{}", document_full(item, cx, heading_offset))
675 }
676 })
677}
678
679fn render_markdown(
681 cx: &Context<'_>,
682 md_text: &str,
683 links: Vec<RenderedLink>,
684 heading_offset: HeadingOffset,
685) -> impl fmt::Display {
686 fmt::from_fn(move |f| {
687 f.write_str("<div class=\"docblock\">")?;
688 Markdown {
689 content: md_text,
690 links: &links,
691 ids: &mut cx.id_map.borrow_mut(),
692 error_codes: cx.shared.codes,
693 edition: cx.shared.edition(),
694 playground: &cx.shared.playground,
695 heading_offset,
696 }
697 .write_into(&mut *f)?;
698 f.write_str("</div>")
699 })
700}
701
702fn document_short(
705 item: &clean::Item,
706 cx: &Context<'_>,
707 link: AssocItemLink<'_>,
708 parent: &clean::Item,
709 show_def_docs: bool,
710) -> impl fmt::Display {
711 fmt::from_fn(move |f| {
712 document_item_info(cx, item, Some(parent)).render_into(f)?;
713 if !show_def_docs {
714 return Ok(());
715 }
716 let s = item.doc_value();
717 if !s.is_empty() {
718 let (mut summary_html, has_more_content) =
719 MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content();
720
721 let link = if has_more_content {
722 let link = fmt::from_fn(|f| {
723 write!(
724 f,
725 " <a{}>Read more</a>",
726 assoc_href_attr(item, link, cx).maybe_display()
727 )
728 });
729
730 if let Some(idx) = summary_html.rfind("</p>") {
731 summary_html.insert_str(idx, &link.to_string());
732 None
733 } else {
734 Some(link)
735 }
736 } else {
737 None
738 }
739 .maybe_display();
740
741 write!(f, "<div class='docblock'>{summary_html}{link}</div>")?;
742 }
743 Ok(())
744 })
745}
746
747fn document_full_collapsible(
748 item: &clean::Item,
749 cx: &Context<'_>,
750 heading_offset: HeadingOffset,
751) -> impl fmt::Display {
752 document_full_inner(item, cx, true, heading_offset)
753}
754
755fn document_full(
756 item: &clean::Item,
757 cx: &Context<'_>,
758 heading_offset: HeadingOffset,
759) -> impl fmt::Display {
760 document_full_inner(item, cx, false, heading_offset)
761}
762
763fn document_full_inner(
764 item: &clean::Item,
765 cx: &Context<'_>,
766 is_collapsible: bool,
767 heading_offset: HeadingOffset,
768) -> impl fmt::Display {
769 fmt::from_fn(move |f| {
770 if let Some(s) = item.opt_doc_value() {
771 debug!("Doc block: =====\n{s}\n=====");
772 if is_collapsible {
773 write!(
774 f,
775 "<details class=\"toggle top-doc\" open>\
776 <summary class=\"hideme\">\
777 <span>Expand description</span>\
778 </summary>{}</details>",
779 render_markdown(cx, &s, item.links(cx), heading_offset)
780 )?;
781 } else {
782 write!(f, "{}", render_markdown(cx, &s, item.links(cx), heading_offset))?;
783 }
784 }
785
786 let kind = match &item.kind {
787 clean::ItemKind::StrippedItem(box kind) | kind => kind,
788 };
789
790 if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind {
791 render_call_locations(f, cx, item)?;
792 }
793 Ok(())
794 })
795}
796
797#[derive(Template)]
798#[template(path = "item_info.html")]
799struct ItemInfo {
800 items: Vec<ShortItemInfo>,
801}
802fn document_item_info(
808 cx: &Context<'_>,
809 item: &clean::Item,
810 parent: Option<&clean::Item>,
811) -> ItemInfo {
812 let items = short_item_info(item, cx, parent);
813 ItemInfo { items }
814}
815
816fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> {
817 let cfg = match (&item.cfg, parent.and_then(|p| p.cfg.as_ref())) {
818 (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
819 (cfg, _) => cfg.as_deref().cloned(),
820 };
821
822 debug!(
823 "Portability {name:?} {item_cfg:?} (parent: {parent:?}) - {parent_cfg:?} = {cfg:?}",
824 name = item.name,
825 item_cfg = item.cfg,
826 parent_cfg = parent.and_then(|p| p.cfg.as_ref()),
827 );
828
829 Some(cfg?.render_long_html())
830}
831
832#[derive(Template)]
833#[template(path = "short_item_info.html")]
834enum ShortItemInfo {
835 Deprecation {
837 message: String,
838 },
839 Unstable {
842 feature: String,
843 tracking: Option<(String, u32)>,
844 },
845 Portability {
846 message: String,
847 },
848}
849
850fn short_item_info(
853 item: &clean::Item,
854 cx: &Context<'_>,
855 parent: Option<&clean::Item>,
856) -> Vec<ShortItemInfo> {
857 let mut extra_info = vec![];
858
859 if let Some(depr @ Deprecation { note, since, suggestion: _ }) = item.deprecation(cx.tcx()) {
860 let mut message = match since {
863 DeprecatedSince::RustcVersion(version) => {
864 if depr.is_in_effect() {
865 format!("Deprecated since {version}")
866 } else {
867 format!("Deprecating in {version}")
868 }
869 }
870 DeprecatedSince::Future => String::from("Deprecating in a future version"),
871 DeprecatedSince::NonStandard(since) => {
872 format!("Deprecated since {}", Escape(since.as_str()))
873 }
874 DeprecatedSince::Unspecified | DeprecatedSince::Err => String::from("Deprecated"),
875 };
876
877 if let Some(note) = note {
878 let note = note.as_str();
879 let mut id_map = cx.id_map.borrow_mut();
880 let links = item.links(cx);
881 let html = MarkdownItemInfo::new(note, &links, &mut id_map);
882 message.push_str(": ");
883 html.write_into(&mut message).unwrap();
884 }
885 extra_info.push(ShortItemInfo::Deprecation { message });
886 }
887
888 if let Some((StabilityLevel::Unstable { reason: _, issue, .. }, feature)) = item
891 .stability(cx.tcx())
892 .as_ref()
893 .filter(|stab| stab.feature != sym::rustc_private)
894 .map(|stab| (stab.level, stab.feature))
895 {
896 let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue)
897 {
898 Some((url.clone(), issue.get()))
899 } else {
900 None
901 };
902 extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking });
903 }
904
905 if let Some(message) = portability(item, parent) {
906 extra_info.push(ShortItemInfo::Portability { message });
907 }
908
909 extra_info
910}
911
912fn render_impls(
915 cx: &Context<'_>,
916 mut w: impl Write,
917 impls: &[&Impl],
918 containing_item: &clean::Item,
919 toggle_open_by_default: bool,
920) -> fmt::Result {
921 let mut rendered_impls = impls
922 .iter()
923 .map(|i| {
924 let did = i.trait_did().unwrap();
925 let provided_trait_methods = i.inner_impl().provided_trait_methods(cx.tcx());
926 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods);
927 let imp = render_impl(
928 cx,
929 i,
930 containing_item,
931 assoc_link,
932 RenderMode::Normal,
933 None,
934 &[],
935 ImplRenderingParameters {
936 show_def_docs: true,
937 show_default_items: true,
938 show_non_assoc_items: true,
939 toggle_open_by_default,
940 },
941 );
942 imp.to_string()
943 })
944 .collect::<Vec<_>>();
945 rendered_impls.sort();
946 w.write_str(&rendered_impls.join(""))
947}
948
949fn assoc_href_attr(
951 it: &clean::Item,
952 link: AssocItemLink<'_>,
953 cx: &Context<'_>,
954) -> Option<impl fmt::Display> {
955 let name = it.name.unwrap();
956 let item_type = it.type_();
957
958 enum Href<'a> {
959 AnchorId(&'a str),
960 Anchor(ItemType),
961 Url(String, ItemType),
962 }
963
964 let href = match link {
965 AssocItemLink::Anchor(Some(id)) => Href::AnchorId(id),
966 AssocItemLink::Anchor(None) => Href::Anchor(item_type),
967 AssocItemLink::GotoSource(did, provided_methods) => {
968 let item_type = match item_type {
971 ItemType::Method | ItemType::TyMethod => {
975 if provided_methods.contains(&name) {
976 ItemType::Method
977 } else {
978 ItemType::TyMethod
979 }
980 }
981 item_type => item_type,
983 };
984
985 match href(did.expect_def_id(), cx) {
986 Ok(HrefInfo { url, .. }) => Href::Url(url, item_type),
987 Err(HrefError::DocumentationNotBuilt) => return None,
999 Err(_) => Href::Anchor(item_type),
1000 }
1001 }
1002 };
1003
1004 let href = fmt::from_fn(move |f| match &href {
1005 Href::AnchorId(id) => write!(f, "#{id}"),
1006 Href::Url(url, item_type) => {
1007 write!(f, "{url}#{item_type}.{name}")
1008 }
1009 Href::Anchor(item_type) => {
1010 write!(f, "#{item_type}.{name}")
1011 }
1012 });
1013
1014 Some(fmt::from_fn(move |f| write!(f, " href=\"{href}\"")))
1017}
1018
1019#[derive(Debug)]
1020enum AssocConstValue<'a> {
1021 TraitDefault(&'a clean::ConstantKind),
1025 Impl(&'a clean::ConstantKind),
1027 None,
1028}
1029
1030fn assoc_const(
1031 it: &clean::Item,
1032 generics: &clean::Generics,
1033 ty: &clean::Type,
1034 value: AssocConstValue<'_>,
1035 link: AssocItemLink<'_>,
1036 indent: usize,
1037 cx: &Context<'_>,
1038) -> impl fmt::Display {
1039 let tcx = cx.tcx();
1040 fmt::from_fn(move |w| {
1041 render_attributes_in_code(w, it, &" ".repeat(indent), cx)?;
1042 write!(
1043 w,
1044 "{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
1045 indent = " ".repeat(indent),
1046 vis = visibility_print_with_space(it, cx),
1047 href = assoc_href_attr(it, link, cx).maybe_display(),
1048 name = it.name.as_ref().unwrap(),
1049 generics = print_generics(generics, cx),
1050 ty = print_type(ty, cx),
1051 )?;
1052 if let AssocConstValue::TraitDefault(konst) | AssocConstValue::Impl(konst) = value {
1053 let repr = konst.expr(tcx);
1054 if match value {
1055 AssocConstValue::TraitDefault(_) => true, AssocConstValue::Impl(_) => repr != "_", AssocConstValue::None => unreachable!(),
1060 } {
1061 write!(w, " = {}", Escape(&repr))?;
1062 }
1063 }
1064 write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display())
1065 })
1066}
1067
1068fn assoc_type(
1069 it: &clean::Item,
1070 generics: &clean::Generics,
1071 bounds: &[clean::GenericBound],
1072 default: Option<&clean::Type>,
1073 link: AssocItemLink<'_>,
1074 indent: usize,
1075 cx: &Context<'_>,
1076) -> impl fmt::Display {
1077 fmt::from_fn(move |w| {
1078 render_attributes_in_code(w, it, &" ".repeat(indent), cx)?;
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 = print_generics(generics, 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, " = {}", print_type(default, 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!("{:#}", print_generics(g, 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 = print_generics(g, cx),
1159 decl = full_print_fn_decl(d, 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) -> fmt::Result {
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 }
1380
1381 if !synthetic.is_empty() {
1382 write!(
1383 w,
1384 "{}<div id=\"synthetic-implementations-list\">",
1385 write_impl_section_heading("Auto Trait Implementations", "synthetic-implementations",)
1386 )?;
1387 render_impls(cx, &mut w, synthetic, containing_item, false)?;
1388 w.write_str("</div>")?;
1389 }
1390
1391 if !blanket_impl.is_empty() {
1392 write!(
1393 w,
1394 "{}<div id=\"blanket-implementations-list\">",
1395 write_impl_section_heading("Blanket Implementations", "blanket-implementations")
1396 )?;
1397 render_impls(cx, &mut w, blanket_impl, containing_item, false)?;
1398 w.write_str("</div>")?;
1399 }
1400 Ok(())
1401}
1402
1403fn render_assoc_items(
1404 cx: &Context<'_>,
1405 containing_item: &clean::Item,
1406 it: DefId,
1407 what: AssocItemRender<'_>,
1408) -> impl fmt::Display {
1409 fmt::from_fn(move |f| {
1410 let mut derefs = DefIdSet::default();
1411 derefs.insert(it);
1412 render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs)
1413 })
1414}
1415
1416fn render_assoc_items_inner(
1417 mut w: &mut dyn fmt::Write,
1418 cx: &Context<'_>,
1419 containing_item: &clean::Item,
1420 it: DefId,
1421 what: AssocItemRender<'_>,
1422 derefs: &mut DefIdSet,
1423) -> fmt::Result {
1424 info!("Documenting associated items of {:?}", containing_item.name);
1425 let cache = &cx.shared.cache;
1426 let Some(v) = cache.impls.get(&it) else { return Ok(()) };
1427 let (mut non_trait, traits): (Vec<_>, _) =
1428 v.iter().partition(|i| i.inner_impl().trait_.is_none());
1429 if !non_trait.is_empty() {
1430 let render_mode = what.render_mode();
1431 let class_html = what
1432 .class()
1433 .map(|class| fmt::from_fn(move |f| write!(f, r#" class="{class}""#)))
1434 .maybe_display();
1435 let (section_heading, id) = match what {
1436 AssocItemRender::All => (
1437 Either::Left(write_impl_section_heading("Implementations", "implementations")),
1438 Cow::Borrowed("implementations-list"),
1439 ),
1440 AssocItemRender::DerefFor { trait_, type_, .. } => {
1441 let id = cx.derive_id(small_url_encode(format!(
1442 "deref-methods-{:#}",
1443 print_type(type_, cx)
1444 )));
1445 non_trait.retain(|impl_| {
1453 type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache)
1454 });
1455 let derived_id = cx.derive_id(&id);
1456 if let Some(def_id) = type_.def_id(cx.cache()) {
1457 cx.deref_id_map.borrow_mut().insert(def_id, id.clone());
1458 }
1459 (
1460 Either::Right(fmt::from_fn(move |f| {
1461 write!(
1462 f,
1463 "<details class=\"toggle big-toggle\" open><summary>{}</summary>",
1464 write_impl_section_heading(
1465 fmt::from_fn(|f| write!(
1466 f,
1467 "<span>Methods from {trait_}<Target = {type_}></span>",
1468 trait_ = print_path(trait_, cx),
1469 type_ = print_type(type_, cx),
1470 )),
1471 &id,
1472 )
1473 )
1474 })),
1475 Cow::Owned(derived_id),
1476 )
1477 }
1478 };
1479 let impls_buf = fmt::from_fn(|f| {
1480 non_trait
1481 .iter()
1482 .map(|i| {
1483 render_impl(
1484 cx,
1485 i,
1486 containing_item,
1487 AssocItemLink::Anchor(None),
1488 render_mode,
1489 None,
1490 &[],
1491 ImplRenderingParameters {
1492 show_def_docs: true,
1493 show_default_items: true,
1494 show_non_assoc_items: true,
1495 toggle_open_by_default: true,
1496 },
1497 )
1498 })
1499 .joined("", f)
1500 })
1501 .to_string();
1502
1503 if !impls_buf.is_empty() {
1504 write!(
1505 w,
1506 "{section_heading}<div id=\"{id}\"{class_html}>{impls_buf}</div>{}",
1507 matches!(what, AssocItemRender::DerefFor { .. })
1508 .then_some("</details>")
1509 .maybe_display(),
1510 )?;
1511 }
1512 }
1513
1514 if !traits.is_empty() {
1515 let deref_impl =
1516 traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait());
1517 if let Some(impl_) = deref_impl {
1518 let has_deref_mut =
1519 traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
1520 render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs)?;
1521 }
1522
1523 if let AssocItemRender::DerefFor { .. } = what {
1526 return Ok(());
1527 }
1528
1529 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
1530 traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
1531 let (blanket_impl, concrete): (Vec<&Impl>, _) =
1532 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
1533
1534 render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl)?;
1535 }
1536 Ok(())
1537}
1538
1539fn render_deref_methods(
1541 mut w: impl Write,
1542 cx: &Context<'_>,
1543 impl_: &Impl,
1544 container_item: &clean::Item,
1545 deref_mut: bool,
1546 derefs: &mut DefIdSet,
1547) -> fmt::Result {
1548 let cache = cx.cache();
1549 let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
1550 let (target, real_target) = impl_
1551 .inner_impl()
1552 .items
1553 .iter()
1554 .find_map(|item| match item.kind {
1555 clean::AssocTypeItem(box ref t, _) => Some(match *t {
1556 clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
1557 _ => (&t.type_, &t.type_),
1558 }),
1559 _ => None,
1560 })
1561 .expect("Expected associated type binding");
1562 debug!(
1563 "Render deref methods for {for_:#?}, target {target:#?}",
1564 for_ = impl_.inner_impl().for_
1565 );
1566 let what =
1567 AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
1568 if let Some(did) = target.def_id(cache) {
1569 if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) {
1570 if did == type_did || !derefs.insert(did) {
1572 return Ok(());
1574 }
1575 }
1576 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1577 } else if let Some(prim) = target.primitive_type()
1578 && let Some(&did) = cache.primitive_locations.get(&prim)
1579 {
1580 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1581 }
1582 Ok(())
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!("{:#}", print_type(ty, 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 print_type(&impl_.for_, 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>", print_impl(impl_, 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!("{:#}", print_type(ty, 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 let mut trait_item_deprecated = false;
1796 if render_method_item {
1797 if !is_default_item {
1798 if let Some(t) = trait_ {
1799 if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1802 trait_item_deprecated = it.is_deprecated(cx.tcx());
1803 if !item.doc_value().is_empty() {
1806 document_item_info(cx, it, Some(parent))
1807 .render_into(&mut info_buffer)?;
1808 doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1809 short_documented = false;
1810 } else {
1811 doc_buffer = document_short(
1814 it,
1815 cx,
1816 link,
1817 parent,
1818 rendering_params.show_def_docs,
1819 )
1820 .to_string();
1821 }
1822 }
1823 } else {
1824 document_item_info(cx, item, Some(parent)).render_into(&mut info_buffer)?;
1825 if rendering_params.show_def_docs {
1826 doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1827 short_documented = false;
1828 }
1829 }
1830 } else {
1831 doc_buffer =
1832 document_short(item, cx, link, parent, rendering_params.show_def_docs)
1833 .to_string();
1834 }
1835 }
1836 let mut w = if short_documented && trait_.is_some() {
1837 Either::Left(interesting)
1838 } else {
1839 Either::Right(boring)
1840 };
1841
1842 let mut deprecation_class = if trait_item_deprecated || item.is_deprecated(cx.tcx()) {
1843 " deprecated"
1844 } else {
1845 ""
1846 };
1847
1848 let toggled = !doc_buffer.is_empty();
1849 if toggled {
1850 let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
1851 write!(
1852 w,
1853 "<details class=\"toggle{method_toggle_class}{deprecation_class}\" open><summary>"
1854 )?;
1855 deprecation_class = "";
1856 }
1857 match &item.kind {
1858 clean::MethodItem(..) | clean::RequiredMethodItem(_) => {
1859 if render_method_item {
1861 let id = cx.derive_id(format!("{item_type}.{name}"));
1862 let source_id = trait_
1863 .and_then(|trait_| {
1864 trait_
1865 .items
1866 .iter()
1867 .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
1868 })
1869 .map(|item| format!("{}.{name}", item.type_()));
1870 write!(
1871 w,
1872 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1873 {}",
1874 render_rightside(cx, item, render_mode)
1875 )?;
1876 if trait_.is_some() {
1877 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1879 }
1880 write!(
1881 w,
1882 "<h4 class=\"code-header\">{}</h4></section>",
1883 render_assoc_item(
1884 item,
1885 link.anchor(source_id.as_ref().unwrap_or(&id)),
1886 ItemType::Impl,
1887 cx,
1888 render_mode,
1889 ),
1890 )?;
1891 }
1892 }
1893 clean::RequiredAssocConstItem(generics, ty) => {
1894 let source_id = format!("{item_type}.{name}");
1895 let id = cx.derive_id(&source_id);
1896 write!(
1897 w,
1898 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1899 {}",
1900 render_rightside(cx, item, render_mode)
1901 )?;
1902 if trait_.is_some() {
1903 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1905 }
1906 write!(
1907 w,
1908 "<h4 class=\"code-header\">{}</h4></section>",
1909 assoc_const(
1910 item,
1911 generics,
1912 ty,
1913 AssocConstValue::None,
1914 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1915 0,
1916 cx,
1917 ),
1918 )?;
1919 }
1920 clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
1921 let source_id = format!("{item_type}.{name}");
1922 let id = cx.derive_id(&source_id);
1923 write!(
1924 w,
1925 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1926 {}",
1927 render_rightside(cx, item, render_mode),
1928 )?;
1929 if trait_.is_some() {
1930 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1932 }
1933 write!(
1934 w,
1935 "<h4 class=\"code-header\">{}</h4></section>",
1936 assoc_const(
1937 item,
1938 &ci.generics,
1939 &ci.type_,
1940 match item.kind {
1941 clean::ProvidedAssocConstItem(_) =>
1942 AssocConstValue::TraitDefault(&ci.kind),
1943 clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
1944 _ => unreachable!(),
1945 },
1946 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1947 0,
1948 cx,
1949 ),
1950 )?;
1951 }
1952 clean::RequiredAssocTypeItem(generics, bounds) => {
1953 let source_id = format!("{item_type}.{name}");
1954 let id = cx.derive_id(&source_id);
1955 write!(
1956 w,
1957 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1958 {}",
1959 render_rightside(cx, item, render_mode),
1960 )?;
1961 if trait_.is_some() {
1962 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1964 }
1965 write!(
1966 w,
1967 "<h4 class=\"code-header\">{}</h4></section>",
1968 assoc_type(
1969 item,
1970 generics,
1971 bounds,
1972 None,
1973 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1974 0,
1975 cx,
1976 ),
1977 )?;
1978 }
1979 clean::AssocTypeItem(tydef, _bounds) => {
1980 let source_id = format!("{item_type}.{name}");
1981 let id = cx.derive_id(&source_id);
1982 write!(
1983 w,
1984 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1985 {}",
1986 render_rightside(cx, item, render_mode),
1987 )?;
1988 if trait_.is_some() {
1989 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1991 }
1992 write!(
1993 w,
1994 "<h4 class=\"code-header\">{}</h4></section>",
1995 assoc_type(
1996 item,
1997 &tydef.generics,
1998 &[], Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
2000 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2001 0,
2002 cx,
2003 ),
2004 )?;
2005 }
2006 clean::StrippedItem(..) => return Ok(()),
2007 _ => panic!("can't make docs for trait item with name {:?}", item.name),
2008 }
2009
2010 w.write_str(&info_buffer)?;
2011 if toggled {
2012 write!(w, "</summary>{doc_buffer}</details>")?;
2013 }
2014 Ok(())
2015 }
2016
2017 let mut impl_items = String::new();
2018 let mut default_impl_items = String::new();
2019 let impl_ = i.inner_impl();
2020
2021 let mut assoc_types = Vec::new();
2031 let mut methods = Vec::new();
2032
2033 if !impl_.is_negative_trait_impl() {
2034 for impl_item in &impl_.items {
2035 match impl_item.kind {
2036 clean::MethodItem(..) | clean::RequiredMethodItem(_) => methods.push(impl_item),
2037 clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => {
2038 assoc_types.push(impl_item)
2039 }
2040 clean::RequiredAssocConstItem(..)
2041 | clean::ProvidedAssocConstItem(_)
2042 | clean::ImplAssocConstItem(_) => {
2043 doc_impl_item(
2045 &mut default_impl_items,
2046 &mut impl_items,
2047 cx,
2048 impl_item,
2049 if trait_.is_some() { &i.impl_item } else { parent },
2050 link,
2051 render_mode,
2052 false,
2053 trait_,
2054 rendering_params,
2055 )?;
2056 }
2057 _ => {}
2058 }
2059 }
2060
2061 for assoc_type in assoc_types {
2062 doc_impl_item(
2063 &mut default_impl_items,
2064 &mut impl_items,
2065 cx,
2066 assoc_type,
2067 if trait_.is_some() { &i.impl_item } else { parent },
2068 link,
2069 render_mode,
2070 false,
2071 trait_,
2072 rendering_params,
2073 )?;
2074 }
2075 for method in methods {
2076 doc_impl_item(
2077 &mut default_impl_items,
2078 &mut impl_items,
2079 cx,
2080 method,
2081 if trait_.is_some() { &i.impl_item } else { parent },
2082 link,
2083 render_mode,
2084 false,
2085 trait_,
2086 rendering_params,
2087 )?;
2088 }
2089 }
2090
2091 fn render_default_items(
2092 mut boring: impl fmt::Write,
2093 mut interesting: impl fmt::Write,
2094 cx: &Context<'_>,
2095 t: &clean::Trait,
2096 i: &clean::Impl,
2097 parent: &clean::Item,
2098 render_mode: RenderMode,
2099 rendering_params: ImplRenderingParameters,
2100 ) -> fmt::Result {
2101 for trait_item in &t.items {
2102 if let Some(impl_def_id) = parent.item_id.as_def_id()
2105 && let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
2106 && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id))
2107 {
2108 continue;
2109 }
2110
2111 let n = trait_item.name;
2112 if i.items.iter().any(|m| m.name == n) {
2113 continue;
2114 }
2115 let did = i.trait_.as_ref().unwrap().def_id();
2116 let provided_methods = i.provided_trait_methods(cx.tcx());
2117 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
2118
2119 doc_impl_item(
2120 &mut boring,
2121 &mut interesting,
2122 cx,
2123 trait_item,
2124 parent,
2125 assoc_link,
2126 render_mode,
2127 true,
2128 Some(t),
2129 rendering_params,
2130 )?;
2131 }
2132 Ok(())
2133 }
2134
2135 if rendering_params.show_default_items
2140 && let Some(t) = trait_
2141 && !impl_.is_negative_trait_impl()
2142 {
2143 render_default_items(
2144 &mut default_impl_items,
2145 &mut impl_items,
2146 cx,
2147 t,
2148 impl_,
2149 &i.impl_item,
2150 render_mode,
2151 rendering_params,
2152 )?;
2153 }
2154 if render_mode == RenderMode::Normal {
2155 let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
2156 let deprecation_attr = if impl_.is_deprecated
2157 || trait_.is_some_and(|trait_| trait_.is_deprecated(cx.tcx()))
2158 {
2159 " deprecated"
2160 } else {
2161 ""
2162 };
2163 if toggled {
2164 close_tags.push("</details>");
2165 write!(
2166 w,
2167 "<details class=\"toggle implementors-toggle{deprecation_attr}\"{}>\
2168 <summary>",
2169 if rendering_params.toggle_open_by_default { " open" } else { "" }
2170 )?;
2171 }
2172
2173 let (before_dox, after_dox) = i
2174 .impl_item
2175 .opt_doc_value()
2176 .map(|dox| {
2177 Markdown {
2178 content: &dox,
2179 links: &i.impl_item.links(cx),
2180 ids: &mut cx.id_map.borrow_mut(),
2181 error_codes: cx.shared.codes,
2182 edition: cx.shared.edition(),
2183 playground: &cx.shared.playground,
2184 heading_offset: HeadingOffset::H4,
2185 }
2186 .split_summary_and_content()
2187 })
2188 .unwrap_or((None, None));
2189
2190 write!(
2191 w,
2192 "{}",
2193 render_impl_summary(
2194 cx,
2195 i,
2196 parent,
2197 rendering_params.show_def_docs,
2198 use_absolute,
2199 aliases,
2200 before_dox.as_deref(),
2201 trait_.is_none() && impl_.items.is_empty(),
2202 )
2203 )?;
2204 if toggled {
2205 w.write_str("</summary>")?;
2206 }
2207
2208 if before_dox.is_some()
2209 && let Some(after_dox) = after_dox
2210 {
2211 write!(w, "<div class=\"docblock\">{after_dox}</div>")?;
2212 }
2213
2214 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2215 w.write_str("<div class=\"impl-items\">")?;
2216 close_tags.push("</div>");
2217 }
2218 }
2219 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2220 w.write_str(&default_impl_items)?;
2221 w.write_str(&impl_items)?;
2222 }
2223 for tag in close_tags.into_iter().rev() {
2224 w.write_str(tag)?;
2225 }
2226 Ok(())
2227 })
2228}
2229
2230fn render_rightside(
2233 cx: &Context<'_>,
2234 item: &clean::Item,
2235 render_mode: RenderMode,
2236) -> impl fmt::Display {
2237 let tcx = cx.tcx();
2238
2239 fmt::from_fn(move |w| {
2240 let const_stability = match render_mode {
2243 RenderMode::Normal => item.const_stability(tcx),
2244 RenderMode::ForDeref { .. } => None,
2245 };
2246 let src_href = cx.src_href(item);
2247 let stability = render_stability_since_raw_with_extra(
2248 item.stable_since(tcx),
2249 const_stability,
2250 if src_href.is_some() { "" } else { " rightside" },
2251 );
2252
2253 match (stability, src_href) {
2254 (Some(stability), Some(link)) => {
2255 write!(
2256 w,
2257 "<span class=\"rightside\">{stability} · <a class=\"src\" href=\"{link}\">Source</a></span>",
2258 )
2259 }
2260 (Some(stability), None) => {
2261 write!(w, "{stability}")
2262 }
2263 (None, Some(link)) => {
2264 write!(w, "<a class=\"src rightside\" href=\"{link}\">Source</a>")
2265 }
2266 (None, None) => Ok(()),
2267 }
2268 })
2269}
2270
2271fn render_impl_summary(
2272 cx: &Context<'_>,
2273 i: &Impl,
2274 parent: &clean::Item,
2275 show_def_docs: bool,
2276 use_absolute: Option<bool>,
2277 aliases: &[String],
2280 doc: Option<&str>,
2281 impl_is_empty: bool,
2282) -> impl fmt::Display {
2283 fmt::from_fn(move |w| {
2284 let inner_impl = i.inner_impl();
2285 let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
2286 let aliases = (!aliases.is_empty())
2287 .then_some(fmt::from_fn(|f| {
2288 write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f)))
2289 }))
2290 .maybe_display();
2291 write!(
2292 w,
2293 "<section id=\"{id}\" class=\"impl\"{aliases}>\
2294 {}\
2295 <a href=\"#{id}\" class=\"anchor\">§</a>\
2296 <h3 class=\"code-header\">",
2297 render_rightside(cx, &i.impl_item, RenderMode::Normal)
2298 )?;
2299
2300 if let Some(use_absolute) = use_absolute {
2301 write!(w, "{}", print_impl(inner_impl, use_absolute, cx))?;
2302 if show_def_docs {
2303 for it in &inner_impl.items {
2304 if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
2305 write!(
2306 w,
2307 "<div class=\"where\"> {};</div>",
2308 assoc_type(
2309 it,
2310 &tydef.generics,
2311 &[], Some(&tydef.type_),
2313 AssocItemLink::Anchor(None),
2314 0,
2315 cx,
2316 )
2317 )?;
2318 }
2319 }
2320 }
2321 } else {
2322 write!(w, "{}", print_impl(inner_impl, false, cx))?;
2323 }
2324 w.write_str("</h3>")?;
2325
2326 let is_trait = inner_impl.trait_.is_some();
2327 if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) {
2328 write!(
2329 w,
2330 "<span class=\"item-info\">\
2331 <div class=\"stab portability\">{portability}</div>\
2332 </span>",
2333 )?;
2334 }
2335
2336 if let Some(doc) = doc {
2337 if impl_is_empty {
2338 w.write_str(
2339 "<div class=\"item-info\">\
2340 <div class=\"stab empty-impl\">This impl block contains no items.</div>\
2341 </div>",
2342 )?;
2343 }
2344 write!(w, "<div class=\"docblock\">{doc}</div>")?;
2345 }
2346
2347 w.write_str("</section>")
2348 })
2349}
2350
2351pub(crate) fn small_url_encode(s: String) -> String {
2352 fn dont_escape(c: u8) -> bool {
2357 c.is_ascii_alphanumeric()
2358 || c == b'-'
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'='
2376 }
2377 let mut st = String::new();
2378 let mut last_match = 0;
2379 for (idx, b) in s.bytes().enumerate() {
2380 if dont_escape(b) {
2381 continue;
2382 }
2383
2384 if last_match != idx {
2385 st += &s[last_match..idx];
2387 }
2388 if b == b' ' {
2389 st += "+";
2393 } else {
2394 write!(st, "%{b:02X}").unwrap();
2395 }
2396 last_match = idx + 1;
2402 }
2403
2404 if last_match != 0 {
2405 st += &s[last_match..];
2406 st
2407 } else {
2408 s
2409 }
2410}
2411
2412fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String {
2413 use rustc_middle::ty::print::with_forced_trimmed_paths;
2414 let (type_, trait_) = match impl_id {
2415 ItemId::Auto { trait_, for_ } => {
2416 let ty = tcx.type_of(for_).skip_binder();
2417 (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
2418 }
2419 ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
2420 if let Some(trait_ref) = tcx.impl_opt_trait_ref(impl_id) {
2421 let trait_ref = trait_ref.skip_binder();
2422 (trait_ref.self_ty(), Some(trait_ref))
2423 } else {
2424 (tcx.type_of(impl_id).skip_binder(), None)
2425 }
2426 }
2427 };
2428 with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
2429 format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
2430 } else {
2431 format!("impl-{type_}")
2432 }))
2433}
2434
2435fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2436 match item.kind {
2437 clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
2438 Some((
2441 format!("{:#}", print_type(&i.for_, cx)),
2442 get_id_for_impl(cx.tcx(), item.item_id),
2443 ))
2444 }
2445 _ => None,
2446 }
2447}
2448
2449pub(crate) fn get_filtered_impls_for_reference<'a>(
2453 shared: &'a SharedContext<'_>,
2454 it: &clean::Item,
2455) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
2456 let def_id = it.item_id.expect_def_id();
2457 let Some(v) = shared.cache.impls.get(&def_id) else {
2459 return (Vec::new(), Vec::new(), Vec::new());
2460 };
2461 let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
2464 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
2465 traits.partition(|t| t.inner_impl().kind.is_auto());
2466
2467 let (blanket_impl, concrete): (Vec<&Impl>, _) =
2468 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
2469 let concrete: Vec<_> = concrete
2471 .into_iter()
2472 .filter(|t| match t.inner_impl().for_ {
2473 clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
2474 _ => false,
2475 })
2476 .collect();
2477
2478 (concrete, synthetic, blanket_impl)
2479}
2480
2481#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2482pub(crate) enum ItemSection {
2483 Reexports,
2484 PrimitiveTypes,
2485 Modules,
2486 Macros,
2487 Structs,
2488 Enums,
2489 Constants,
2490 Statics,
2491 Traits,
2492 Functions,
2493 TypeAliases,
2494 Unions,
2495 Implementations,
2496 TypeMethods,
2497 Methods,
2498 StructFields,
2499 Variants,
2500 AssociatedTypes,
2501 AssociatedConstants,
2502 ForeignTypes,
2503 Keywords,
2504 Attributes,
2505 AttributeMacros,
2506 DeriveMacros,
2507 TraitAliases,
2508}
2509
2510impl ItemSection {
2511 const ALL: &'static [Self] = {
2512 use ItemSection::*;
2513 &[
2516 Reexports,
2517 PrimitiveTypes,
2518 Modules,
2519 Macros,
2520 Structs,
2521 Enums,
2522 Constants,
2523 Statics,
2524 Traits,
2525 Functions,
2526 TypeAliases,
2527 Unions,
2528 Implementations,
2529 TypeMethods,
2530 Methods,
2531 StructFields,
2532 Variants,
2533 AssociatedTypes,
2534 AssociatedConstants,
2535 ForeignTypes,
2536 Keywords,
2537 Attributes,
2538 AttributeMacros,
2539 DeriveMacros,
2540 TraitAliases,
2541 ]
2542 };
2543
2544 fn id(self) -> &'static str {
2545 match self {
2546 Self::Reexports => "reexports",
2547 Self::Modules => "modules",
2548 Self::Structs => "structs",
2549 Self::Unions => "unions",
2550 Self::Enums => "enums",
2551 Self::Functions => "functions",
2552 Self::TypeAliases => "types",
2553 Self::Statics => "statics",
2554 Self::Constants => "constants",
2555 Self::Traits => "traits",
2556 Self::Implementations => "impls",
2557 Self::TypeMethods => "tymethods",
2558 Self::Methods => "methods",
2559 Self::StructFields => "fields",
2560 Self::Variants => "variants",
2561 Self::Macros => "macros",
2562 Self::PrimitiveTypes => "primitives",
2563 Self::AssociatedTypes => "associated-types",
2564 Self::AssociatedConstants => "associated-consts",
2565 Self::ForeignTypes => "foreign-types",
2566 Self::Keywords => "keywords",
2567 Self::Attributes => "attributes",
2568 Self::AttributeMacros => "attributes",
2569 Self::DeriveMacros => "derives",
2570 Self::TraitAliases => "trait-aliases",
2571 }
2572 }
2573
2574 fn name(self) -> &'static str {
2575 match self {
2576 Self::Reexports => "Re-exports",
2577 Self::Modules => "Modules",
2578 Self::Structs => "Structs",
2579 Self::Unions => "Unions",
2580 Self::Enums => "Enums",
2581 Self::Functions => "Functions",
2582 Self::TypeAliases => "Type Aliases",
2583 Self::Statics => "Statics",
2584 Self::Constants => "Constants",
2585 Self::Traits => "Traits",
2586 Self::Implementations => "Implementations",
2587 Self::TypeMethods => "Type Methods",
2588 Self::Methods => "Methods",
2589 Self::StructFields => "Struct Fields",
2590 Self::Variants => "Variants",
2591 Self::Macros => "Macros",
2592 Self::PrimitiveTypes => "Primitive Types",
2593 Self::AssociatedTypes => "Associated Types",
2594 Self::AssociatedConstants => "Associated Constants",
2595 Self::ForeignTypes => "Foreign Types",
2596 Self::Keywords => "Keywords",
2597 Self::Attributes => "Attributes",
2598 Self::AttributeMacros => "Attribute Macros",
2599 Self::DeriveMacros => "Derive Macros",
2600 Self::TraitAliases => "Trait Aliases",
2601 }
2602 }
2603}
2604
2605fn item_ty_to_section(ty: ItemType) -> ItemSection {
2606 match ty {
2607 ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
2608 ItemType::Module => ItemSection::Modules,
2609 ItemType::Struct => ItemSection::Structs,
2610 ItemType::Union => ItemSection::Unions,
2611 ItemType::Enum => ItemSection::Enums,
2612 ItemType::Function => ItemSection::Functions,
2613 ItemType::TypeAlias => ItemSection::TypeAliases,
2614 ItemType::Static => ItemSection::Statics,
2615 ItemType::Constant => ItemSection::Constants,
2616 ItemType::Trait => ItemSection::Traits,
2617 ItemType::Impl => ItemSection::Implementations,
2618 ItemType::TyMethod => ItemSection::TypeMethods,
2619 ItemType::Method => ItemSection::Methods,
2620 ItemType::StructField => ItemSection::StructFields,
2621 ItemType::Variant => ItemSection::Variants,
2622 ItemType::Macro => ItemSection::Macros,
2623 ItemType::Primitive => ItemSection::PrimitiveTypes,
2624 ItemType::AssocType => ItemSection::AssociatedTypes,
2625 ItemType::AssocConst => ItemSection::AssociatedConstants,
2626 ItemType::ForeignType => ItemSection::ForeignTypes,
2627 ItemType::Keyword => ItemSection::Keywords,
2628 ItemType::Attribute => ItemSection::Attributes,
2629 ItemType::ProcAttribute => ItemSection::AttributeMacros,
2630 ItemType::ProcDerive => ItemSection::DeriveMacros,
2631 ItemType::TraitAlias => ItemSection::TraitAliases,
2632 }
2633}
2634
2635fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec<String> {
2642 let mut out = Vec::new();
2643 let mut visited = FxHashSet::default();
2644 let mut work = VecDeque::new();
2645
2646 let mut process_path = |did: DefId| {
2647 let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
2648 let fqp = cache.exact_paths.get(&did).or_else(get_extern);
2649
2650 if let Some(path) = fqp {
2651 out.push(join_path_syms(path));
2652 }
2653 };
2654
2655 work.push_back(first_ty);
2656
2657 while let Some(ty) = work.pop_front() {
2658 if !visited.insert(ty) {
2659 continue;
2660 }
2661
2662 match ty {
2663 clean::Type::Path { path } => process_path(path.def_id()),
2664 clean::Type::Tuple(tys) => {
2665 work.extend(tys.iter());
2666 }
2667 clean::Type::Slice(ty) => {
2668 work.push_back(ty);
2669 }
2670 clean::Type::Array(ty, _) => {
2671 work.push_back(ty);
2672 }
2673 clean::Type::RawPointer(_, ty) => {
2674 work.push_back(ty);
2675 }
2676 clean::Type::BorrowedRef { type_, .. } => {
2677 work.push_back(type_);
2678 }
2679 clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
2680 work.push_back(self_type);
2681 if let Some(trait_) = trait_ {
2682 process_path(trait_.def_id());
2683 }
2684 }
2685 _ => {}
2686 }
2687 }
2688 out
2689}
2690
2691const MAX_FULL_EXAMPLES: usize = 5;
2692const NUM_VISIBLE_LINES: usize = 10;
2693
2694fn render_call_locations<W: fmt::Write>(
2696 mut w: W,
2697 cx: &Context<'_>,
2698 item: &clean::Item,
2699) -> fmt::Result {
2700 let tcx = cx.tcx();
2701 let def_id = item.item_id.expect_def_id();
2702 let key = tcx.def_path_hash(def_id);
2703 let Some(call_locations) = cx.shared.call_locations.get(&key) else { return Ok(()) };
2704
2705 let id = cx.derive_id("scraped-examples");
2707 write!(
2708 &mut w,
2709 "<div class=\"docblock scraped-example-list\">\
2710 <span></span>\
2711 <h5 id=\"{id}\">\
2712 <a href=\"#{id}\">Examples found in repository</a>\
2713 <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
2714 </h5>",
2715 root_path = cx.root_path(),
2716 id = id
2717 )?;
2718
2719 let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
2721 let (line_lo, line_hi) = loc.call_expr.line_span;
2722 let (anchor, title) = if line_lo == line_hi {
2723 ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
2724 } else {
2725 (
2726 format!("{}-{}", line_lo + 1, line_hi + 1),
2727 format!("lines {}-{}", line_lo + 1, line_hi + 1),
2728 )
2729 };
2730 let url = format!("{}{}#{anchor}", cx.root_path(), call_data.url);
2731 (url, title)
2732 };
2733
2734 let write_example = |w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
2736 let contents = match fs::read_to_string(path) {
2737 Ok(contents) => contents,
2738 Err(err) => {
2739 let span = item.span(tcx).map_or(DUMMY_SP, |span| span.inner());
2740 tcx.dcx().span_err(span, format!("failed to read file {}: {err}", path.display()));
2741 return false;
2742 }
2743 };
2744
2745 assert!(!call_data.locations.is_empty());
2748 let min_loc =
2749 call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2750 let byte_min = min_loc.enclosing_item.byte_span.0;
2751 let line_min = min_loc.enclosing_item.line_span.0;
2752 let max_loc =
2753 call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
2754 let byte_max = max_loc.enclosing_item.byte_span.1;
2755 let line_max = max_loc.enclosing_item.line_span.1;
2756
2757 let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
2759
2760 let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
2763 .locations
2764 .iter()
2765 .map(|loc| {
2766 let (byte_lo, byte_hi) = loc.call_ident.byte_span;
2767 let (line_lo, line_hi) = loc.call_expr.line_span;
2768 let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
2769
2770 let line_range = (line_lo - line_min, line_hi - line_min);
2771 let (line_url, line_title) = link_to_loc(call_data, loc);
2772
2773 (byte_range, (line_range, line_url, line_title))
2774 })
2775 .unzip();
2776
2777 let (_, init_url, init_title) = &line_ranges[0];
2778 let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
2779 let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
2780
2781 let source_map = tcx.sess.source_map();
2782 let files = source_map.files();
2783 let local = tcx.sess.local_crate_source_file().unwrap();
2784
2785 let get_file_start_pos = || {
2786 let crate_src = local.clone().into_local_path()?;
2787 let abs_crate_src = crate_src.canonicalize().ok()?;
2788 let crate_root = abs_crate_src.parent()?.parent()?;
2789 let rel_path = path.strip_prefix(crate_root).ok()?;
2790 files
2791 .iter()
2792 .find(|file| match &file.name {
2793 FileName::Real(real) => real.local_path().map_or(false, |p| p == rel_path),
2794 _ => false,
2795 })
2796 .map(|file| file.start_pos)
2797 };
2798
2799 let Some(file_span) = get_file_start_pos()
2802 .or_else(|| {
2803 files
2804 .iter()
2805 .find(|file| match &file.name {
2806 FileName::Real(file_name) => file_name == &local,
2807 _ => false,
2808 })
2809 .map(|file| file.start_pos)
2810 })
2811 .map(|start_pos| {
2812 rustc_span::Span::with_root_ctxt(
2813 start_pos + BytePos(byte_min),
2814 start_pos + BytePos(byte_max),
2815 )
2816 })
2817 else {
2818 return false;
2820 };
2821
2822 let mut decoration_info = FxIndexMap::default();
2823 decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
2824 decoration_info.insert("highlight", byte_ranges);
2825
2826 sources::print_src(
2827 w,
2828 contents_subset,
2829 file_span,
2830 cx,
2831 &cx.root_path(),
2832 &highlight::DecorationInfo(decoration_info),
2833 &sources::SourceContext::Embedded(sources::ScrapedInfo {
2834 needs_expansion,
2835 offset: line_min,
2836 name: &call_data.display_name,
2837 url: init_url,
2838 title: init_title,
2839 locations: locations_encoded,
2840 }),
2841 )
2842 .unwrap();
2843
2844 true
2845 };
2846
2847 let ordered_locations = {
2859 fn sort_criterion<'a>(
2860 (_, call_data): &(&PathBuf, &'a CallData),
2861 ) -> (bool, u32, &'a String) {
2862 let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2864 (!call_data.is_bin, hi - lo, &call_data.display_name)
2865 }
2866
2867 let mut locs = call_locations.iter().collect::<Vec<_>>();
2868 locs.sort_by_key(sort_criterion);
2869 locs
2870 };
2871
2872 let mut it = ordered_locations.into_iter().peekable();
2873
2874 let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
2877 for example in it.by_ref() {
2878 if write_example(&mut *w, example) {
2879 break;
2880 }
2881 }
2882 };
2883
2884 write_and_skip_failure(&mut w, &mut it);
2886
2887 if it.peek().is_some() {
2889 write!(
2890 w,
2891 "<details class=\"toggle more-examples-toggle\">\
2892 <summary class=\"hideme\">\
2893 <span>More examples</span>\
2894 </summary>\
2895 <div class=\"hide-more\">Hide additional examples</div>\
2896 <div class=\"more-scraped-examples\">\
2897 <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
2898 )?;
2899
2900 for _ in 0..MAX_FULL_EXAMPLES {
2903 write_and_skip_failure(&mut w, &mut it);
2904 }
2905
2906 if it.peek().is_some() {
2908 w.write_str(
2909 r#"<div class="example-links">Additional examples can be found in:<br><ul>"#,
2910 )?;
2911 it.try_for_each(|(_, call_data)| {
2912 let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
2913 write!(
2914 w,
2915 r#"<li><a href="{url}">{name}</a></li>"#,
2916 url = url,
2917 name = call_data.display_name
2918 )
2919 })?;
2920 w.write_str("</ul></div>")?;
2921 }
2922
2923 w.write_str("</div></details>")?;
2924 }
2925
2926 w.write_str("</div>")
2927}
2928
2929fn render_attributes_in_code(
2930 w: &mut impl fmt::Write,
2931 item: &clean::Item,
2932 prefix: &str,
2933 cx: &Context<'_>,
2934) -> fmt::Result {
2935 render_attributes_in_code_with_options(w, item, prefix, cx, true, "")
2936}
2937
2938pub(super) fn render_attributes_in_code_with_options(
2939 w: &mut impl fmt::Write,
2940 item: &clean::Item,
2941 prefix: &str,
2942 cx: &Context<'_>,
2943 render_doc_hidden: bool,
2944 open_tag: &str,
2945) -> fmt::Result {
2946 w.write_str(open_tag)?;
2947 if render_doc_hidden && item.is_doc_hidden() {
2948 render_code_attribute(prefix, "#[doc(hidden)]", w)?;
2949 }
2950 for attr in &item.attrs.other_attrs {
2951 let hir::Attribute::Parsed(kind) = attr else { continue };
2952 let attr = match kind {
2953 AttributeKind::LinkSection { name, .. } => {
2954 Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}"))))
2955 }
2956 AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"),
2957 AttributeKind::ExportName { name, .. } => {
2958 Cow::Owned(format!("#[unsafe(export_name = {})]", Escape(&format!("{name:?}"))))
2959 }
2960 AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"),
2961 _ => continue,
2962 };
2963 render_code_attribute(prefix, attr.as_ref(), w)?;
2964 }
2965
2966 if let Some(def_id) = item.def_id()
2967 && let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id)
2968 {
2969 render_code_attribute(prefix, &repr, w)?;
2970 }
2971 Ok(())
2972}
2973
2974fn render_repr_attribute_in_code(
2975 w: &mut impl fmt::Write,
2976 cx: &Context<'_>,
2977 def_id: DefId,
2978) -> fmt::Result {
2979 if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) {
2980 render_code_attribute("", &repr, w)?;
2981 }
2982 Ok(())
2983}
2984
2985fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) -> fmt::Result {
2986 write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>")
2987}
2988
2989fn repr_attribute<'tcx>(
2994 tcx: TyCtxt<'tcx>,
2995 cache: &Cache,
2996 def_id: DefId,
2997) -> Option<Cow<'static, str>> {
2998 let adt = match tcx.def_kind(def_id) {
2999 DefKind::Struct | DefKind::Enum | DefKind::Union => tcx.adt_def(def_id),
3000 _ => return None,
3001 };
3002 let repr = adt.repr();
3003
3004 let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id);
3005 let is_public_field = |field: &ty::FieldDef| {
3006 (cache.document_private || field.vis.is_public()) && is_visible(field.did)
3007 };
3008
3009 if repr.transparent() {
3010 let is_public = 'is_public: {
3013 let var = adt.variant(rustc_abi::FIRST_VARIANT); if !is_visible(var.def_id) {
3017 break 'is_public false;
3018 }
3019
3020 let non_1zst_field = var.fields.iter().find(|field| {
3022 let ty = ty::TypingEnv::post_analysis(tcx, field.did)
3023 .as_query_input(tcx.type_of(field.did).instantiate_identity());
3024 tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst())
3025 });
3026
3027 match non_1zst_field {
3028 Some(field) => is_public_field(field),
3029 None => var.fields.is_empty() || var.fields.iter().any(is_public_field),
3030 }
3031 };
3032
3033 return is_public.then(|| "#[repr(transparent)]".into());
3036 }
3037
3038 if !repr.c()
3042 && !repr.simd()
3043 && repr.int.is_none()
3044 && repr.pack.is_none()
3045 && repr.align.is_none()
3046 {
3047 return None;
3048 }
3049
3050 let is_public = adt
3052 .variants()
3053 .iter()
3054 .all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field));
3055 if !is_public {
3056 return None;
3057 }
3058
3059 let mut result = Vec::<Cow<'_, _>>::new();
3060
3061 if repr.c() {
3062 result.push("C".into());
3063 }
3064 if repr.simd() {
3065 result.push("simd".into());
3066 }
3067 if let Some(int) = repr.int {
3068 let prefix = if int.is_signed() { 'i' } else { 'u' };
3069 let int = match int {
3070 rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"),
3071 rustc_abi::IntegerType::Fixed(int, _) => {
3072 format!("{prefix}{}", int.size().bytes() * 8)
3073 }
3074 };
3075 result.push(int.into());
3076 }
3077
3078 if let Some(pack) = repr.pack {
3080 result.push(format!("packed({})", pack.bytes()).into());
3081 }
3082 if let Some(align) = repr.align {
3083 result.push(format!("align({})", align.bytes()).into());
3084 }
3085
3086 (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into())
3087}