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