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::{BTreeMap, 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, RustcVersion};
57use rustc_hir::def::DefKind;
58use rustc_hir::def_id::{DefId, DefIdSet};
59use rustc_hir::{ConstStability, Mutability, 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 is_notable_trait_passthrough(did: DefId, cx: &Context<'_>) -> bool {
1672 let lang_items = cx.tcx().lang_items();
1673 Some(did) == lang_items.owned_box() || Some(did) == lang_items.pin_type()
1674}
1675
1676fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option<impl fmt::Display> {
1677 if ty.is_unit() {
1678 return None;
1680 }
1681
1682 let did = ty.def_id(cx.cache())?;
1683
1684 if is_notable_trait_passthrough(did, cx) {
1685 return None;
1686 }
1687
1688 let impls = cx.cache().impls.get(&did)?;
1689 let has_notable_trait = impls
1690 .iter()
1691 .map(Impl::inner_impl)
1692 .filter(|impl_| {
1693 impl_.polarity == ty::ImplPolarity::Positive
1694 && ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1697 })
1698 .filter_map(|impl_| impl_.trait_.as_ref())
1699 .filter_map(|trait_| cx.cache().traits.get(&trait_.def_id()))
1700 .any(|t| t.is_notable_trait(cx.tcx()));
1701
1702 has_notable_trait.then(|| {
1703 cx.types_with_notable_traits.borrow_mut().insert(ty.clone());
1704 fmt::from_fn(|f| {
1705 write!(
1706 f,
1707 " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>",
1708 ty = Escape(&format!("{:#}", print_type(ty, cx))),
1709 )
1710 })
1711 })
1712}
1713
1714fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
1715 let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
1716
1717 let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");
1718
1719 let out = fmt::from_fn(|f| {
1720 let mut notable_impls = impls
1721 .iter()
1722 .map(|impl_| impl_.inner_impl())
1723 .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive)
1724 .filter(|impl_| {
1725 ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1727 })
1728 .filter_map(|impl_| {
1729 if let Some(trait_) = &impl_.trait_
1730 && let trait_did = trait_.def_id()
1731 && let Some(trait_) = cx.cache().traits.get(&trait_did)
1732 && trait_.is_notable_trait(cx.tcx())
1733 {
1734 Some((impl_, trait_did))
1735 } else {
1736 None
1737 }
1738 })
1739 .peekable();
1740
1741 let has_notable_impl = if let Some((impl_, _)) = notable_impls.peek() {
1742 write!(
1743 f,
1744 "<h3>Notable traits for <code>{}</code></h3>\
1745 <pre><code>",
1746 print_type(&impl_.for_, cx),
1747 )?;
1748 true
1749 } else {
1750 false
1751 };
1752
1753 for (impl_, trait_did) in notable_impls {
1754 write!(f, "<div class=\"where\">{}</div>", print_impl(impl_, false, cx))?;
1755 for it in &impl_.items {
1756 let clean::AssocTypeItem(tydef, ..) = &it.kind else {
1757 continue;
1758 };
1759
1760 let empty_set = FxIndexSet::default();
1761 let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
1762
1763 write!(
1764 f,
1765 "<div class=\"where\"> {};</div>",
1766 assoc_type(
1767 it,
1768 &tydef.generics,
1769 &[], Some(&tydef.type_),
1771 src_link,
1772 0,
1773 cx,
1774 )
1775 )?;
1776 }
1777 }
1778
1779 if !has_notable_impl {
1780 f.write_str("</code></pre>")?;
1781 }
1782
1783 Ok(())
1784 })
1785 .to_string();
1786
1787 (format!("{:#}", print_type(ty, cx)), out)
1788}
1789
1790fn notable_traits_json<'a>(tys: impl Iterator<Item = &'a clean::Type>, cx: &Context<'_>) -> String {
1791 let mut mp = tys.map(|ty| notable_traits_decl(ty, cx)).collect::<IndexMap<_, _>>();
1792 mp.sort_unstable_keys();
1793 serde_json::to_string(&mp).expect("serialize (string, string) -> json object cannot fail")
1794}
1795
1796pub(crate) struct NotableTraitBadge {
1797 pub name: String,
1798 pub full_path: String,
1799 pub href: Option<String>,
1801}
1802
1803pub(crate) fn notable_trait_badges(item: &clean::Item, cx: &Context<'_>) -> Vec<NotableTraitBadge> {
1806 let tcx = cx.tcx();
1807 if let Some(def_id) = item.def_id()
1808 && !is_notable_trait_passthrough(def_id, cx)
1809 && let Some(impls) = cx.cache().impls.get(&def_id)
1810 {
1811 impls
1812 .iter()
1813 .map(Impl::inner_impl)
1814 .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive)
1815 .filter_map(|impl_| {
1816 if let Some(trait_) = &impl_.trait_
1817 && let trait_did = trait_.def_id()
1818 && let Some(trait_) = cx.cache().traits.get(&trait_did)
1819 && trait_.is_notable_trait(tcx)
1820 {
1821 let name = tcx.item_name(trait_did).to_string();
1822 let (full_path, href) = match href(trait_did, cx) {
1823 Ok(info) => (join_path_syms(&info.rust_path), Some(info.url)),
1824 Err(_) => (tcx.def_path_str(trait_did), None),
1825 };
1826 Some((name.clone(), NotableTraitBadge { name, full_path, href }))
1827 } else {
1828 None
1829 }
1830 })
1831 .collect::<BTreeMap<String, NotableTraitBadge>>()
1832 .into_values()
1833 .collect()
1834 } else {
1835 Vec::new()
1836 }
1837}
1838
1839#[derive(Clone, Copy, Debug)]
1840struct ImplRenderingParameters {
1841 show_def_docs: bool,
1842 show_default_items: bool,
1843 show_non_assoc_items: bool,
1845 toggle_open_by_default: bool,
1846}
1847
1848fn render_impl(
1849 cx: &Context<'_>,
1850 i: &Impl,
1851 parent: &clean::Item,
1852 link: AssocItemLink<'_>,
1853 render_mode: RenderMode,
1854 use_absolute: Option<bool>,
1855 aliases: &[String],
1856 rendering_params: ImplRenderingParameters,
1857) -> impl fmt::Display {
1858 fmt::from_fn(move |w| {
1859 let cache = &cx.shared.cache;
1860 let traits = &cache.traits;
1861 let trait_ = i.trait_did().map(|did| &traits[&did]);
1862 let mut close_tags = <Vec<&str>>::with_capacity(2);
1863
1864 fn doc_impl_item(
1870 boring: impl fmt::Write,
1871 interesting: impl fmt::Write,
1872 cx: &Context<'_>,
1873 item: &clean::Item,
1874 parent: &clean::Item,
1875 link: AssocItemLink<'_>,
1876 render_mode: RenderMode,
1877 is_default_item: bool,
1878 trait_: Option<&clean::Trait>,
1879 rendering_params: ImplRenderingParameters,
1880 ) -> fmt::Result {
1881 let item_type = item.type_();
1882 let name = item.name.as_ref().unwrap();
1883
1884 let render_method_item = rendering_params.show_non_assoc_items
1885 && match render_mode {
1886 RenderMode::Normal => true,
1887 RenderMode::ForDeref { mut_: deref_mut_ } => {
1888 should_render_item(item, deref_mut_, cx.tcx())
1889 }
1890 };
1891
1892 let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
1893
1894 let mut doc_buffer = String::new();
1895 let mut info_buffer = String::new();
1896 let mut short_documented = true;
1897
1898 let mut trait_item_deprecated = false;
1899 if render_method_item {
1900 if !is_default_item {
1901 if let Some(t) = trait_ {
1902 if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1905 trait_item_deprecated = it.is_deprecated(cx.tcx());
1906 if !item.doc_value().is_empty() {
1909 document_item_info(cx, it, Some(parent))
1910 .render_into(&mut info_buffer)?;
1911 doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1912 short_documented = false;
1913 } else {
1914 doc_buffer = document_short(
1917 it,
1918 cx,
1919 link,
1920 parent,
1921 rendering_params.show_def_docs,
1922 )
1923 .to_string();
1924 }
1925 }
1926 } else {
1927 document_item_info(cx, item, Some(parent)).render_into(&mut info_buffer)?;
1928 if rendering_params.show_def_docs {
1929 doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1930 short_documented = false;
1931 }
1932 }
1933 } else {
1934 doc_buffer =
1935 document_short(item, cx, link, parent, rendering_params.show_def_docs)
1936 .to_string();
1937 }
1938 }
1939 let mut w = if short_documented && trait_.is_some() {
1940 Either::Left(interesting)
1941 } else {
1942 Either::Right(boring)
1943 };
1944
1945 let mut deprecation_class = if trait_item_deprecated || item.is_deprecated(cx.tcx()) {
1946 " deprecated"
1947 } else {
1948 ""
1949 };
1950
1951 let toggled = !doc_buffer.is_empty();
1952 if toggled {
1953 let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
1954 write!(
1955 w,
1956 "<details class=\"toggle{method_toggle_class}{deprecation_class}\" open><summary>"
1957 )?;
1958 deprecation_class = "";
1959 }
1960 match &item.kind {
1961 clean::MethodItem(..) | clean::RequiredMethodItem(..) => {
1962 if render_method_item {
1964 let id = cx.derive_id(format!("{item_type}.{name}"));
1965 let source_id = trait_
1966 .and_then(|trait_| {
1967 trait_
1968 .items
1969 .iter()
1970 .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
1971 })
1972 .map(|item| format!("{}.{name}", item.type_()));
1973 write!(
1974 w,
1975 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1976 {}",
1977 render_rightside(cx, item, render_mode)
1978 )?;
1979 if trait_.is_some() {
1980 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1982 }
1983 write!(
1984 w,
1985 "<h4 class=\"code-header\">{}</h4></section>",
1986 render_assoc_item(
1987 item,
1988 link.anchor(source_id.as_ref().unwrap_or(&id)),
1989 ItemType::Impl,
1990 cx,
1991 render_mode,
1992 ),
1993 )?;
1994 }
1995 }
1996 clean::RequiredAssocConstItem(generics, ty) => {
1997 let source_id = format!("{item_type}.{name}");
1998 let id = cx.derive_id(&source_id);
1999 write!(
2000 w,
2001 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
2002 {}",
2003 render_rightside(cx, item, render_mode)
2004 )?;
2005 if trait_.is_some() {
2006 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
2008 }
2009 write!(
2010 w,
2011 "<h4 class=\"code-header\">{}</h4></section>",
2012 assoc_const(
2013 item,
2014 generics,
2015 ty,
2016 AssocConstValue::None,
2017 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2018 0,
2019 cx,
2020 ),
2021 )?;
2022 }
2023 clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
2024 let source_id = format!("{item_type}.{name}");
2025 let id = cx.derive_id(&source_id);
2026 write!(
2027 w,
2028 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
2029 {}",
2030 render_rightside(cx, item, render_mode),
2031 )?;
2032 if trait_.is_some() {
2033 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
2035 }
2036 write!(
2037 w,
2038 "<h4 class=\"code-header\">{}</h4></section>",
2039 assoc_const(
2040 item,
2041 &ci.generics,
2042 &ci.type_,
2043 match item.kind {
2044 clean::ProvidedAssocConstItem(_) =>
2045 AssocConstValue::TraitDefault(&ci.kind),
2046 clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
2047 _ => unreachable!(),
2048 },
2049 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2050 0,
2051 cx,
2052 ),
2053 )?;
2054 }
2055 clean::RequiredAssocTypeItem(generics, bounds) => {
2056 let source_id = format!("{item_type}.{name}");
2057 let id = cx.derive_id(&source_id);
2058 write!(
2059 w,
2060 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
2061 {}",
2062 render_rightside(cx, item, render_mode),
2063 )?;
2064 if trait_.is_some() {
2065 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
2067 }
2068 write!(
2069 w,
2070 "<h4 class=\"code-header\">{}</h4></section>",
2071 assoc_type(
2072 item,
2073 generics,
2074 bounds,
2075 None,
2076 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2077 0,
2078 cx,
2079 ),
2080 )?;
2081 }
2082 clean::AssocTypeItem(tydef, _bounds) => {
2083 let source_id = format!("{item_type}.{name}");
2084 let id = cx.derive_id(&source_id);
2085 write!(
2086 w,
2087 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
2088 {}",
2089 render_rightside(cx, item, render_mode),
2090 )?;
2091 if trait_.is_some() {
2092 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
2094 }
2095 write!(
2096 w,
2097 "<h4 class=\"code-header\">{}</h4></section>",
2098 assoc_type(
2099 item,
2100 &tydef.generics,
2101 &[], Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
2103 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2104 0,
2105 cx,
2106 ),
2107 )?;
2108 }
2109 clean::StrippedItem(..) => return Ok(()),
2110 _ => panic!("can't make docs for trait item with name {:?}", item.name),
2111 }
2112
2113 w.write_str(&info_buffer)?;
2114 if toggled {
2115 write!(w, "</summary>{doc_buffer}</details>")?;
2116 }
2117 Ok(())
2118 }
2119
2120 let mut impl_items = String::new();
2121 let mut default_impl_items = String::new();
2122 let impl_ = i.inner_impl();
2123
2124 let mut assoc_types = Vec::new();
2134 let mut methods = Vec::new();
2135
2136 if !impl_.is_negative_trait_impl() {
2137 for impl_item in &impl_.items {
2138 match impl_item.kind {
2139 clean::MethodItem(..) | clean::RequiredMethodItem(..) => {
2140 methods.push(impl_item)
2141 }
2142 clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => {
2143 assoc_types.push(impl_item)
2144 }
2145 clean::RequiredAssocConstItem(..)
2146 | clean::ProvidedAssocConstItem(_)
2147 | clean::ImplAssocConstItem(_) => {
2148 doc_impl_item(
2150 &mut default_impl_items,
2151 &mut impl_items,
2152 cx,
2153 impl_item,
2154 if trait_.is_some() { &i.impl_item } else { parent },
2155 link,
2156 render_mode,
2157 false,
2158 trait_,
2159 rendering_params,
2160 )?;
2161 }
2162 _ => {}
2163 }
2164 }
2165
2166 for assoc_type in assoc_types {
2167 doc_impl_item(
2168 &mut default_impl_items,
2169 &mut impl_items,
2170 cx,
2171 assoc_type,
2172 if trait_.is_some() { &i.impl_item } else { parent },
2173 link,
2174 render_mode,
2175 false,
2176 trait_,
2177 rendering_params,
2178 )?;
2179 }
2180 for method in methods {
2181 doc_impl_item(
2182 &mut default_impl_items,
2183 &mut impl_items,
2184 cx,
2185 method,
2186 if trait_.is_some() { &i.impl_item } else { parent },
2187 link,
2188 render_mode,
2189 false,
2190 trait_,
2191 rendering_params,
2192 )?;
2193 }
2194 }
2195
2196 fn render_default_items(
2197 mut boring: impl fmt::Write,
2198 mut interesting: impl fmt::Write,
2199 cx: &Context<'_>,
2200 t: &clean::Trait,
2201 i: &clean::Impl,
2202 parent: &clean::Item,
2203 render_mode: RenderMode,
2204 rendering_params: ImplRenderingParameters,
2205 ) -> fmt::Result {
2206 for trait_item in &t.items {
2207 if let Some(impl_def_id) = parent.item_id.as_def_id()
2210 && let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
2211 && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id))
2212 {
2213 continue;
2214 }
2215
2216 let n = trait_item.name;
2217 if i.items.iter().any(|m| m.name == n) {
2218 continue;
2219 }
2220 let did = i.trait_.as_ref().unwrap().def_id();
2221 let provided_methods = i.provided_trait_methods(cx.tcx());
2222 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
2223
2224 doc_impl_item(
2225 &mut boring,
2226 &mut interesting,
2227 cx,
2228 trait_item,
2229 parent,
2230 assoc_link,
2231 render_mode,
2232 true,
2233 Some(t),
2234 rendering_params,
2235 )?;
2236 }
2237 Ok(())
2238 }
2239
2240 if rendering_params.show_default_items
2245 && let Some(t) = trait_
2246 && !impl_.is_negative_trait_impl()
2247 {
2248 render_default_items(
2249 &mut default_impl_items,
2250 &mut impl_items,
2251 cx,
2252 t,
2253 impl_,
2254 &i.impl_item,
2255 render_mode,
2256 rendering_params,
2257 )?;
2258 }
2259 if render_mode == RenderMode::Normal {
2260 let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
2261 let deprecation_attr = if impl_.is_deprecated
2262 || trait_.is_some_and(|trait_| trait_.is_deprecated(cx.tcx()))
2263 {
2264 " deprecated"
2265 } else {
2266 ""
2267 };
2268 if toggled {
2269 close_tags.push("</details>");
2270 write!(
2271 w,
2272 "<details class=\"toggle implementors-toggle{deprecation_attr}\"{}>\
2273 <summary>",
2274 if rendering_params.toggle_open_by_default { " open" } else { "" }
2275 )?;
2276 }
2277
2278 let (before_dox, after_dox) = i
2279 .impl_item
2280 .opt_doc_value()
2281 .map(|dox| {
2282 Markdown {
2283 content: &dox,
2284 links: &i.impl_item.links(cx),
2285 ids: &mut cx.id_map.borrow_mut(),
2286 error_codes: cx.shared.codes,
2287 edition: cx.shared.edition(),
2288 playground: &cx.shared.playground,
2289 heading_offset: HeadingOffset::H4,
2290 }
2291 .split_summary_and_content()
2292 })
2293 .unwrap_or((None, None));
2294
2295 write!(
2296 w,
2297 "{}",
2298 render_impl_summary(
2299 cx,
2300 i,
2301 parent,
2302 rendering_params.show_def_docs,
2303 use_absolute,
2304 aliases,
2305 before_dox.as_deref(),
2306 trait_.is_none() && impl_.items.is_empty(),
2307 )
2308 )?;
2309 if toggled {
2310 w.write_str("</summary>")?;
2311 }
2312
2313 if before_dox.is_some()
2314 && let Some(after_dox) = after_dox
2315 {
2316 write!(w, "<div class=\"docblock\">{after_dox}</div>")?;
2317 }
2318
2319 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2320 w.write_str("<div class=\"impl-items\">")?;
2321 close_tags.push("</div>");
2322 }
2323 }
2324 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2325 w.write_str(&default_impl_items)?;
2326 w.write_str(&impl_items)?;
2327 }
2328 for tag in close_tags.into_iter().rev() {
2329 w.write_str(tag)?;
2330 }
2331 Ok(())
2332 })
2333}
2334
2335fn render_rightside(
2338 cx: &Context<'_>,
2339 item: &clean::Item,
2340 render_mode: RenderMode,
2341) -> impl fmt::Display {
2342 let tcx = cx.tcx();
2343
2344 fmt::from_fn(move |w| {
2345 let const_stability = match render_mode {
2348 RenderMode::Normal => item.const_stability(tcx),
2349 RenderMode::ForDeref { .. } => None,
2350 };
2351 let src_href = cx.src_href(item);
2352 let stability = render_stability_since_raw_with_extra(
2353 item.stable_since(tcx),
2354 const_stability,
2355 if src_href.is_some() { "" } else { " rightside" },
2356 );
2357
2358 match (stability, src_href) {
2359 (Some(stability), Some(link)) => {
2360 write!(
2361 w,
2362 "<span class=\"rightside\">{stability} · <a class=\"src\" href=\"{link}\">Source</a></span>",
2363 )
2364 }
2365 (Some(stability), None) => {
2366 write!(w, "{stability}")
2367 }
2368 (None, Some(link)) => {
2369 write!(w, "<a class=\"src rightside\" href=\"{link}\">Source</a>")
2370 }
2371 (None, None) => Ok(()),
2372 }
2373 })
2374}
2375
2376fn render_impl_summary(
2377 cx: &Context<'_>,
2378 i: &Impl,
2379 parent: &clean::Item,
2380 show_def_docs: bool,
2381 use_absolute: Option<bool>,
2382 aliases: &[String],
2385 doc: Option<&str>,
2386 impl_is_empty: bool,
2387) -> impl fmt::Display {
2388 fmt::from_fn(move |w| {
2389 let inner_impl = i.inner_impl();
2390 let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
2391 let aliases = (!aliases.is_empty())
2392 .then_some(fmt::from_fn(|f| {
2393 write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f)))
2394 }))
2395 .maybe_display();
2396 write!(
2397 w,
2398 "<section id=\"{id}\" class=\"impl\"{aliases}>\
2399 {}\
2400 <a href=\"#{id}\" class=\"anchor\">§</a>\
2401 <h3 class=\"code-header\">",
2402 render_rightside(cx, &i.impl_item, RenderMode::Normal)
2403 )?;
2404
2405 if let Some(use_absolute) = use_absolute {
2406 write!(w, "{}", print_impl(inner_impl, use_absolute, cx))?;
2407 if show_def_docs {
2408 for it in &inner_impl.items {
2409 if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
2410 write!(
2411 w,
2412 "<div class=\"where\"> {};</div>",
2413 assoc_type(
2414 it,
2415 &tydef.generics,
2416 &[], Some(&tydef.type_),
2418 AssocItemLink::Anchor(None),
2419 0,
2420 cx,
2421 )
2422 )?;
2423 }
2424 }
2425 }
2426 } else {
2427 write!(w, "{}", print_impl(inner_impl, false, cx))?;
2428 }
2429 w.write_str("</h3>")?;
2430
2431 let is_trait = inner_impl.trait_.is_some();
2432 if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) {
2433 write!(
2434 w,
2435 "<span class=\"item-info\">\
2436 <div class=\"stab portability\">{portability}</div>\
2437 </span>",
2438 )?;
2439 }
2440
2441 if let Some(doc) = doc {
2442 if impl_is_empty {
2443 w.write_str(
2444 "\
2445<div class=\"item-info\">\
2446 <div class=\"stab empty-impl\">This impl block contains no public items.</div>\
2447</div>",
2448 )?;
2449 }
2450 write!(w, "<div class=\"docblock\">{doc}</div>")?;
2451 }
2452
2453 w.write_str("</section>")
2454 })
2455}
2456
2457pub(crate) fn small_url_encode(s: String) -> String {
2458 fn dont_escape(c: u8) -> bool {
2463 c.is_ascii_alphanumeric()
2464 || c == b'-'
2465 || c == b'_'
2466 || c == b'.'
2467 || c == b','
2468 || c == b'~'
2469 || c == b'!'
2470 || c == b'\''
2471 || c == b'('
2472 || c == b')'
2473 || c == b'*'
2474 || c == b'/'
2475 || c == b';'
2476 || c == b':'
2477 || c == b'?'
2478 || c == b'='
2482 }
2483 let mut st = String::new();
2484 let mut last_match = 0;
2485 for (idx, b) in s.bytes().enumerate() {
2486 if dont_escape(b) {
2487 continue;
2488 }
2489
2490 if last_match != idx {
2491 st += &s[last_match..idx];
2493 }
2494 if b == b' ' {
2495 st += "+";
2499 } else {
2500 write!(st, "%{b:02X}").unwrap();
2501 }
2502 last_match = idx + 1;
2508 }
2509
2510 if last_match != 0 {
2511 st += &s[last_match..];
2512 st
2513 } else {
2514 s
2515 }
2516}
2517
2518fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String {
2519 use rustc_middle::ty::print::with_forced_trimmed_paths;
2520 let (type_, trait_) = match impl_id {
2521 ItemId::Auto { trait_, for_ } => {
2522 let ty = tcx.type_of(for_).skip_binder();
2523 (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
2524 }
2525 ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
2526 if let Some(trait_ref) = tcx.impl_opt_trait_ref(impl_id) {
2527 let trait_ref = trait_ref.skip_binder();
2528 (trait_ref.self_ty(), Some(trait_ref))
2529 } else {
2530 (tcx.type_of(impl_id).skip_binder(), None)
2531 }
2532 }
2533 };
2534 with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
2535 format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
2536 } else {
2537 format!("impl-{type_}")
2538 }))
2539}
2540
2541fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2542 match item.kind {
2543 clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
2544 Some((
2547 format!("{:#}", print_type(&i.for_, cx)),
2548 get_id_for_impl(cx.tcx(), item.item_id),
2549 ))
2550 }
2551 _ => None,
2552 }
2553}
2554
2555pub(crate) fn get_filtered_impls_for_reference<'a>(
2559 shared: &'a SharedContext<'_>,
2560 it: &clean::Item,
2561) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
2562 let def_id = it.item_id.expect_def_id();
2563 let Some(v) = shared.cache.impls.get(&def_id) else {
2565 return (Vec::new(), Vec::new(), Vec::new());
2566 };
2567 let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
2570 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
2571 traits.partition(|t| t.inner_impl().kind.is_auto());
2572
2573 let (blanket_impl, concrete): (Vec<&Impl>, _) =
2574 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
2575 let concrete: Vec<_> = concrete
2577 .into_iter()
2578 .filter(|t| match t.inner_impl().for_ {
2579 clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
2580 _ => false,
2581 })
2582 .collect();
2583
2584 (concrete, synthetic, blanket_impl)
2585}
2586
2587#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2588pub(crate) enum ItemSection {
2589 Reexports,
2590 PrimitiveTypes,
2591 Modules,
2592 Macros,
2593 Structs,
2594 Enums,
2595 Constants,
2596 Statics,
2597 Traits,
2598 Functions,
2599 TypeAliases,
2600 Unions,
2601 Implementations,
2602 TypeMethods,
2603 Methods,
2604 StructFields,
2605 Variants,
2606 AssociatedTypes,
2607 AssociatedConstants,
2608 ForeignTypes,
2609 Keywords,
2610 Attributes,
2611 AttributeMacros,
2612 DeriveMacros,
2613 TraitAliases,
2614}
2615
2616impl ItemSection {
2617 const ALL: &'static [Self] = {
2618 use ItemSection::*;
2619 &[
2622 Reexports,
2623 PrimitiveTypes,
2624 Modules,
2625 Macros,
2626 Structs,
2627 Enums,
2628 Constants,
2629 Statics,
2630 Traits,
2631 Functions,
2632 TypeAliases,
2633 Unions,
2634 Implementations,
2635 TypeMethods,
2636 Methods,
2637 StructFields,
2638 Variants,
2639 AssociatedTypes,
2640 AssociatedConstants,
2641 ForeignTypes,
2642 Keywords,
2643 Attributes,
2644 AttributeMacros,
2645 DeriveMacros,
2646 TraitAliases,
2647 ]
2648 };
2649
2650 fn id(self) -> &'static str {
2651 match self {
2652 Self::Reexports => "reexports",
2653 Self::Modules => "modules",
2654 Self::Structs => "structs",
2655 Self::Unions => "unions",
2656 Self::Enums => "enums",
2657 Self::Functions => "functions",
2658 Self::TypeAliases => "types",
2659 Self::Statics => "statics",
2660 Self::Constants => "constants",
2661 Self::Traits => "traits",
2662 Self::Implementations => "impls",
2663 Self::TypeMethods => "tymethods",
2664 Self::Methods => "methods",
2665 Self::StructFields => "fields",
2666 Self::Variants => "variants",
2667 Self::Macros => "macros",
2668 Self::PrimitiveTypes => "primitives",
2669 Self::AssociatedTypes => "associated-types",
2670 Self::AssociatedConstants => "associated-consts",
2671 Self::ForeignTypes => "foreign-types",
2672 Self::Keywords => "keywords",
2673 Self::Attributes => "attribute-docs",
2674 Self::AttributeMacros => "attributes",
2675 Self::DeriveMacros => "derives",
2676 Self::TraitAliases => "trait-aliases",
2677 }
2678 }
2679
2680 fn name(self) -> &'static str {
2681 match self {
2682 Self::Reexports => "Re-exports",
2683 Self::Modules => "Modules",
2684 Self::Structs => "Structs",
2685 Self::Unions => "Unions",
2686 Self::Enums => "Enums",
2687 Self::Functions => "Functions",
2688 Self::TypeAliases => "Type Aliases",
2689 Self::Statics => "Statics",
2690 Self::Constants => "Constants",
2691 Self::Traits => "Traits",
2692 Self::Implementations => "Implementations",
2693 Self::TypeMethods => "Type Methods",
2694 Self::Methods => "Methods",
2695 Self::StructFields => "Struct Fields",
2696 Self::Variants => "Variants",
2697 Self::Macros => "Macros",
2698 Self::PrimitiveTypes => "Primitive Types",
2699 Self::AssociatedTypes => "Associated Types",
2700 Self::AssociatedConstants => "Associated Constants",
2701 Self::ForeignTypes => "Foreign Types",
2702 Self::Keywords => "Keywords",
2703 Self::Attributes => "Attributes",
2704 Self::AttributeMacros => "Attribute Macros",
2705 Self::DeriveMacros => "Derive Macros",
2706 Self::TraitAliases => "Trait Aliases",
2707 }
2708 }
2709}
2710
2711fn item_ty_to_section(ty: ItemType) -> ItemSection {
2712 match ty {
2713 ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
2714 ItemType::Module => ItemSection::Modules,
2715 ItemType::Struct => ItemSection::Structs,
2716 ItemType::Union => ItemSection::Unions,
2717 ItemType::Enum => ItemSection::Enums,
2718 ItemType::Function => ItemSection::Functions,
2719 ItemType::TypeAlias => ItemSection::TypeAliases,
2720 ItemType::Static => ItemSection::Statics,
2721 ItemType::Constant => ItemSection::Constants,
2722 ItemType::Trait => ItemSection::Traits,
2723 ItemType::Impl => ItemSection::Implementations,
2724 ItemType::TyMethod => ItemSection::TypeMethods,
2725 ItemType::Method => ItemSection::Methods,
2726 ItemType::StructField => ItemSection::StructFields,
2727 ItemType::Variant => ItemSection::Variants,
2728 ItemType::Macro => ItemSection::Macros,
2729 ItemType::Primitive => ItemSection::PrimitiveTypes,
2730 ItemType::AssocType => ItemSection::AssociatedTypes,
2731 ItemType::AssocConst => ItemSection::AssociatedConstants,
2732 ItemType::ForeignType => ItemSection::ForeignTypes,
2733 ItemType::Keyword => ItemSection::Keywords,
2734 ItemType::Attribute => ItemSection::Attributes,
2735 ItemType::ProcAttribute | ItemType::DeclMacroAttribute => ItemSection::AttributeMacros,
2736 ItemType::ProcDerive | ItemType::DeclMacroDerive => ItemSection::DeriveMacros,
2737 ItemType::TraitAlias => ItemSection::TraitAliases,
2738 }
2739}
2740
2741fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec<String> {
2748 let mut out = Vec::new();
2749 let mut visited = FxHashSet::default();
2750 let mut work = VecDeque::new();
2751
2752 let mut process_path = |did: DefId| {
2753 let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
2754 let fqp = cache.exact_paths.get(&did).or_else(get_extern);
2755
2756 if let Some(path) = fqp {
2757 out.push(join_path_syms(path));
2758 }
2759 };
2760
2761 work.push_back(first_ty);
2762
2763 while let Some(ty) = work.pop_front() {
2764 if !visited.insert(ty) {
2765 continue;
2766 }
2767
2768 match ty {
2769 clean::Type::Path { path } => process_path(path.def_id()),
2770 clean::Type::Tuple(tys) => {
2771 work.extend(tys.iter());
2772 }
2773 clean::Type::Slice(ty) => {
2774 work.push_back(ty);
2775 }
2776 clean::Type::Array(ty, _) => {
2777 work.push_back(ty);
2778 }
2779 clean::Type::RawPointer(_, ty) => {
2780 work.push_back(ty);
2781 }
2782 clean::Type::BorrowedRef { type_, .. } => {
2783 work.push_back(type_);
2784 }
2785 clean::Type::QPath(clean::QPathData { self_type, trait_, .. }) => {
2786 work.push_back(self_type);
2787 if let Some(trait_) = trait_ {
2788 process_path(trait_.def_id());
2789 }
2790 }
2791 _ => {}
2792 }
2793 }
2794 out
2795}
2796
2797const MAX_FULL_EXAMPLES: usize = 5;
2798const NUM_VISIBLE_LINES: usize = 10;
2799
2800fn render_call_locations<W: fmt::Write>(
2802 mut w: W,
2803 cx: &Context<'_>,
2804 item: &clean::Item,
2805) -> fmt::Result {
2806 let tcx = cx.tcx();
2807 let def_id = item.item_id.expect_def_id();
2808 let key = tcx.def_path_hash(def_id);
2809 let Some(call_locations) = cx.shared.call_locations.get(&key) else { return Ok(()) };
2810
2811 let id = cx.derive_id("scraped-examples");
2813 write!(
2814 &mut w,
2815 "<div class=\"docblock scraped-example-list\">\
2816 <span></span>\
2817 <h5 id=\"{id}\">\
2818 <a href=\"#{id}\">Examples found in repository</a>\
2819 <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
2820 </h5>",
2821 root_path = cx.root_path(),
2822 id = id
2823 )?;
2824
2825 let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
2827 let (line_lo, line_hi) = loc.call_expr.line_span;
2828 let (anchor, title) = if line_lo == line_hi {
2829 ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
2830 } else {
2831 (
2832 format!("{}-{}", line_lo + 1, line_hi + 1),
2833 format!("lines {}-{}", line_lo + 1, line_hi + 1),
2834 )
2835 };
2836 let url = format!("{}{}#{anchor}", cx.root_path(), call_data.url);
2837 (url, title)
2838 };
2839
2840 let write_example = |w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
2842 let contents = match fs::read_to_string(path) {
2843 Ok(contents) => contents,
2844 Err(err) => {
2845 let span = item.span(tcx).map_or(DUMMY_SP, |span| span.inner());
2846 tcx.dcx().span_err(span, format!("failed to read file {}: {err}", path.display()));
2847 return false;
2848 }
2849 };
2850
2851 assert!(!call_data.locations.is_empty());
2854 let min_loc =
2855 call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2856 let byte_min = min_loc.enclosing_item.byte_span.0;
2857 let line_min = min_loc.enclosing_item.line_span.0;
2858 let max_loc =
2859 call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
2860 let byte_max = max_loc.enclosing_item.byte_span.1;
2861 let line_max = max_loc.enclosing_item.line_span.1;
2862
2863 let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
2865
2866 let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
2869 .locations
2870 .iter()
2871 .map(|loc| {
2872 let (byte_lo, byte_hi) = loc.call_ident.byte_span;
2873 let (line_lo, line_hi) = loc.call_expr.line_span;
2874 let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
2875
2876 let line_range = (line_lo - line_min, line_hi - line_min);
2877 let (line_url, line_title) = link_to_loc(call_data, loc);
2878
2879 (byte_range, (line_range, line_url, line_title))
2880 })
2881 .unzip();
2882
2883 let (_, init_url, init_title) = &line_ranges[0];
2884 let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
2885 let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
2886
2887 let file_span = rustc_span::DUMMY_SP;
2893
2894 let mut decoration_info = FxIndexMap::default();
2895 decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
2896 decoration_info.insert("highlight", byte_ranges);
2897
2898 sources::print_src(
2899 w,
2900 contents_subset,
2901 file_span,
2902 cx,
2903 &cx.root_path(),
2904 &highlight::DecorationInfo(decoration_info),
2905 &sources::SourceContext::Embedded(sources::ScrapedInfo {
2906 needs_expansion,
2907 offset: line_min,
2908 name: &call_data.display_name,
2909 url: init_url,
2910 title: init_title,
2911 locations: locations_encoded,
2912 }),
2913 )
2914 .unwrap();
2915
2916 true
2917 };
2918
2919 let ordered_locations = {
2931 fn sort_criterion<'a>(
2932 (_, call_data): &(&PathBuf, &'a CallData),
2933 ) -> (bool, u32, &'a String) {
2934 let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2936 (!call_data.is_bin, hi - lo, &call_data.display_name)
2937 }
2938
2939 let mut locs = call_locations.iter().collect::<Vec<_>>();
2940 locs.sort_by_key(sort_criterion);
2941 locs
2942 };
2943
2944 let mut it = ordered_locations.into_iter().peekable();
2945
2946 let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
2949 for example in it.by_ref() {
2950 if write_example(&mut *w, example) {
2951 break;
2952 }
2953 }
2954 };
2955
2956 write_and_skip_failure(&mut w, &mut it);
2958
2959 if it.peek().is_some() {
2961 write!(
2962 w,
2963 "<details class=\"toggle more-examples-toggle\">\
2964 <summary class=\"hideme\">\
2965 <span>More examples</span>\
2966 </summary>\
2967 <div class=\"hide-more\">Hide additional examples</div>\
2968 <div class=\"more-scraped-examples\">\
2969 <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
2970 )?;
2971
2972 for _ in 0..MAX_FULL_EXAMPLES {
2975 write_and_skip_failure(&mut w, &mut it);
2976 }
2977
2978 if it.peek().is_some() {
2980 w.write_str(
2981 r#"<div class="example-links">Additional examples can be found in:<br><ul>"#,
2982 )?;
2983 it.try_for_each(|(_, call_data)| {
2984 let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
2985 write!(
2986 w,
2987 r#"<li><a href="{url}">{name}</a></li>"#,
2988 url = url,
2989 name = call_data.display_name
2990 )
2991 })?;
2992 w.write_str("</ul></div>")?;
2993 }
2994
2995 w.write_str("</div></details>")?;
2996 }
2997
2998 w.write_str("</div>")
2999}
3000
3001fn render_attributes_in_code(
3002 w: &mut impl fmt::Write,
3003 item: &clean::Item,
3004 prefix: impl fmt::Display,
3005 cx: &Context<'_>,
3006) -> fmt::Result {
3007 render_attributes_in_code_with_options(w, item, prefix, cx, true, "")
3008}
3009
3010pub(super) fn render_attributes_in_code_with_options(
3011 w: &mut impl fmt::Write,
3012 item: &clean::Item,
3013 prefix: impl fmt::Display,
3014 cx: &Context<'_>,
3015 render_doc_hidden: bool,
3016 open_tag: &str,
3017) -> fmt::Result {
3018 w.write_str(open_tag)?;
3019 if render_doc_hidden && item.is_doc_hidden() {
3020 render_code_attribute(&prefix, "#[doc(hidden)]", w)?;
3021 }
3022 for attr in &item.attrs.other_attrs {
3023 let hir::Attribute::Parsed(kind) = attr else { continue };
3024 let attr = match kind {
3025 AttributeKind::LinkSection { name, .. } => {
3026 Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}"))))
3027 }
3028 AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"),
3029 AttributeKind::ExportName { name, .. } => {
3030 Cow::Owned(format!("#[unsafe(export_name = {})]", Escape(&format!("{name:?}"))))
3031 }
3032 AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"),
3033 _ => continue,
3034 };
3035 render_code_attribute(&prefix, attr.as_ref(), w)?;
3036 }
3037
3038 if let Some(def_id) = item.def_id()
3039 && let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id)
3040 {
3041 render_code_attribute(prefix, &repr, w)?;
3042 }
3043 Ok(())
3044}
3045
3046fn render_repr_attribute_in_code(
3047 w: &mut impl fmt::Write,
3048 cx: &Context<'_>,
3049 def_id: DefId,
3050) -> fmt::Result {
3051 if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) {
3052 render_code_attribute("", &repr, w)?;
3053 }
3054 Ok(())
3055}
3056
3057fn render_code_attribute(
3058 prefix: impl fmt::Display,
3059 attr: impl fmt::Display,
3060 w: &mut impl fmt::Write,
3061) -> fmt::Result {
3062 write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>")
3063}
3064
3065fn repr_attribute<'tcx>(
3070 tcx: TyCtxt<'tcx>,
3071 cache: &Cache,
3072 def_id: DefId,
3073) -> Option<Cow<'static, str>> {
3074 let adt = match tcx.def_kind(def_id) {
3075 DefKind::Struct | DefKind::Enum | DefKind::Union => tcx.adt_def(def_id),
3076 _ => return None,
3077 };
3078 let repr = adt.repr();
3079
3080 let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id);
3081 let is_public_field = |field: &ty::FieldDef| {
3082 (cache.document_private || field.vis.is_public()) && is_visible(field.did)
3083 };
3084
3085 if repr.transparent() {
3086 let is_public = 'is_public: {
3089 let var = adt.variant(rustc_abi::FIRST_VARIANT); if !is_visible(var.def_id) {
3093 break 'is_public false;
3094 }
3095
3096 let non_1zst_field = var.fields.iter().find(|field| {
3098 let ty = ty::TypingEnv::post_analysis(tcx, field.did)
3099 .as_query_input(tcx.type_of(field.did).instantiate_identity().skip_norm_wip());
3100 tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst())
3101 });
3102
3103 match non_1zst_field {
3104 Some(field) => is_public_field(field),
3105 None => var.fields.is_empty() || var.fields.iter().any(is_public_field),
3106 }
3107 };
3108
3109 return is_public.then(|| "#[repr(transparent)]".into());
3112 }
3113
3114 let is_public = adt
3116 .variants()
3117 .iter()
3118 .all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field));
3119 if !is_public {
3120 return None;
3121 }
3122
3123 let mut result = Vec::<Cow<'_, _>>::new();
3124
3125 if repr.c() {
3126 result.push("C".into());
3127 }
3128 if repr.simd() {
3129 result.push("simd".into());
3130 }
3131 if let Some(int) = repr.int {
3132 let prefix = if int.is_signed() { 'i' } else { 'u' };
3133 let int = match int {
3134 rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"),
3135 rustc_abi::IntegerType::Fixed(int, _) => {
3136 format!("{prefix}{}", int.size().bytes() * 8)
3137 }
3138 };
3139 result.push(int.into());
3140 }
3141
3142 if let Some(pack) = repr.pack {
3144 result.push(format!("packed({})", pack.bytes()).into());
3145 }
3146 if let Some(align) = repr.align {
3147 result.push(format!("align({})", align.bytes()).into());
3148 }
3149
3150 (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into())
3151}