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: &[&Impl],
1401 synthetic: &[&Impl],
1402 blanket_impl: &[&Impl],
1403) -> fmt::Result {
1404 let impls = {
1405 let mut buf = String::new();
1406 render_impls(cx, &mut buf, concrete, 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 !synthetic.is_empty() {
1418 write!(
1419 w,
1420 "{}<div id=\"synthetic-implementations-list\">",
1421 write_impl_section_heading("Auto Trait Implementations", "synthetic-implementations",)
1422 )?;
1423 render_impls(cx, &mut w, synthetic, containing_item, false)?;
1424 w.write_str("</div>")?;
1425 }
1426
1427 if !blanket_impl.is_empty() {
1428 write!(
1429 w,
1430 "{}<div id=\"blanket-implementations-list\">",
1431 write_impl_section_heading("Blanket Implementations", "blanket-implementations")
1432 )?;
1433 render_impls(cx, &mut w, blanket_impl, containing_item, false)?;
1434 w.write_str("</div>")?;
1435 }
1436 Ok(())
1437}
1438
1439fn render_assoc_items(
1440 cx: &Context<'_>,
1441 containing_item: &clean::Item,
1442 it: DefId,
1443 what: AssocItemRender<'_>,
1444) -> impl fmt::Display {
1445 fmt::from_fn(move |f| {
1446 let mut derefs = DefIdSet::default();
1447 derefs.insert(it);
1448 render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs)
1449 })
1450}
1451
1452fn render_assoc_items_inner(
1453 mut w: &mut dyn fmt::Write,
1454 cx: &Context<'_>,
1455 containing_item: &clean::Item,
1456 it: DefId,
1457 what: AssocItemRender<'_>,
1458 derefs: &mut DefIdSet,
1459) -> fmt::Result {
1460 info!("Documenting associated items of {:?}", containing_item.name);
1461 let cache = &cx.shared.cache;
1462 let Some(v) = cache.impls.get(&it) else { return Ok(()) };
1463 let (mut non_trait, traits): (Vec<_>, _) =
1464 v.iter().partition(|i| i.inner_impl().trait_.is_none());
1465 if !non_trait.is_empty() {
1466 let render_mode = what.render_mode();
1467 let class_html = what
1468 .class()
1469 .map(|class| fmt::from_fn(move |f| write!(f, r#" class="{class}""#)))
1470 .maybe_display();
1471 let (section_heading, id) = match what {
1472 AssocItemRender::All => (
1473 Either::Left(write_impl_section_heading("Implementations", "implementations")),
1474 Cow::Borrowed("implementations-list"),
1475 ),
1476 AssocItemRender::DerefFor { trait_, type_, .. } => {
1477 let id = cx.derive_id(small_url_encode(format!(
1478 "deref-methods-{:#}",
1479 print_type(type_, cx)
1480 )));
1481 non_trait.retain(|impl_| {
1489 type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache)
1490 });
1491 let derived_id = cx.derive_id(&id);
1492 if let Some(def_id) = type_.def_id(cx.cache()) {
1493 cx.deref_id_map.borrow_mut().insert(def_id, id.clone());
1494 }
1495 (
1496 Either::Right(fmt::from_fn(move |f| {
1497 write!(
1498 f,
1499 "<details class=\"toggle big-toggle\" open><summary>{}</summary>",
1500 write_impl_section_heading(
1501 fmt::from_fn(|f| write!(
1502 f,
1503 "<span>Methods from {trait_}<Target = {type_}></span>",
1504 trait_ = print_path(trait_, cx),
1505 type_ = print_type(type_, cx),
1506 )),
1507 &id,
1508 )
1509 )
1510 })),
1511 Cow::Owned(derived_id),
1512 )
1513 }
1514 };
1515 let impls_buf = fmt::from_fn(|f| {
1516 non_trait
1517 .iter()
1518 .map(|i| {
1519 render_impl(
1520 cx,
1521 i,
1522 containing_item,
1523 AssocItemLink::Anchor(None),
1524 render_mode,
1525 None,
1526 &[],
1527 ImplRenderingParameters {
1528 show_def_docs: true,
1529 show_default_items: true,
1530 show_non_assoc_items: true,
1531 toggle_open_by_default: true,
1532 },
1533 )
1534 })
1535 .joined("", f)
1536 })
1537 .to_string();
1538
1539 if !impls_buf.is_empty() {
1540 write!(
1541 w,
1542 "{section_heading}<div id=\"{id}\"{class_html}>{impls_buf}</div>{}",
1543 matches!(what, AssocItemRender::DerefFor { .. })
1544 .then_some("</details>")
1545 .maybe_display(),
1546 )?;
1547 }
1548 }
1549
1550 if !traits.is_empty() {
1551 let deref_impl = traits.iter().find(|t| {
1552 t.trait_did() == cx.tcx().lang_items().deref_trait() && !t.is_negative_trait_impl()
1553 });
1554 if let Some(impl_) = deref_impl {
1555 let has_deref_mut =
1556 traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
1557 render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs)?;
1558 }
1559
1560 if let AssocItemRender::DerefFor { .. } = what {
1563 return Ok(());
1564 }
1565
1566 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
1567 traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
1568 let (blanket_impl, concrete): (Vec<&Impl>, _) =
1569 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
1570
1571 render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl)?;
1572 }
1573 Ok(())
1574}
1575
1576fn render_deref_methods(
1578 mut w: impl Write,
1579 cx: &Context<'_>,
1580 impl_: &Impl,
1581 container_item: &clean::Item,
1582 deref_mut: bool,
1583 derefs: &mut DefIdSet,
1584) -> fmt::Result {
1585 let cache = cx.cache();
1586 let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
1587 let (target, real_target) = impl_
1588 .inner_impl()
1589 .items
1590 .iter()
1591 .find_map(|item| match item.kind {
1592 clean::AssocTypeItem(ref t, _) => Some(match *t {
1593 clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
1594 _ => (&t.type_, &t.type_),
1595 }),
1596 _ => None,
1597 })
1598 .expect("Expected associated type binding");
1599 debug!(
1600 "Render deref methods for {for_:#?}, target {target:#?}",
1601 for_ = impl_.inner_impl().for_
1602 );
1603 let what =
1604 AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
1605 if let Some(did) = target.def_id(cache) {
1606 if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) {
1607 if did == type_did || !derefs.insert(did) {
1609 return Ok(());
1611 }
1612 }
1613 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1614 } else if let Some(prim) = target.primitive_type()
1615 && let Some(&did) = cache.primitive_locations.get(&prim)
1616 {
1617 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1618 }
1619 Ok(())
1620}
1621
1622fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool {
1623 let self_type_opt = match item.kind {
1624 clean::MethodItem(ref method, _) => method.decl.receiver_type(),
1625 clean::RequiredMethodItem(ref method, _) => method.decl.receiver_type(),
1626 _ => None,
1627 };
1628
1629 if let Some(self_ty) = self_type_opt {
1630 let (by_mut_ref, by_box, by_value) = match *self_ty {
1631 clean::Type::BorrowedRef { mutability, .. } => {
1632 (mutability == Mutability::Mut, false, false)
1633 }
1634 clean::Type::Path { ref path } => {
1635 (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false)
1636 }
1637 clean::Type::SelfTy => (false, false, true),
1638 _ => (false, false, false),
1639 };
1640
1641 (deref_mut_ || !by_mut_ref) && !by_box && !by_value
1642 } else {
1643 false
1644 }
1645}
1646
1647fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option<impl fmt::Display> {
1648 if ty.is_unit() {
1649 return None;
1651 }
1652
1653 let did = ty.def_id(cx.cache())?;
1654
1655 if Some(did) == cx.tcx().lang_items().owned_box()
1660 || Some(did) == cx.tcx().lang_items().pin_type()
1661 {
1662 return None;
1663 }
1664
1665 let impls = cx.cache().impls.get(&did)?;
1666 let has_notable_trait = impls
1667 .iter()
1668 .map(Impl::inner_impl)
1669 .filter(|impl_| {
1670 impl_.polarity == ty::ImplPolarity::Positive
1671 && ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1674 })
1675 .filter_map(|impl_| impl_.trait_.as_ref())
1676 .filter_map(|trait_| cx.cache().traits.get(&trait_.def_id()))
1677 .any(|t| t.is_notable_trait(cx.tcx()));
1678
1679 has_notable_trait.then(|| {
1680 cx.types_with_notable_traits.borrow_mut().insert(ty.clone());
1681 fmt::from_fn(|f| {
1682 write!(
1683 f,
1684 " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>",
1685 ty = Escape(&format!("{:#}", print_type(ty, cx))),
1686 )
1687 })
1688 })
1689}
1690
1691fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
1692 let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
1693
1694 let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");
1695
1696 let out = fmt::from_fn(|f| {
1697 let mut notable_impls = impls
1698 .iter()
1699 .map(|impl_| impl_.inner_impl())
1700 .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive)
1701 .filter(|impl_| {
1702 ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1704 })
1705 .filter_map(|impl_| {
1706 if let Some(trait_) = &impl_.trait_
1707 && let trait_did = trait_.def_id()
1708 && let Some(trait_) = cx.cache().traits.get(&trait_did)
1709 && trait_.is_notable_trait(cx.tcx())
1710 {
1711 Some((impl_, trait_did))
1712 } else {
1713 None
1714 }
1715 })
1716 .peekable();
1717
1718 let has_notable_impl = if let Some((impl_, _)) = notable_impls.peek() {
1719 write!(
1720 f,
1721 "<h3>Notable traits for <code>{}</code></h3>\
1722 <pre><code>",
1723 print_type(&impl_.for_, cx),
1724 )?;
1725 true
1726 } else {
1727 false
1728 };
1729
1730 for (impl_, trait_did) in notable_impls {
1731 write!(f, "<div class=\"where\">{}</div>", print_impl(impl_, false, cx))?;
1732 for it in &impl_.items {
1733 let clean::AssocTypeItem(tydef, ..) = &it.kind else {
1734 continue;
1735 };
1736
1737 let empty_set = FxIndexSet::default();
1738 let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
1739
1740 write!(
1741 f,
1742 "<div class=\"where\"> {};</div>",
1743 assoc_type(
1744 it,
1745 &tydef.generics,
1746 &[], Some(&tydef.type_),
1748 src_link,
1749 0,
1750 cx,
1751 )
1752 )?;
1753 }
1754 }
1755
1756 if !has_notable_impl {
1757 f.write_str("</code></pre>")?;
1758 }
1759
1760 Ok(())
1761 })
1762 .to_string();
1763
1764 (format!("{:#}", print_type(ty, cx)), out)
1765}
1766
1767fn notable_traits_json<'a>(tys: impl Iterator<Item = &'a clean::Type>, cx: &Context<'_>) -> String {
1768 let mut mp = tys.map(|ty| notable_traits_decl(ty, cx)).collect::<IndexMap<_, _>>();
1769 mp.sort_unstable_keys();
1770 serde_json::to_string(&mp).expect("serialize (string, string) -> json object cannot fail")
1771}
1772
1773#[derive(Clone, Copy, Debug)]
1774struct ImplRenderingParameters {
1775 show_def_docs: bool,
1776 show_default_items: bool,
1777 show_non_assoc_items: bool,
1779 toggle_open_by_default: bool,
1780}
1781
1782fn render_impl(
1783 cx: &Context<'_>,
1784 i: &Impl,
1785 parent: &clean::Item,
1786 link: AssocItemLink<'_>,
1787 render_mode: RenderMode,
1788 use_absolute: Option<bool>,
1789 aliases: &[String],
1790 rendering_params: ImplRenderingParameters,
1791) -> impl fmt::Display {
1792 fmt::from_fn(move |w| {
1793 let cache = &cx.shared.cache;
1794 let traits = &cache.traits;
1795 let trait_ = i.trait_did().map(|did| &traits[&did]);
1796 let mut close_tags = <Vec<&str>>::with_capacity(2);
1797
1798 fn doc_impl_item(
1804 boring: impl fmt::Write,
1805 interesting: impl fmt::Write,
1806 cx: &Context<'_>,
1807 item: &clean::Item,
1808 parent: &clean::Item,
1809 link: AssocItemLink<'_>,
1810 render_mode: RenderMode,
1811 is_default_item: bool,
1812 trait_: Option<&clean::Trait>,
1813 rendering_params: ImplRenderingParameters,
1814 ) -> fmt::Result {
1815 let item_type = item.type_();
1816 let name = item.name.as_ref().unwrap();
1817
1818 let render_method_item = rendering_params.show_non_assoc_items
1819 && match render_mode {
1820 RenderMode::Normal => true,
1821 RenderMode::ForDeref { mut_: deref_mut_ } => {
1822 should_render_item(item, deref_mut_, cx.tcx())
1823 }
1824 };
1825
1826 let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
1827
1828 let mut doc_buffer = String::new();
1829 let mut info_buffer = String::new();
1830 let mut short_documented = true;
1831
1832 let mut trait_item_deprecated = false;
1833 if render_method_item {
1834 if !is_default_item {
1835 if let Some(t) = trait_ {
1836 if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1839 trait_item_deprecated = it.is_deprecated(cx.tcx());
1840 if !item.doc_value().is_empty() {
1843 document_item_info(cx, it, Some(parent))
1844 .render_into(&mut info_buffer)?;
1845 doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1846 short_documented = false;
1847 } else {
1848 doc_buffer = document_short(
1851 it,
1852 cx,
1853 link,
1854 parent,
1855 rendering_params.show_def_docs,
1856 )
1857 .to_string();
1858 }
1859 }
1860 } else {
1861 document_item_info(cx, item, Some(parent)).render_into(&mut info_buffer)?;
1862 if rendering_params.show_def_docs {
1863 doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1864 short_documented = false;
1865 }
1866 }
1867 } else {
1868 doc_buffer =
1869 document_short(item, cx, link, parent, rendering_params.show_def_docs)
1870 .to_string();
1871 }
1872 }
1873 let mut w = if short_documented && trait_.is_some() {
1874 Either::Left(interesting)
1875 } else {
1876 Either::Right(boring)
1877 };
1878
1879 let mut deprecation_class = if trait_item_deprecated || item.is_deprecated(cx.tcx()) {
1880 " deprecated"
1881 } else {
1882 ""
1883 };
1884
1885 let toggled = !doc_buffer.is_empty();
1886 if toggled {
1887 let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
1888 write!(
1889 w,
1890 "<details class=\"toggle{method_toggle_class}{deprecation_class}\" open><summary>"
1891 )?;
1892 deprecation_class = "";
1893 }
1894 match &item.kind {
1895 clean::MethodItem(..) | clean::RequiredMethodItem(..) => {
1896 if render_method_item {
1898 let id = cx.derive_id(format!("{item_type}.{name}"));
1899 let source_id = trait_
1900 .and_then(|trait_| {
1901 trait_
1902 .items
1903 .iter()
1904 .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
1905 })
1906 .map(|item| format!("{}.{name}", item.type_()));
1907 write!(
1908 w,
1909 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1910 {}",
1911 render_rightside(cx, item, render_mode)
1912 )?;
1913 if trait_.is_some() {
1914 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1916 }
1917 write!(
1918 w,
1919 "<h4 class=\"code-header\">{}</h4></section>",
1920 render_assoc_item(
1921 item,
1922 link.anchor(source_id.as_ref().unwrap_or(&id)),
1923 ItemType::Impl,
1924 cx,
1925 render_mode,
1926 ),
1927 )?;
1928 }
1929 }
1930 clean::RequiredAssocConstItem(generics, ty) => {
1931 let source_id = format!("{item_type}.{name}");
1932 let id = cx.derive_id(&source_id);
1933 write!(
1934 w,
1935 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1936 {}",
1937 render_rightside(cx, item, render_mode)
1938 )?;
1939 if trait_.is_some() {
1940 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1942 }
1943 write!(
1944 w,
1945 "<h4 class=\"code-header\">{}</h4></section>",
1946 assoc_const(
1947 item,
1948 generics,
1949 ty,
1950 AssocConstValue::None,
1951 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1952 0,
1953 cx,
1954 ),
1955 )?;
1956 }
1957 clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
1958 let source_id = format!("{item_type}.{name}");
1959 let id = cx.derive_id(&source_id);
1960 write!(
1961 w,
1962 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1963 {}",
1964 render_rightside(cx, item, render_mode),
1965 )?;
1966 if trait_.is_some() {
1967 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1969 }
1970 write!(
1971 w,
1972 "<h4 class=\"code-header\">{}</h4></section>",
1973 assoc_const(
1974 item,
1975 &ci.generics,
1976 &ci.type_,
1977 match item.kind {
1978 clean::ProvidedAssocConstItem(_) =>
1979 AssocConstValue::TraitDefault(&ci.kind),
1980 clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
1981 _ => unreachable!(),
1982 },
1983 link.anchor(if trait_.is_some() { &source_id } else { &id }),
1984 0,
1985 cx,
1986 ),
1987 )?;
1988 }
1989 clean::RequiredAssocTypeItem(generics, bounds) => {
1990 let source_id = format!("{item_type}.{name}");
1991 let id = cx.derive_id(&source_id);
1992 write!(
1993 w,
1994 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1995 {}",
1996 render_rightside(cx, item, render_mode),
1997 )?;
1998 if trait_.is_some() {
1999 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
2001 }
2002 write!(
2003 w,
2004 "<h4 class=\"code-header\">{}</h4></section>",
2005 assoc_type(
2006 item,
2007 generics,
2008 bounds,
2009 None,
2010 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2011 0,
2012 cx,
2013 ),
2014 )?;
2015 }
2016 clean::AssocTypeItem(tydef, _bounds) => {
2017 let source_id = format!("{item_type}.{name}");
2018 let id = cx.derive_id(&source_id);
2019 write!(
2020 w,
2021 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
2022 {}",
2023 render_rightside(cx, item, render_mode),
2024 )?;
2025 if trait_.is_some() {
2026 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
2028 }
2029 write!(
2030 w,
2031 "<h4 class=\"code-header\">{}</h4></section>",
2032 assoc_type(
2033 item,
2034 &tydef.generics,
2035 &[], Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
2037 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2038 0,
2039 cx,
2040 ),
2041 )?;
2042 }
2043 clean::StrippedItem(..) => return Ok(()),
2044 _ => panic!("can't make docs for trait item with name {:?}", item.name),
2045 }
2046
2047 w.write_str(&info_buffer)?;
2048 if toggled {
2049 write!(w, "</summary>{doc_buffer}</details>")?;
2050 }
2051 Ok(())
2052 }
2053
2054 let mut impl_items = String::new();
2055 let mut default_impl_items = String::new();
2056 let impl_ = i.inner_impl();
2057
2058 let mut assoc_types = Vec::new();
2068 let mut methods = Vec::new();
2069
2070 if !impl_.is_negative_trait_impl() {
2071 for impl_item in &impl_.items {
2072 match impl_item.kind {
2073 clean::MethodItem(..) | clean::RequiredMethodItem(..) => {
2074 methods.push(impl_item)
2075 }
2076 clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => {
2077 assoc_types.push(impl_item)
2078 }
2079 clean::RequiredAssocConstItem(..)
2080 | clean::ProvidedAssocConstItem(_)
2081 | clean::ImplAssocConstItem(_) => {
2082 doc_impl_item(
2084 &mut default_impl_items,
2085 &mut impl_items,
2086 cx,
2087 impl_item,
2088 if trait_.is_some() { &i.impl_item } else { parent },
2089 link,
2090 render_mode,
2091 false,
2092 trait_,
2093 rendering_params,
2094 )?;
2095 }
2096 _ => {}
2097 }
2098 }
2099
2100 for assoc_type in assoc_types {
2101 doc_impl_item(
2102 &mut default_impl_items,
2103 &mut impl_items,
2104 cx,
2105 assoc_type,
2106 if trait_.is_some() { &i.impl_item } else { parent },
2107 link,
2108 render_mode,
2109 false,
2110 trait_,
2111 rendering_params,
2112 )?;
2113 }
2114 for method in methods {
2115 doc_impl_item(
2116 &mut default_impl_items,
2117 &mut impl_items,
2118 cx,
2119 method,
2120 if trait_.is_some() { &i.impl_item } else { parent },
2121 link,
2122 render_mode,
2123 false,
2124 trait_,
2125 rendering_params,
2126 )?;
2127 }
2128 }
2129
2130 fn render_default_items(
2131 mut boring: impl fmt::Write,
2132 mut interesting: impl fmt::Write,
2133 cx: &Context<'_>,
2134 t: &clean::Trait,
2135 i: &clean::Impl,
2136 parent: &clean::Item,
2137 render_mode: RenderMode,
2138 rendering_params: ImplRenderingParameters,
2139 ) -> fmt::Result {
2140 for trait_item in &t.items {
2141 if let Some(impl_def_id) = parent.item_id.as_def_id()
2144 && let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
2145 && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id))
2146 {
2147 continue;
2148 }
2149
2150 let n = trait_item.name;
2151 if i.items.iter().any(|m| m.name == n) {
2152 continue;
2153 }
2154 let did = i.trait_.as_ref().unwrap().def_id();
2155 let provided_methods = i.provided_trait_methods(cx.tcx());
2156 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
2157
2158 doc_impl_item(
2159 &mut boring,
2160 &mut interesting,
2161 cx,
2162 trait_item,
2163 parent,
2164 assoc_link,
2165 render_mode,
2166 true,
2167 Some(t),
2168 rendering_params,
2169 )?;
2170 }
2171 Ok(())
2172 }
2173
2174 if rendering_params.show_default_items
2179 && let Some(t) = trait_
2180 && !impl_.is_negative_trait_impl()
2181 {
2182 render_default_items(
2183 &mut default_impl_items,
2184 &mut impl_items,
2185 cx,
2186 t,
2187 impl_,
2188 &i.impl_item,
2189 render_mode,
2190 rendering_params,
2191 )?;
2192 }
2193 if render_mode == RenderMode::Normal {
2194 let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
2195 let deprecation_attr = if impl_.is_deprecated
2196 || trait_.is_some_and(|trait_| trait_.is_deprecated(cx.tcx()))
2197 {
2198 " deprecated"
2199 } else {
2200 ""
2201 };
2202 if toggled {
2203 close_tags.push("</details>");
2204 write!(
2205 w,
2206 "<details class=\"toggle implementors-toggle{deprecation_attr}\"{}>\
2207 <summary>",
2208 if rendering_params.toggle_open_by_default { " open" } else { "" }
2209 )?;
2210 }
2211
2212 let (before_dox, after_dox) = i
2213 .impl_item
2214 .opt_doc_value()
2215 .map(|dox| {
2216 Markdown {
2217 content: &dox,
2218 links: &i.impl_item.links(cx),
2219 ids: &mut cx.id_map.borrow_mut(),
2220 error_codes: cx.shared.codes,
2221 edition: cx.shared.edition(),
2222 playground: &cx.shared.playground,
2223 heading_offset: HeadingOffset::H4,
2224 }
2225 .split_summary_and_content()
2226 })
2227 .unwrap_or((None, None));
2228
2229 write!(
2230 w,
2231 "{}",
2232 render_impl_summary(
2233 cx,
2234 i,
2235 parent,
2236 rendering_params.show_def_docs,
2237 use_absolute,
2238 aliases,
2239 before_dox.as_deref(),
2240 trait_.is_none() && impl_.items.is_empty(),
2241 )
2242 )?;
2243 if toggled {
2244 w.write_str("</summary>")?;
2245 }
2246
2247 if before_dox.is_some()
2248 && let Some(after_dox) = after_dox
2249 {
2250 write!(w, "<div class=\"docblock\">{after_dox}</div>")?;
2251 }
2252
2253 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2254 w.write_str("<div class=\"impl-items\">")?;
2255 close_tags.push("</div>");
2256 }
2257 }
2258 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2259 w.write_str(&default_impl_items)?;
2260 w.write_str(&impl_items)?;
2261 }
2262 for tag in close_tags.into_iter().rev() {
2263 w.write_str(tag)?;
2264 }
2265 Ok(())
2266 })
2267}
2268
2269fn render_rightside(
2272 cx: &Context<'_>,
2273 item: &clean::Item,
2274 render_mode: RenderMode,
2275) -> impl fmt::Display {
2276 let tcx = cx.tcx();
2277
2278 fmt::from_fn(move |w| {
2279 let const_stability = match render_mode {
2282 RenderMode::Normal => item.const_stability(tcx),
2283 RenderMode::ForDeref { .. } => None,
2284 };
2285 let src_href = cx.src_href(item);
2286 let stability = render_stability_since_raw_with_extra(
2287 item.stable_since(tcx),
2288 const_stability,
2289 if src_href.is_some() { "" } else { " rightside" },
2290 );
2291
2292 match (stability, src_href) {
2293 (Some(stability), Some(link)) => {
2294 write!(
2295 w,
2296 "<span class=\"rightside\">{stability} · <a class=\"src\" href=\"{link}\">Source</a></span>",
2297 )
2298 }
2299 (Some(stability), None) => {
2300 write!(w, "{stability}")
2301 }
2302 (None, Some(link)) => {
2303 write!(w, "<a class=\"src rightside\" href=\"{link}\">Source</a>")
2304 }
2305 (None, None) => Ok(()),
2306 }
2307 })
2308}
2309
2310fn render_impl_summary(
2311 cx: &Context<'_>,
2312 i: &Impl,
2313 parent: &clean::Item,
2314 show_def_docs: bool,
2315 use_absolute: Option<bool>,
2316 aliases: &[String],
2319 doc: Option<&str>,
2320 impl_is_empty: bool,
2321) -> impl fmt::Display {
2322 fmt::from_fn(move |w| {
2323 let inner_impl = i.inner_impl();
2324 let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
2325 let aliases = (!aliases.is_empty())
2326 .then_some(fmt::from_fn(|f| {
2327 write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f)))
2328 }))
2329 .maybe_display();
2330 write!(
2331 w,
2332 "<section id=\"{id}\" class=\"impl\"{aliases}>\
2333 {}\
2334 <a href=\"#{id}\" class=\"anchor\">§</a>\
2335 <h3 class=\"code-header\">",
2336 render_rightside(cx, &i.impl_item, RenderMode::Normal)
2337 )?;
2338
2339 if let Some(use_absolute) = use_absolute {
2340 write!(w, "{}", print_impl(inner_impl, use_absolute, cx))?;
2341 if show_def_docs {
2342 for it in &inner_impl.items {
2343 if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
2344 write!(
2345 w,
2346 "<div class=\"where\"> {};</div>",
2347 assoc_type(
2348 it,
2349 &tydef.generics,
2350 &[], Some(&tydef.type_),
2352 AssocItemLink::Anchor(None),
2353 0,
2354 cx,
2355 )
2356 )?;
2357 }
2358 }
2359 }
2360 } else {
2361 write!(w, "{}", print_impl(inner_impl, false, cx))?;
2362 }
2363 w.write_str("</h3>")?;
2364
2365 let is_trait = inner_impl.trait_.is_some();
2366 if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) {
2367 write!(
2368 w,
2369 "<span class=\"item-info\">\
2370 <div class=\"stab portability\">{portability}</div>\
2371 </span>",
2372 )?;
2373 }
2374
2375 if let Some(doc) = doc {
2376 if impl_is_empty {
2377 w.write_str(
2378 "\
2379<div class=\"item-info\">\
2380 <div class=\"stab empty-impl\">This impl block contains no public items.</div>\
2381</div>",
2382 )?;
2383 }
2384 write!(w, "<div class=\"docblock\">{doc}</div>")?;
2385 }
2386
2387 w.write_str("</section>")
2388 })
2389}
2390
2391pub(crate) fn small_url_encode(s: String) -> String {
2392 fn dont_escape(c: u8) -> bool {
2397 c.is_ascii_alphanumeric()
2398 || c == b'-'
2399 || c == b'_'
2400 || c == b'.'
2401 || c == b','
2402 || c == b'~'
2403 || c == b'!'
2404 || c == b'\''
2405 || c == b'('
2406 || c == b')'
2407 || c == b'*'
2408 || c == b'/'
2409 || c == b';'
2410 || c == b':'
2411 || c == b'?'
2412 || c == b'='
2416 }
2417 let mut st = String::new();
2418 let mut last_match = 0;
2419 for (idx, b) in s.bytes().enumerate() {
2420 if dont_escape(b) {
2421 continue;
2422 }
2423
2424 if last_match != idx {
2425 st += &s[last_match..idx];
2427 }
2428 if b == b' ' {
2429 st += "+";
2433 } else {
2434 write!(st, "%{b:02X}").unwrap();
2435 }
2436 last_match = idx + 1;
2442 }
2443
2444 if last_match != 0 {
2445 st += &s[last_match..];
2446 st
2447 } else {
2448 s
2449 }
2450}
2451
2452fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String {
2453 use rustc_middle::ty::print::with_forced_trimmed_paths;
2454 let (type_, trait_) = match impl_id {
2455 ItemId::Auto { trait_, for_ } => {
2456 let ty = tcx.type_of(for_).skip_binder();
2457 (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
2458 }
2459 ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
2460 if let Some(trait_ref) = tcx.impl_opt_trait_ref(impl_id) {
2461 let trait_ref = trait_ref.skip_binder();
2462 (trait_ref.self_ty(), Some(trait_ref))
2463 } else {
2464 (tcx.type_of(impl_id).skip_binder(), None)
2465 }
2466 }
2467 };
2468 with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
2469 format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
2470 } else {
2471 format!("impl-{type_}")
2472 }))
2473}
2474
2475fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2476 match item.kind {
2477 clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
2478 Some((
2481 format!("{:#}", print_type(&i.for_, cx)),
2482 get_id_for_impl(cx.tcx(), item.item_id),
2483 ))
2484 }
2485 _ => None,
2486 }
2487}
2488
2489pub(crate) fn get_filtered_impls_for_reference<'a>(
2493 shared: &'a SharedContext<'_>,
2494 it: &clean::Item,
2495) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
2496 let def_id = it.item_id.expect_def_id();
2497 let Some(v) = shared.cache.impls.get(&def_id) else {
2499 return (Vec::new(), Vec::new(), Vec::new());
2500 };
2501 let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
2504 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
2505 traits.partition(|t| t.inner_impl().kind.is_auto());
2506
2507 let (blanket_impl, concrete): (Vec<&Impl>, _) =
2508 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
2509 let concrete: Vec<_> = concrete
2511 .into_iter()
2512 .filter(|t| match t.inner_impl().for_ {
2513 clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
2514 _ => false,
2515 })
2516 .collect();
2517
2518 (concrete, synthetic, blanket_impl)
2519}
2520
2521#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2522pub(crate) enum ItemSection {
2523 Reexports,
2524 PrimitiveTypes,
2525 Modules,
2526 Macros,
2527 Structs,
2528 Enums,
2529 Constants,
2530 Statics,
2531 Traits,
2532 Functions,
2533 TypeAliases,
2534 Unions,
2535 Implementations,
2536 TypeMethods,
2537 Methods,
2538 StructFields,
2539 Variants,
2540 AssociatedTypes,
2541 AssociatedConstants,
2542 ForeignTypes,
2543 Keywords,
2544 Attributes,
2545 AttributeMacros,
2546 DeriveMacros,
2547 TraitAliases,
2548}
2549
2550impl ItemSection {
2551 const ALL: &'static [Self] = {
2552 use ItemSection::*;
2553 &[
2556 Reexports,
2557 PrimitiveTypes,
2558 Modules,
2559 Macros,
2560 Structs,
2561 Enums,
2562 Constants,
2563 Statics,
2564 Traits,
2565 Functions,
2566 TypeAliases,
2567 Unions,
2568 Implementations,
2569 TypeMethods,
2570 Methods,
2571 StructFields,
2572 Variants,
2573 AssociatedTypes,
2574 AssociatedConstants,
2575 ForeignTypes,
2576 Keywords,
2577 Attributes,
2578 AttributeMacros,
2579 DeriveMacros,
2580 TraitAliases,
2581 ]
2582 };
2583
2584 fn id(self) -> &'static str {
2585 match self {
2586 Self::Reexports => "reexports",
2587 Self::Modules => "modules",
2588 Self::Structs => "structs",
2589 Self::Unions => "unions",
2590 Self::Enums => "enums",
2591 Self::Functions => "functions",
2592 Self::TypeAliases => "types",
2593 Self::Statics => "statics",
2594 Self::Constants => "constants",
2595 Self::Traits => "traits",
2596 Self::Implementations => "impls",
2597 Self::TypeMethods => "tymethods",
2598 Self::Methods => "methods",
2599 Self::StructFields => "fields",
2600 Self::Variants => "variants",
2601 Self::Macros => "macros",
2602 Self::PrimitiveTypes => "primitives",
2603 Self::AssociatedTypes => "associated-types",
2604 Self::AssociatedConstants => "associated-consts",
2605 Self::ForeignTypes => "foreign-types",
2606 Self::Keywords => "keywords",
2607 Self::Attributes => "attribute-docs",
2608 Self::AttributeMacros => "attributes",
2609 Self::DeriveMacros => "derives",
2610 Self::TraitAliases => "trait-aliases",
2611 }
2612 }
2613
2614 fn name(self) -> &'static str {
2615 match self {
2616 Self::Reexports => "Re-exports",
2617 Self::Modules => "Modules",
2618 Self::Structs => "Structs",
2619 Self::Unions => "Unions",
2620 Self::Enums => "Enums",
2621 Self::Functions => "Functions",
2622 Self::TypeAliases => "Type Aliases",
2623 Self::Statics => "Statics",
2624 Self::Constants => "Constants",
2625 Self::Traits => "Traits",
2626 Self::Implementations => "Implementations",
2627 Self::TypeMethods => "Type Methods",
2628 Self::Methods => "Methods",
2629 Self::StructFields => "Struct Fields",
2630 Self::Variants => "Variants",
2631 Self::Macros => "Macros",
2632 Self::PrimitiveTypes => "Primitive Types",
2633 Self::AssociatedTypes => "Associated Types",
2634 Self::AssociatedConstants => "Associated Constants",
2635 Self::ForeignTypes => "Foreign Types",
2636 Self::Keywords => "Keywords",
2637 Self::Attributes => "Attributes",
2638 Self::AttributeMacros => "Attribute Macros",
2639 Self::DeriveMacros => "Derive Macros",
2640 Self::TraitAliases => "Trait Aliases",
2641 }
2642 }
2643}
2644
2645fn item_ty_to_section(ty: ItemType) -> ItemSection {
2646 match ty {
2647 ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
2648 ItemType::Module => ItemSection::Modules,
2649 ItemType::Struct => ItemSection::Structs,
2650 ItemType::Union => ItemSection::Unions,
2651 ItemType::Enum => ItemSection::Enums,
2652 ItemType::Function => ItemSection::Functions,
2653 ItemType::TypeAlias => ItemSection::TypeAliases,
2654 ItemType::Static => ItemSection::Statics,
2655 ItemType::Constant => ItemSection::Constants,
2656 ItemType::Trait => ItemSection::Traits,
2657 ItemType::Impl => ItemSection::Implementations,
2658 ItemType::TyMethod => ItemSection::TypeMethods,
2659 ItemType::Method => ItemSection::Methods,
2660 ItemType::StructField => ItemSection::StructFields,
2661 ItemType::Variant => ItemSection::Variants,
2662 ItemType::Macro => ItemSection::Macros,
2663 ItemType::Primitive => ItemSection::PrimitiveTypes,
2664 ItemType::AssocType => ItemSection::AssociatedTypes,
2665 ItemType::AssocConst => ItemSection::AssociatedConstants,
2666 ItemType::ForeignType => ItemSection::ForeignTypes,
2667 ItemType::Keyword => ItemSection::Keywords,
2668 ItemType::Attribute => ItemSection::Attributes,
2669 ItemType::ProcAttribute | ItemType::DeclMacroAttribute => ItemSection::AttributeMacros,
2670 ItemType::ProcDerive | ItemType::DeclMacroDerive => ItemSection::DeriveMacros,
2671 ItemType::TraitAlias => ItemSection::TraitAliases,
2672 }
2673}
2674
2675fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec<String> {
2682 let mut out = Vec::new();
2683 let mut visited = FxHashSet::default();
2684 let mut work = VecDeque::new();
2685
2686 let mut process_path = |did: DefId| {
2687 let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
2688 let fqp = cache.exact_paths.get(&did).or_else(get_extern);
2689
2690 if let Some(path) = fqp {
2691 out.push(join_path_syms(path));
2692 }
2693 };
2694
2695 work.push_back(first_ty);
2696
2697 while let Some(ty) = work.pop_front() {
2698 if !visited.insert(ty) {
2699 continue;
2700 }
2701
2702 match ty {
2703 clean::Type::Path { path } => process_path(path.def_id()),
2704 clean::Type::Tuple(tys) => {
2705 work.extend(tys.iter());
2706 }
2707 clean::Type::Slice(ty) => {
2708 work.push_back(ty);
2709 }
2710 clean::Type::Array(ty, _) => {
2711 work.push_back(ty);
2712 }
2713 clean::Type::RawPointer(_, ty) => {
2714 work.push_back(ty);
2715 }
2716 clean::Type::BorrowedRef { type_, .. } => {
2717 work.push_back(type_);
2718 }
2719 clean::Type::QPath(clean::QPathData { self_type, trait_, .. }) => {
2720 work.push_back(self_type);
2721 if let Some(trait_) = trait_ {
2722 process_path(trait_.def_id());
2723 }
2724 }
2725 _ => {}
2726 }
2727 }
2728 out
2729}
2730
2731const MAX_FULL_EXAMPLES: usize = 5;
2732const NUM_VISIBLE_LINES: usize = 10;
2733
2734fn render_call_locations<W: fmt::Write>(
2736 mut w: W,
2737 cx: &Context<'_>,
2738 item: &clean::Item,
2739) -> fmt::Result {
2740 let tcx = cx.tcx();
2741 let def_id = item.item_id.expect_def_id();
2742 let key = tcx.def_path_hash(def_id);
2743 let Some(call_locations) = cx.shared.call_locations.get(&key) else { return Ok(()) };
2744
2745 let id = cx.derive_id("scraped-examples");
2747 write!(
2748 &mut w,
2749 "<div class=\"docblock scraped-example-list\">\
2750 <span></span>\
2751 <h5 id=\"{id}\">\
2752 <a href=\"#{id}\">Examples found in repository</a>\
2753 <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
2754 </h5>",
2755 root_path = cx.root_path(),
2756 id = id
2757 )?;
2758
2759 let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
2761 let (line_lo, line_hi) = loc.call_expr.line_span;
2762 let (anchor, title) = if line_lo == line_hi {
2763 ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
2764 } else {
2765 (
2766 format!("{}-{}", line_lo + 1, line_hi + 1),
2767 format!("lines {}-{}", line_lo + 1, line_hi + 1),
2768 )
2769 };
2770 let url = format!("{}{}#{anchor}", cx.root_path(), call_data.url);
2771 (url, title)
2772 };
2773
2774 let write_example = |w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
2776 let contents = match fs::read_to_string(path) {
2777 Ok(contents) => contents,
2778 Err(err) => {
2779 let span = item.span(tcx).map_or(DUMMY_SP, |span| span.inner());
2780 tcx.dcx().span_err(span, format!("failed to read file {}: {err}", path.display()));
2781 return false;
2782 }
2783 };
2784
2785 assert!(!call_data.locations.is_empty());
2788 let min_loc =
2789 call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2790 let byte_min = min_loc.enclosing_item.byte_span.0;
2791 let line_min = min_loc.enclosing_item.line_span.0;
2792 let max_loc =
2793 call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
2794 let byte_max = max_loc.enclosing_item.byte_span.1;
2795 let line_max = max_loc.enclosing_item.line_span.1;
2796
2797 let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
2799
2800 let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
2803 .locations
2804 .iter()
2805 .map(|loc| {
2806 let (byte_lo, byte_hi) = loc.call_ident.byte_span;
2807 let (line_lo, line_hi) = loc.call_expr.line_span;
2808 let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
2809
2810 let line_range = (line_lo - line_min, line_hi - line_min);
2811 let (line_url, line_title) = link_to_loc(call_data, loc);
2812
2813 (byte_range, (line_range, line_url, line_title))
2814 })
2815 .unzip();
2816
2817 let (_, init_url, init_title) = &line_ranges[0];
2818 let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
2819 let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
2820
2821 let file_span = rustc_span::DUMMY_SP;
2827
2828 let mut decoration_info = FxIndexMap::default();
2829 decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
2830 decoration_info.insert("highlight", byte_ranges);
2831
2832 sources::print_src(
2833 w,
2834 contents_subset,
2835 file_span,
2836 cx,
2837 &cx.root_path(),
2838 &highlight::DecorationInfo(decoration_info),
2839 &sources::SourceContext::Embedded(sources::ScrapedInfo {
2840 needs_expansion,
2841 offset: line_min,
2842 name: &call_data.display_name,
2843 url: init_url,
2844 title: init_title,
2845 locations: locations_encoded,
2846 }),
2847 )
2848 .unwrap();
2849
2850 true
2851 };
2852
2853 let ordered_locations = {
2865 fn sort_criterion<'a>(
2866 (_, call_data): &(&PathBuf, &'a CallData),
2867 ) -> (bool, u32, &'a String) {
2868 let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2870 (!call_data.is_bin, hi - lo, &call_data.display_name)
2871 }
2872
2873 let mut locs = call_locations.iter().collect::<Vec<_>>();
2874 locs.sort_by_key(sort_criterion);
2875 locs
2876 };
2877
2878 let mut it = ordered_locations.into_iter().peekable();
2879
2880 let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
2883 for example in it.by_ref() {
2884 if write_example(&mut *w, example) {
2885 break;
2886 }
2887 }
2888 };
2889
2890 write_and_skip_failure(&mut w, &mut it);
2892
2893 if it.peek().is_some() {
2895 write!(
2896 w,
2897 "<details class=\"toggle more-examples-toggle\">\
2898 <summary class=\"hideme\">\
2899 <span>More examples</span>\
2900 </summary>\
2901 <div class=\"hide-more\">Hide additional examples</div>\
2902 <div class=\"more-scraped-examples\">\
2903 <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
2904 )?;
2905
2906 for _ in 0..MAX_FULL_EXAMPLES {
2909 write_and_skip_failure(&mut w, &mut it);
2910 }
2911
2912 if it.peek().is_some() {
2914 w.write_str(
2915 r#"<div class="example-links">Additional examples can be found in:<br><ul>"#,
2916 )?;
2917 it.try_for_each(|(_, call_data)| {
2918 let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
2919 write!(
2920 w,
2921 r#"<li><a href="{url}">{name}</a></li>"#,
2922 url = url,
2923 name = call_data.display_name
2924 )
2925 })?;
2926 w.write_str("</ul></div>")?;
2927 }
2928
2929 w.write_str("</div></details>")?;
2930 }
2931
2932 w.write_str("</div>")
2933}
2934
2935fn render_attributes_in_code(
2936 w: &mut impl fmt::Write,
2937 item: &clean::Item,
2938 prefix: &str,
2939 cx: &Context<'_>,
2940) -> fmt::Result {
2941 render_attributes_in_code_with_options(w, item, prefix, cx, true, "")
2942}
2943
2944pub(super) fn render_attributes_in_code_with_options(
2945 w: &mut impl fmt::Write,
2946 item: &clean::Item,
2947 prefix: &str,
2948 cx: &Context<'_>,
2949 render_doc_hidden: bool,
2950 open_tag: &str,
2951) -> fmt::Result {
2952 w.write_str(open_tag)?;
2953 if render_doc_hidden && item.is_doc_hidden() {
2954 render_code_attribute(prefix, "#[doc(hidden)]", w)?;
2955 }
2956 for attr in &item.attrs.other_attrs {
2957 let hir::Attribute::Parsed(kind) = attr else { continue };
2958 let attr = match kind {
2959 AttributeKind::LinkSection { name, .. } => {
2960 Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}"))))
2961 }
2962 AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"),
2963 AttributeKind::ExportName { name, .. } => {
2964 Cow::Owned(format!("#[unsafe(export_name = {})]", Escape(&format!("{name:?}"))))
2965 }
2966 AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"),
2967 _ => continue,
2968 };
2969 render_code_attribute(prefix, attr.as_ref(), w)?;
2970 }
2971
2972 if let Some(def_id) = item.def_id()
2973 && let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id)
2974 {
2975 render_code_attribute(prefix, &repr, w)?;
2976 }
2977 Ok(())
2978}
2979
2980fn render_repr_attribute_in_code(
2981 w: &mut impl fmt::Write,
2982 cx: &Context<'_>,
2983 def_id: DefId,
2984) -> fmt::Result {
2985 if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) {
2986 render_code_attribute("", &repr, w)?;
2987 }
2988 Ok(())
2989}
2990
2991fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) -> fmt::Result {
2992 write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>")
2993}
2994
2995fn repr_attribute<'tcx>(
3000 tcx: TyCtxt<'tcx>,
3001 cache: &Cache,
3002 def_id: DefId,
3003) -> Option<Cow<'static, str>> {
3004 let adt = match tcx.def_kind(def_id) {
3005 DefKind::Struct | DefKind::Enum | DefKind::Union => tcx.adt_def(def_id),
3006 _ => return None,
3007 };
3008 let repr = adt.repr();
3009
3010 let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id);
3011 let is_public_field = |field: &ty::FieldDef| {
3012 (cache.document_private || field.vis.is_public()) && is_visible(field.did)
3013 };
3014
3015 if repr.transparent() {
3016 let is_public = 'is_public: {
3019 let var = adt.variant(rustc_abi::FIRST_VARIANT); if !is_visible(var.def_id) {
3023 break 'is_public false;
3024 }
3025
3026 let non_1zst_field = var.fields.iter().find(|field| {
3028 let ty = ty::TypingEnv::post_analysis(tcx, field.did)
3029 .as_query_input(tcx.type_of(field.did).instantiate_identity().skip_norm_wip());
3030 tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst())
3031 });
3032
3033 match non_1zst_field {
3034 Some(field) => is_public_field(field),
3035 None => var.fields.is_empty() || var.fields.iter().any(is_public_field),
3036 }
3037 };
3038
3039 return is_public.then(|| "#[repr(transparent)]".into());
3042 }
3043
3044 if !repr.c()
3048 && !repr.simd()
3049 && repr.int.is_none()
3050 && repr.pack.is_none()
3051 && repr.align.is_none()
3052 {
3053 return None;
3054 }
3055
3056 let is_public = adt
3058 .variants()
3059 .iter()
3060 .all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field));
3061 if !is_public {
3062 return None;
3063 }
3064
3065 let mut result = Vec::<Cow<'_, _>>::new();
3066
3067 if repr.c() {
3068 result.push("C".into());
3069 }
3070 if repr.simd() {
3071 result.push("simd".into());
3072 }
3073 if let Some(int) = repr.int {
3074 let prefix = if int.is_signed() { 'i' } else { 'u' };
3075 let int = match int {
3076 rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"),
3077 rustc_abi::IntegerType::Fixed(int, _) => {
3078 format!("{prefix}{}", int.size().bytes() * 8)
3079 }
3080 };
3081 result.push(int.into());
3082 }
3083
3084 if let Some(pack) = repr.pack {
3086 result.push(format!("packed({})", pack.bytes()).into());
3087 }
3088 if let Some(align) = repr.align {
3089 result.push(format!("align({})", align.bytes()).into());
3090 }
3091
3092 (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into())
3093}