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