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