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