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() -> 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: crate::html::markdown::ErrorCodes::No,
685 edition: rustc_span::edition::LATEST_STABLE_EDITION,
686 playground: &Default::default(),
687 heading_offset: HeadingOffset::H1,
688 }
689 .write_into(f))
690 )
691}
692
693fn document(
694 cx: &Context<'_>,
695 item: &clean::Item,
696 parent: Option<&clean::Item>,
697 heading_offset: HeadingOffset,
698) -> impl fmt::Display {
699 if let Some(ref name) = item.name {
700 info!("Documenting {name}");
701 }
702
703 fmt::from_fn(move |f| {
704 document_item_info(cx, item, parent).render_into(f)?;
705 if parent.is_none() {
706 write!(f, "{}", document_full_collapsible(item, cx, heading_offset))
707 } else {
708 write!(f, "{}", document_full(item, cx, heading_offset))
709 }
710 })
711}
712
713fn render_markdown(
715 cx: &Context<'_>,
716 md_text: &str,
717 links: Vec<RenderedLink>,
718 heading_offset: HeadingOffset,
719) -> impl fmt::Display {
720 fmt::from_fn(move |f| {
721 f.write_str("<div class=\"docblock\">")?;
722 Markdown {
723 content: md_text,
724 links: &links,
725 ids: &mut cx.id_map.borrow_mut(),
726 error_codes: cx.shared.codes,
727 edition: cx.shared.edition(),
728 playground: &cx.shared.playground,
729 heading_offset,
730 }
731 .write_into(&mut *f)?;
732 f.write_str("</div>")
733 })
734}
735
736fn document_short(
739 item: &clean::Item,
740 cx: &Context<'_>,
741 link: AssocItemLink<'_>,
742 parent: &clean::Item,
743 show_def_docs: bool,
744) -> impl fmt::Display {
745 fmt::from_fn(move |f| {
746 document_item_info(cx, item, Some(parent)).render_into(f)?;
747 if !show_def_docs {
748 return Ok(());
749 }
750 let s = item.doc_value();
751 if !s.is_empty() {
752 let (mut summary_html, has_more_content) =
753 MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content();
754
755 let link = if has_more_content {
756 let link = fmt::from_fn(|f| {
757 write!(
758 f,
759 " <a{}>Read more</a>",
760 assoc_href_attr(item, link, cx).maybe_display()
761 )
762 });
763
764 if let Some(idx) = summary_html.rfind("</p>") {
765 summary_html.insert_str(idx, &link.to_string());
766 None
767 } else {
768 Some(link)
769 }
770 } else {
771 None
772 }
773 .maybe_display();
774
775 write!(f, "<div class='docblock'>{summary_html}{link}</div>")?;
776 }
777 Ok(())
778 })
779}
780
781fn document_full_collapsible(
782 item: &clean::Item,
783 cx: &Context<'_>,
784 heading_offset: HeadingOffset,
785) -> impl fmt::Display {
786 document_full_inner(item, cx, true, heading_offset)
787}
788
789fn document_full(
790 item: &clean::Item,
791 cx: &Context<'_>,
792 heading_offset: HeadingOffset,
793) -> impl fmt::Display {
794 document_full_inner(item, cx, false, heading_offset)
795}
796
797fn document_full_inner(
798 item: &clean::Item,
799 cx: &Context<'_>,
800 is_collapsible: bool,
801 heading_offset: HeadingOffset,
802) -> impl fmt::Display {
803 fmt::from_fn(move |f| {
804 if let Some(s) = item.opt_doc_value() {
805 debug!("Doc block: =====\n{s}\n=====");
806 if is_collapsible {
807 write!(
808 f,
809 "<details class=\"toggle top-doc\" open>\
810 <summary class=\"hideme\">\
811 <span>Expand description</span>\
812 </summary>{}</details>",
813 render_markdown(cx, &s, item.links(cx), heading_offset)
814 )?;
815 } else {
816 write!(f, "{}", render_markdown(cx, &s, item.links(cx), heading_offset))?;
817 }
818 }
819
820 let kind = match &item.kind {
821 clean::ItemKind::StrippedItem(kind) => kind,
822 kind => kind,
823 };
824
825 if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind {
826 render_call_locations(f, cx, item)?;
827 }
828 Ok(())
829 })
830}
831
832#[derive(Template)]
833#[template(path = "item_info.html")]
834struct ItemInfo {
835 items: Vec<ShortItemInfo>,
836}
837fn document_item_info(
843 cx: &Context<'_>,
844 item: &clean::Item,
845 parent: Option<&clean::Item>,
846) -> ItemInfo {
847 let items = short_item_info(item, cx, parent);
848 ItemInfo { items }
849}
850
851fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> {
852 let cfg = match (&item.cfg, parent.and_then(|p| p.cfg.as_ref())) {
853 (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
854 (cfg, _) => cfg.as_deref().cloned(),
855 };
856
857 debug!(
858 "Portability {name:?} {item_cfg:?} (parent: {parent:?}) - {parent_cfg:?} = {cfg:?}",
859 name = item.name,
860 item_cfg = item.cfg,
861 parent_cfg = parent.and_then(|p| p.cfg.as_ref()),
862 );
863
864 Some(cfg?.render_long_html())
865}
866
867#[derive(Template)]
868#[template(path = "short_item_info.html")]
869enum ShortItemInfo {
870 Deprecation {
872 message: String,
873 },
874 Unstable {
877 feature: String,
878 tracking: Option<(String, u32)>,
879 },
880 Portability {
881 message: String,
882 },
883}
884
885fn short_item_info(
888 item: &clean::Item,
889 cx: &Context<'_>,
890 parent: Option<&clean::Item>,
891) -> Vec<ShortItemInfo> {
892 let mut extra_info = vec![];
893
894 if let Some(depr @ Deprecation { note, since, suggestion: _ }) = item.deprecation(cx.tcx()) {
895 let mut message = match since {
898 DeprecatedSince::RustcVersion(version) => {
899 if depr.is_in_effect() {
900 format!("Deprecated since {version}")
901 } else {
902 format!("Deprecating in {version}")
903 }
904 }
905 DeprecatedSince::Future => String::from("Deprecating in a future version"),
906 DeprecatedSince::NonStandard(since) => {
907 format!("Deprecated since {}", Escape(since.as_str()))
908 }
909 DeprecatedSince::Unspecified | DeprecatedSince::Err => String::from("Deprecated"),
910 };
911
912 if let Some(note) = note {
913 let note = note.as_str();
914 let mut id_map = cx.id_map.borrow_mut();
915 let links = item.links(cx);
916 let html = MarkdownItemInfo::new(note, &links, &mut id_map);
917 message.push_str(": ");
918 html.write_into(&mut message).unwrap();
919 }
920 extra_info.push(ShortItemInfo::Deprecation { message });
921 }
922
923 if let Some((StabilityLevel::Unstable { reason: _, issue, .. }, feature)) = item
926 .stability(cx.tcx())
927 .as_ref()
928 .filter(|stab| stab.feature != sym::rustc_private)
929 .map(|stab| (stab.level, stab.feature))
930 {
931 let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue)
932 {
933 Some((url.clone(), issue.get()))
934 } else {
935 None
936 };
937 extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking });
938 }
939
940 if let Some(message) = portability(item, parent) {
941 extra_info.push(ShortItemInfo::Portability { message });
942 }
943
944 extra_info
945}
946
947fn impl_trait_key(cx: &Context<'_>, i: &Impl) -> Option<String> {
949 let trait_ = i.inner_impl().trait_.as_ref()?;
950 let prefix = match i.inner_impl().polarity {
951 ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
952 ty::ImplPolarity::Negative => "!",
953 };
954 Some(format!("{prefix}{:#}", print_path(trait_, cx)))
955}
956
957fn render_impls<'a, 'cx>(
960 cx: &'a Context<'cx>,
961 mut impls: Vec<&'a Impl>,
962 containing_item: &'a clean::Item,
963 toggle_open_by_default: bool,
964) -> impl fmt::Display + use<'a, 'cx> {
965 impls.sort_by_cached_key(|imp| {
966 let prefix = match imp.inner_impl().polarity {
967 ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => Ordering::Greater,
968 ty::ImplPolarity::Negative => Ordering::Less,
969 };
970 (prefix, ImplString::new_path(imp, cx))
971 });
972 fmt::from_fn(move |f| {
976 impls
977 .iter()
978 .map(|i| {
979 fmt::from_fn(|f| {
980 let did = i.trait_did().unwrap();
981 let provided_trait_methods = i.inner_impl().provided_trait_methods(cx.tcx());
982 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods);
983 render_impl(
984 cx,
985 i,
986 containing_item,
987 assoc_link,
988 RenderMode::Normal,
989 None,
990 &[],
991 ImplRenderingParameters {
992 show_def_docs: true,
993 show_default_items: true,
994 show_non_assoc_items: true,
995 toggle_open_by_default,
996 },
997 )
998 .fmt(f)
999 })
1000 })
1001 .joined("", f)
1002 })
1003}
1004
1005fn assoc_href_attr(
1007 it: &clean::Item,
1008 link: AssocItemLink<'_>,
1009 cx: &Context<'_>,
1010) -> Option<impl fmt::Display> {
1011 let name = it.name.unwrap();
1012 let item_type = it.type_();
1013
1014 enum Href<'a> {
1015 AnchorId(&'a str),
1016 Anchor(ItemType),
1017 Url(String, ItemType),
1018 }
1019
1020 let href = match link {
1021 AssocItemLink::Anchor(Some(id)) => Href::AnchorId(id),
1022 AssocItemLink::Anchor(None) => Href::Anchor(item_type),
1023 AssocItemLink::GotoSource(did, provided_methods) => {
1024 let item_type = match item_type {
1027 ItemType::Method | ItemType::TyMethod => {
1031 if provided_methods.contains(&name) {
1032 ItemType::Method
1033 } else {
1034 ItemType::TyMethod
1035 }
1036 }
1037 item_type => item_type,
1039 };
1040
1041 match href(did.expect_def_id(), cx) {
1042 Ok(HrefInfo { url, .. }) => Href::Url(url, item_type),
1043 Err(HrefError::DocumentationNotBuilt) => return None,
1055 Err(_) => Href::Anchor(item_type),
1056 }
1057 }
1058 };
1059
1060 let href = fmt::from_fn(move |f| match &href {
1061 Href::AnchorId(id) => write!(f, "#{id}"),
1062 Href::Url(url, item_type) => {
1063 write!(f, "{url}#{item_type}.{name}")
1064 }
1065 Href::Anchor(item_type) => {
1066 write!(f, "#{item_type}.{name}")
1067 }
1068 });
1069
1070 Some(fmt::from_fn(move |f| write!(f, " href=\"{href}\"")))
1073}
1074
1075#[derive(Debug)]
1076enum AssocConstValue<'a> {
1077 TraitDefault(&'a clean::ConstantKind),
1081 Impl(&'a clean::ConstantKind),
1083 None,
1084}
1085
1086fn assoc_const(
1087 it: &clean::Item,
1088 generics: &clean::Generics,
1089 ty: &clean::Type,
1090 value: AssocConstValue<'_>,
1091 link: AssocItemLink<'_>,
1092 indent: usize,
1093 cx: &Context<'_>,
1094) -> impl fmt::Display {
1095 let tcx = cx.tcx();
1096 fmt::from_fn(move |w| {
1097 render_attributes_in_code(w, it, &" ".repeat(indent), cx)?;
1098 write!(
1099 w,
1100 "{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
1101 indent = " ".repeat(indent),
1102 vis = visibility_print_with_space(it, cx),
1103 href = assoc_href_attr(it, link, cx).maybe_display(),
1104 name = it.name.as_ref().unwrap(),
1105 generics = print_generics(generics, cx),
1106 ty = print_type(ty, cx),
1107 )?;
1108 if let AssocConstValue::TraitDefault(konst) | AssocConstValue::Impl(konst) = value {
1109 let repr = konst.expr(tcx);
1110 if match value {
1111 AssocConstValue::TraitDefault(_) => true, AssocConstValue::Impl(_) => repr != "_", AssocConstValue::None => unreachable!(),
1116 } {
1117 write!(w, " = {}", Escape(&repr))?;
1118 }
1119 }
1120 write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display())
1121 })
1122}
1123
1124fn assoc_type(
1125 it: &clean::Item,
1126 generics: &clean::Generics,
1127 bounds: &[clean::GenericBound],
1128 default: Option<&clean::Type>,
1129 link: AssocItemLink<'_>,
1130 indent: usize,
1131 cx: &Context<'_>,
1132) -> impl fmt::Display {
1133 fmt::from_fn(move |w| {
1134 render_attributes_in_code(w, it, &" ".repeat(indent), cx)?;
1135 write!(
1136 w,
1137 "{indent}{vis}type <a{href} class=\"associatedtype\">{name}</a>{generics}",
1138 indent = " ".repeat(indent),
1139 vis = visibility_print_with_space(it, cx),
1140 href = assoc_href_attr(it, link, cx).maybe_display(),
1141 name = it.name.as_ref().unwrap(),
1142 generics = print_generics(generics, cx),
1143 )?;
1144 if !bounds.is_empty() {
1145 write!(w, ": {}", print_generic_bounds(bounds, cx))?;
1146 }
1147 if let Some(default) = default {
1149 write!(w, " = {}", print_type(default, cx))?;
1150 }
1151 write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display())
1152 })
1153}
1154
1155fn assoc_method(
1156 meth: &clean::Item,
1157 g: &clean::Generics,
1158 d: &clean::FnDecl,
1159 link: AssocItemLink<'_>,
1160 parent: ItemType,
1161 cx: &Context<'_>,
1162 render_mode: RenderMode,
1163) -> impl fmt::Display {
1164 let tcx = cx.tcx();
1165 let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item");
1166 let name = meth.name.as_ref().unwrap();
1167 let vis = visibility_print_with_space(meth, cx).to_string();
1168 let defaultness = match meth.defaultness().expect("Expected assoc method to have defaultness") {
1169 Defaultness::Implicit => "",
1170 Defaultness::Final => "final ",
1171 Defaultness::Default => "default ",
1172 };
1173 let constness = match render_mode {
1176 RenderMode::Normal => print_constness_with_space(
1177 &header.constness,
1178 meth.stable_since(tcx),
1179 meth.const_stability(tcx),
1180 ),
1181 RenderMode::ForDeref { .. } => "",
1182 };
1183
1184 fmt::from_fn(move |w| {
1185 let asyncness = header.asyncness.print_with_space();
1186 let safety = header.safety.print_with_space();
1187 let abi = print_abi_with_space(header.abi).to_string();
1188 let href = assoc_href_attr(meth, link, cx).maybe_display();
1189
1190 let generics_len = format!("{:#}", print_generics(g, cx)).len();
1192 let mut header_len = "fn ".len()
1193 + vis.len()
1194 + defaultness.len()
1195 + constness.len()
1196 + asyncness.len()
1197 + safety.len()
1198 + abi.len()
1199 + name.as_str().len()
1200 + generics_len;
1201
1202 let notable_traits = notable_traits_button(&d.output, cx).maybe_display();
1203
1204 let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
1205 header_len += 4;
1206 let indent_str = " ";
1207 render_attributes_in_code(w, meth, indent_str, cx)?;
1208 (4, indent_str, Ending::NoNewline)
1209 } else {
1210 render_attributes_in_code(w, meth, "", cx)?;
1211 (0, "", Ending::Newline)
1212 };
1213 write!(
1214 w,
1215 "{indent}{vis}{defaultness}{constness}{asyncness}{safety}{abi}fn \
1216 <a{href} class=\"fn\">{name}</a>{generics}{decl}{notable_traits}{where_clause}",
1217 indent = indent_str,
1218 generics = print_generics(g, cx),
1219 decl = full_print_fn_decl(d, header_len, indent, cx),
1220 where_clause = print_where_clause(g, cx, indent, end_newline).maybe_display(),
1221 )
1222 })
1223}
1224
1225fn render_stability_since_raw_with_extra(
1240 stable_version: Option<StableSince>,
1241 const_stability: Option<ConstStability>,
1242 extra_class: &str,
1243) -> Option<impl fmt::Display> {
1244 let mut title = String::new();
1245 let mut stability = String::new();
1246
1247 if let Some(version) = stable_version.and_then(|version| since_to_string(&version)) {
1248 stability.push_str(&version);
1249 title.push_str(&format!("Stable since Rust version {version}"));
1250 }
1251
1252 let const_title_and_stability = match const_stability {
1253 Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) => {
1254 since_to_string(&since)
1255 .map(|since| (format!("const since {since}"), format!("const: {since}")))
1256 }
1257 Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => {
1258 if stable_version.is_none() {
1259 None
1261 } else {
1262 let unstable = if let Some(n) = issue {
1263 format!(
1264 "<a \
1265 href=\"https://github.com/rust-lang/rust/issues/{n}\" \
1266 title=\"Tracking issue for {feature}\"\
1267 >unstable</a>"
1268 )
1269 } else {
1270 String::from("unstable")
1271 };
1272
1273 Some((String::from("const unstable"), format!("const: {unstable}")))
1274 }
1275 }
1276 _ => None,
1277 };
1278
1279 if let Some((const_title, const_stability)) = const_title_and_stability {
1280 if !title.is_empty() {
1281 title.push_str(&format!(", {const_title}"));
1282 } else {
1283 title.push_str(&const_title);
1284 }
1285
1286 if !stability.is_empty() {
1287 stability.push_str(&format!(" ({const_stability})"));
1288 } else {
1289 stability.push_str(&const_stability);
1290 }
1291 }
1292
1293 (!stability.is_empty()).then_some(fmt::from_fn(move |w| {
1294 write!(w, r#"<span class="since{extra_class}" title="{title}">{stability}</span>"#)
1295 }))
1296}
1297
1298fn since_to_string(since: &StableSince) -> Option<String> {
1299 match since {
1300 StableSince::Version(since) => Some(since.to_string()),
1301 StableSince::Current => Some(RustcVersion::CURRENT.to_string()),
1302 StableSince::Err(_) => None,
1303 }
1304}
1305
1306#[inline]
1307fn render_stability_since_raw(
1308 ver: Option<StableSince>,
1309 const_stability: Option<ConstStability>,
1310) -> Option<impl fmt::Display> {
1311 render_stability_since_raw_with_extra(ver, const_stability, "")
1312}
1313
1314fn render_assoc_item(
1315 item: &clean::Item,
1316 link: AssocItemLink<'_>,
1317 parent: ItemType,
1318 cx: &Context<'_>,
1319 render_mode: RenderMode,
1320) -> impl fmt::Display {
1321 fmt::from_fn(move |f| match &item.kind {
1322 clean::StrippedItem(..) => Ok(()),
1323 clean::RequiredMethodItem(m, _) | clean::MethodItem(m, _) => {
1324 assoc_method(item, &m.generics, &m.decl, link, parent, cx, render_mode).fmt(f)
1325 }
1326 clean::RequiredAssocConstItem(generics, ty) => assoc_const(
1327 item,
1328 generics,
1329 ty,
1330 AssocConstValue::None,
1331 link,
1332 if parent == ItemType::Trait { 4 } else { 0 },
1333 cx,
1334 )
1335 .fmt(f),
1336 clean::ProvidedAssocConstItem(ci) => assoc_const(
1337 item,
1338 &ci.generics,
1339 &ci.type_,
1340 AssocConstValue::TraitDefault(&ci.kind),
1341 link,
1342 if parent == ItemType::Trait { 4 } else { 0 },
1343 cx,
1344 )
1345 .fmt(f),
1346 clean::ImplAssocConstItem(ci) => assoc_const(
1347 item,
1348 &ci.generics,
1349 &ci.type_,
1350 AssocConstValue::Impl(&ci.kind),
1351 link,
1352 if parent == ItemType::Trait { 4 } else { 0 },
1353 cx,
1354 )
1355 .fmt(f),
1356 clean::RequiredAssocTypeItem(generics, bounds) => assoc_type(
1357 item,
1358 generics,
1359 bounds,
1360 None,
1361 link,
1362 if parent == ItemType::Trait { 4 } else { 0 },
1363 cx,
1364 )
1365 .fmt(f),
1366 clean::AssocTypeItem(ty, bounds) => assoc_type(
1367 item,
1368 &ty.generics,
1369 bounds,
1370 Some(ty.item_type.as_ref().unwrap_or(&ty.type_)),
1371 link,
1372 if parent == ItemType::Trait { 4 } else { 0 },
1373 cx,
1374 )
1375 .fmt(f),
1376 _ => panic!("render_assoc_item called on non-associated-item"),
1377 })
1378}
1379
1380#[derive(Copy, Clone)]
1381enum AssocItemLink<'a> {
1382 Anchor(Option<&'a str>),
1383 GotoSource(ItemId, &'a FxIndexSet<Symbol>),
1384}
1385
1386impl<'a> AssocItemLink<'a> {
1387 fn anchor(&self, id: &'a str) -> Self {
1388 match *self {
1389 AssocItemLink::Anchor(_) => AssocItemLink::Anchor(Some(id)),
1390 ref other => *other,
1391 }
1392 }
1393}
1394
1395fn write_section_heading(
1396 title: impl fmt::Display,
1397 id: &str,
1398 extra_class: Option<&str>,
1399 extra: impl fmt::Display,
1400) -> impl fmt::Display {
1401 fmt::from_fn(move |w| {
1402 let (extra_class, whitespace) = match extra_class {
1403 Some(extra) => (extra, " "),
1404 None => ("", ""),
1405 };
1406 write!(
1407 w,
1408 "<h2 id=\"{id}\" class=\"{extra_class}{whitespace}section-header\">\
1409 {title}\
1410 <a href=\"#{id}\" class=\"anchor\">§</a>\
1411 </h2>{extra}",
1412 )
1413 })
1414}
1415
1416fn write_impl_section_heading(title: impl fmt::Display, id: &str) -> impl fmt::Display {
1417 write_section_heading(title, id, None, "")
1418}
1419
1420fn render_all_impls(
1421 mut w: impl Write,
1422 cx: &Context<'_>,
1423 containing_item: &clean::Item,
1424 concrete_impls: Vec<&Impl>,
1425 auto_trait_impls: Vec<&Impl>,
1426 blanket_impls: Vec<&Impl>,
1427) -> fmt::Result {
1428 if !concrete_impls.is_empty() {
1429 let impls = render_impls(cx, concrete_impls, containing_item, true);
1430 write!(
1431 w,
1432 "{}<div id=\"trait-implementations-list\">{impls}</div>",
1433 write_impl_section_heading("Trait Implementations", "trait-implementations")
1434 )?;
1435 }
1436
1437 if !auto_trait_impls.is_empty() {
1438 let impls = render_impls(cx, auto_trait_impls, containing_item, false);
1439 write!(
1441 w,
1442 "{}<div id=\"synthetic-implementations-list\">{impls}</div>",
1443 write_impl_section_heading("Auto Trait Implementations", "synthetic-implementations",)
1444 )?;
1445 }
1446
1447 if !blanket_impls.is_empty() {
1448 let impls = render_impls(cx, blanket_impls, containing_item, false);
1449 write!(
1450 w,
1451 "{}<div id=\"blanket-implementations-list\">{impls}</div>",
1452 write_impl_section_heading("Blanket Implementations", "blanket-implementations")
1453 )?;
1454 }
1455
1456 Ok(())
1457}
1458
1459fn render_assoc_items(
1460 cx: &Context<'_>,
1461 containing_item: &clean::Item,
1462 it: DefId,
1463 what: AssocItemRender<'_>,
1464) -> impl fmt::Display {
1465 fmt::from_fn(move |f| {
1466 let mut derefs = DefIdSet::default();
1467 derefs.insert(it);
1468 render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs)
1469 })
1470}
1471
1472fn render_assoc_items_inner(
1473 mut w: &mut dyn fmt::Write,
1474 cx: &Context<'_>,
1475 containing_item: &clean::Item,
1476 it: DefId,
1477 what: AssocItemRender<'_>,
1478 derefs: &mut DefIdSet,
1479) -> fmt::Result {
1480 info!("Documenting associated items of {:?}", containing_item.name);
1481 let cache = &cx.shared.cache;
1482 let Some(impls) = cache.impls.get(&it) else { return Ok(()) };
1483 let (mut inherent_impls, trait_impls): (Vec<_>, _) =
1484 impls.iter().partition(|i| i.inner_impl().trait_.is_none());
1485 if !inherent_impls.is_empty() {
1486 let render_mode = what.render_mode();
1487 let class_html = what
1488 .class()
1489 .map(|class| fmt::from_fn(move |f| write!(f, r#" class="{class}""#)))
1490 .maybe_display();
1491 let (section_heading, id) = match what {
1492 AssocItemRender::All => (
1493 Either::Left(write_impl_section_heading("Implementations", "implementations")),
1494 Cow::Borrowed("implementations-list"),
1495 ),
1496 AssocItemRender::DerefFor { trait_, type_, .. } => {
1497 let id = cx.derive_id(small_url_encode(format!(
1498 "deref-methods-{:#}",
1499 print_type(type_, cx)
1500 )));
1501 inherent_impls.retain(|impl_| {
1509 type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache)
1510 });
1511 let derived_id = cx.derive_id(&id);
1512 if let Some(def_id) = type_.def_id(cx.cache()) {
1513 cx.deref_id_map.borrow_mut().insert(def_id, id.clone());
1514 }
1515 (
1516 Either::Right(fmt::from_fn(move |f| {
1517 write!(
1518 f,
1519 "<details class=\"toggle big-toggle\" open><summary>{}</summary>",
1520 write_impl_section_heading(
1521 fmt::from_fn(|f| write!(
1522 f,
1523 "<span>Methods from {trait_}<Target = {type_}></span>",
1524 trait_ = print_path(trait_, cx),
1525 type_ = print_type(type_, cx),
1526 )),
1527 &id,
1528 )
1529 )
1530 })),
1531 Cow::Owned(derived_id),
1532 )
1533 }
1534 };
1535 let inherent_impls_buf = fmt::from_fn(|f| {
1536 inherent_impls
1537 .iter()
1538 .map(|i| {
1539 render_impl(
1540 cx,
1541 i,
1542 containing_item,
1543 AssocItemLink::Anchor(None),
1544 render_mode,
1545 None,
1546 &[],
1547 ImplRenderingParameters {
1548 show_def_docs: true,
1549 show_default_items: true,
1550 show_non_assoc_items: true,
1551 toggle_open_by_default: true,
1552 },
1553 )
1554 })
1555 .joined("", f)
1556 })
1557 .to_string();
1558
1559 if !inherent_impls_buf.is_empty() {
1560 write!(
1561 w,
1562 "{section_heading}<div id=\"{id}\"{class_html}>{inherent_impls_buf}</div>{}",
1563 matches!(what, AssocItemRender::DerefFor { .. })
1564 .then_some("</details>")
1565 .maybe_display(),
1566 )?;
1567 }
1568 }
1569
1570 if !trait_impls.is_empty() {
1571 let deref_impl = trait_impls.iter().find(|t| {
1572 t.trait_did() == cx.tcx().lang_items().deref_trait() && !t.is_negative_trait_impl()
1573 });
1574 if let Some(impl_) = deref_impl {
1575 let has_deref_mut = trait_impls
1576 .iter()
1577 .any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
1578 render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs)?;
1579 }
1580
1581 if let AssocItemRender::DerefFor { .. } = what {
1584 return Ok(());
1585 }
1586
1587 let (auto_trait_impls, trait_impls): (Vec<&Impl>, Vec<&Impl>) =
1588 trait_impls.into_iter().partition(|t| t.inner_impl().kind.is_auto());
1589 let (blanket_impls, concrete_impls): (Vec<&Impl>, _) =
1590 trait_impls.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
1591
1592 render_all_impls(w, cx, containing_item, concrete_impls, auto_trait_impls, blanket_impls)?;
1593 }
1594 Ok(())
1595}
1596
1597fn render_deref_methods(
1599 mut w: impl Write,
1600 cx: &Context<'_>,
1601 impl_: &Impl,
1602 container_item: &clean::Item,
1603 deref_mut: bool,
1604 derefs: &mut DefIdSet,
1605) -> fmt::Result {
1606 let cache = cx.cache();
1607 let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
1608 let (target, real_target) = impl_
1609 .inner_impl()
1610 .items
1611 .iter()
1612 .find_map(|item| match item.kind {
1613 clean::AssocTypeItem(ref t, _) => Some(match *t {
1614 clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
1615 _ => (&t.type_, &t.type_),
1616 }),
1617 _ => None,
1618 })
1619 .expect("Expected associated type binding");
1620 debug!(
1621 "Render deref methods for {for_:#?}, target {target:#?}",
1622 for_ = impl_.inner_impl().for_
1623 );
1624 let what =
1625 AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
1626 if let Some(did) = target.def_id(cache) {
1627 if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) {
1628 if did == type_did || !derefs.insert(did) {
1630 return Ok(());
1632 }
1633 }
1634 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1635 } else if let Some(prim) = target.primitive_type()
1636 && let Some(&did) = cache.primitive_locations.get(&prim)
1637 {
1638 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1639 }
1640 Ok(())
1641}
1642
1643fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool {
1644 let self_type_opt = match item.kind {
1645 clean::MethodItem(ref method, _) => method.decl.receiver_type(),
1646 clean::RequiredMethodItem(ref method, _) => method.decl.receiver_type(),
1647 _ => None,
1648 };
1649
1650 if let Some(self_ty) = self_type_opt {
1651 let (by_mut_ref, by_box, by_value) = match *self_ty {
1652 clean::Type::BorrowedRef { mutability, .. } => {
1653 (mutability == Mutability::Mut, false, false)
1654 }
1655 clean::Type::Path { ref path } => {
1656 (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false)
1657 }
1658 clean::Type::SelfTy => (false, false, true),
1659 _ => (false, false, false),
1660 };
1661
1662 (deref_mut_ || !by_mut_ref) && !by_box && !by_value
1663 } else {
1664 false
1665 }
1666}
1667
1668fn is_notable_trait_passthrough(did: DefId, cx: &Context<'_>) -> bool {
1673 let lang_items = cx.tcx().lang_items();
1674 Some(did) == lang_items.owned_box() || Some(did) == lang_items.pin_type()
1675}
1676
1677fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option<impl fmt::Display> {
1678 if ty.is_unit() {
1679 return None;
1681 }
1682
1683 let did = ty.def_id(cx.cache())?;
1684
1685 if is_notable_trait_passthrough(did, cx) {
1686 return None;
1687 }
1688
1689 let impls = cx.cache().impls.get(&did)?;
1690 let has_notable_trait = impls
1691 .iter()
1692 .map(Impl::inner_impl)
1693 .filter(|impl_| {
1694 impl_.polarity == ty::ImplPolarity::Positive
1695 && ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1698 })
1699 .filter_map(|impl_| impl_.trait_.as_ref())
1700 .filter_map(|trait_| cx.cache().traits.get(&trait_.def_id()))
1701 .any(|t| t.is_notable_trait(cx.tcx()));
1702
1703 has_notable_trait.then(|| {
1704 cx.types_with_notable_traits.borrow_mut().insert(ty.clone());
1705 fmt::from_fn(|f| {
1706 write!(
1707 f,
1708 " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>",
1709 ty = Escape(&format!("{:#}", print_type(ty, cx))),
1710 )
1711 })
1712 })
1713}
1714
1715fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
1716 let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
1717
1718 let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");
1719
1720 let out = fmt::from_fn(|f| {
1721 let mut notable_impls = impls
1722 .iter()
1723 .map(|impl_| impl_.inner_impl())
1724 .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive)
1725 .filter(|impl_| {
1726 ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1728 })
1729 .filter_map(|impl_| {
1730 if let Some(trait_) = &impl_.trait_
1731 && let trait_did = trait_.def_id()
1732 && let Some(trait_) = cx.cache().traits.get(&trait_did)
1733 && trait_.is_notable_trait(cx.tcx())
1734 {
1735 Some((impl_, trait_did))
1736 } else {
1737 None
1738 }
1739 })
1740 .peekable();
1741
1742 let has_notable_impl = if let Some((impl_, _)) = notable_impls.peek() {
1743 write!(
1744 f,
1745 "<h3>Notable traits for <code>{}</code></h3>\
1746 <pre><code>",
1747 print_type(&impl_.for_, cx),
1748 )?;
1749 true
1750 } else {
1751 false
1752 };
1753
1754 for (impl_, trait_did) in notable_impls {
1755 write!(f, "<div class=\"where\">{}</div>", print_impl(impl_, false, cx))?;
1756 for it in &impl_.items {
1757 let clean::AssocTypeItem(tydef, ..) = &it.kind else {
1758 continue;
1759 };
1760
1761 let empty_set = FxIndexSet::default();
1762 let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
1763
1764 write!(
1765 f,
1766 "<div class=\"where\"> {};</div>",
1767 assoc_type(
1768 it,
1769 &tydef.generics,
1770 &[], Some(&tydef.type_),
1772 src_link,
1773 0,
1774 cx,
1775 )
1776 )?;
1777 }
1778 }
1779
1780 if !has_notable_impl {
1781 f.write_str("</code></pre>")?;
1782 }
1783
1784 Ok(())
1785 })
1786 .to_string();
1787
1788 (format!("{:#}", print_type(ty, cx)), out)
1789}
1790
1791fn notable_traits_json<'a>(tys: impl Iterator<Item = &'a clean::Type>, cx: &Context<'_>) -> String {
1792 let mut mp = tys.map(|ty| notable_traits_decl(ty, cx)).collect::<IndexMap<_, _>>();
1793 mp.sort_unstable_keys();
1794 serde_json::to_string(&mp).expect("serialize (string, string) -> json object cannot fail")
1795}
1796
1797pub(crate) struct NotableTraitBadge {
1798 pub name: String,
1799 pub full_path: String,
1800 pub href: Option<String>,
1802}
1803
1804pub(crate) fn notable_trait_badges(item: &clean::Item, cx: &Context<'_>) -> Vec<NotableTraitBadge> {
1807 let tcx = cx.tcx();
1808 if let Some(def_id) = item.def_id()
1809 && !is_notable_trait_passthrough(def_id, cx)
1810 && let Some(impls) = cx.cache().impls.get(&def_id)
1811 {
1812 impls
1813 .iter()
1814 .map(Impl::inner_impl)
1815 .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive)
1816 .filter_map(|impl_| {
1817 if let Some(trait_) = &impl_.trait_
1818 && let trait_did = trait_.def_id()
1819 && let Some(trait_) = cx.cache().traits.get(&trait_did)
1820 && trait_.is_notable_trait(tcx)
1821 {
1822 let name = tcx.item_name(trait_did).to_string();
1823 let (full_path, href) = match href(trait_did, cx) {
1824 Ok(info) => (join_path_syms(&info.rust_path), Some(info.url)),
1825 Err(_) => (tcx.def_path_str(trait_did), None),
1826 };
1827 Some((name.clone(), NotableTraitBadge { name, full_path, href }))
1828 } else {
1829 None
1830 }
1831 })
1832 .collect::<BTreeMap<String, NotableTraitBadge>>()
1833 .into_values()
1834 .collect()
1835 } else {
1836 Vec::new()
1837 }
1838}
1839
1840#[derive(Clone, Copy, Debug)]
1841struct ImplRenderingParameters {
1842 show_def_docs: bool,
1843 show_default_items: bool,
1844 show_non_assoc_items: bool,
1846 toggle_open_by_default: bool,
1847}
1848
1849fn render_impl(
1850 cx: &Context<'_>,
1851 i: &Impl,
1852 parent: &clean::Item,
1853 link: AssocItemLink<'_>,
1854 render_mode: RenderMode,
1855 use_absolute: Option<bool>,
1856 aliases: &[String],
1857 rendering_params: ImplRenderingParameters,
1858) -> impl fmt::Display {
1859 fmt::from_fn(move |w| {
1860 let cache = &cx.shared.cache;
1861 let traits = &cache.traits;
1862 let trait_ = i.trait_did().map(|did| &traits[&did]);
1863 let mut close_tags = <Vec<&str>>::with_capacity(2);
1864
1865 fn doc_impl_item(
1871 boring: impl fmt::Write,
1872 interesting: impl fmt::Write,
1873 cx: &Context<'_>,
1874 item: &clean::Item,
1875 parent: &clean::Item,
1876 link: AssocItemLink<'_>,
1877 render_mode: RenderMode,
1878 is_default_item: bool,
1879 trait_: Option<&clean::Trait>,
1880 rendering_params: ImplRenderingParameters,
1881 ) -> fmt::Result {
1882 let item_type = item.type_();
1883 let name = item.name.as_ref().unwrap();
1884
1885 let render_method_item = rendering_params.show_non_assoc_items
1886 && match render_mode {
1887 RenderMode::Normal => true,
1888 RenderMode::ForDeref { mut_: deref_mut_ } => {
1889 should_render_item(item, deref_mut_, cx.tcx())
1890 }
1891 };
1892
1893 let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
1894
1895 let mut doc_buffer = String::new();
1896 let mut info_buffer = String::new();
1897 let mut short_documented = true;
1898
1899 let mut trait_item_deprecated = false;
1900 if render_method_item {
1901 if !is_default_item {
1902 if let Some(t) = trait_ {
1903 if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1906 trait_item_deprecated = it.is_deprecated(cx.tcx());
1907 if !item.doc_value().is_empty() {
1910 document_item_info(cx, it, Some(parent))
1911 .render_into(&mut info_buffer)?;
1912 doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1913 short_documented = false;
1914 } else {
1915 doc_buffer = document_short(
1918 it,
1919 cx,
1920 link,
1921 parent,
1922 rendering_params.show_def_docs,
1923 )
1924 .to_string();
1925 }
1926 }
1927 } else {
1928 document_item_info(cx, item, Some(parent)).render_into(&mut info_buffer)?;
1929 if rendering_params.show_def_docs {
1930 doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1931 short_documented = false;
1932 }
1933 }
1934 } else {
1935 doc_buffer =
1936 document_short(item, cx, link, parent, rendering_params.show_def_docs)
1937 .to_string();
1938 }
1939 }
1940 let mut w = if short_documented && trait_.is_some() {
1941 Either::Left(interesting)
1942 } else {
1943 Either::Right(boring)
1944 };
1945
1946 let mut deprecation_class = if trait_item_deprecated || item.is_deprecated(cx.tcx()) {
1947 " deprecated"
1948 } else {
1949 ""
1950 };
1951
1952 let toggled = !doc_buffer.is_empty();
1953 if toggled {
1954 let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
1955 write!(
1956 w,
1957 "<details class=\"toggle{method_toggle_class}{deprecation_class}\" open><summary>"
1958 )?;
1959 deprecation_class = "";
1960 }
1961 match &item.kind {
1962 clean::MethodItem(..) | clean::RequiredMethodItem(..) => {
1963 if render_method_item {
1965 let id = cx.derive_id(format!("{item_type}.{name}"));
1966 let source_id = trait_
1967 .and_then(|trait_| {
1968 trait_
1969 .items
1970 .iter()
1971 .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
1972 })
1973 .map(|item| format!("{}.{name}", item.type_()));
1974 write!(
1975 w,
1976 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1977 {}",
1978 render_rightside(cx, item, render_mode)
1979 )?;
1980 if trait_.is_some() {
1981 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1983 }
1984 write!(
1985 w,
1986 "<h4 class=\"code-header\">{}</h4></section>",
1987 render_assoc_item(
1988 item,
1989 link.anchor(source_id.as_ref().unwrap_or(&id)),
1990 ItemType::Impl,
1991 cx,
1992 render_mode,
1993 ),
1994 )?;
1995 }
1996 }
1997 clean::RequiredAssocConstItem(generics, ty) => {
1998 let source_id = format!("{item_type}.{name}");
1999 let id = cx.derive_id(&source_id);
2000 write!(
2001 w,
2002 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
2003 {}",
2004 render_rightside(cx, item, render_mode)
2005 )?;
2006 if trait_.is_some() {
2007 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
2009 }
2010 write!(
2011 w,
2012 "<h4 class=\"code-header\">{}</h4></section>",
2013 assoc_const(
2014 item,
2015 generics,
2016 ty,
2017 AssocConstValue::None,
2018 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2019 0,
2020 cx,
2021 ),
2022 )?;
2023 }
2024 clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
2025 let source_id = format!("{item_type}.{name}");
2026 let id = cx.derive_id(&source_id);
2027 write!(
2028 w,
2029 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
2030 {}",
2031 render_rightside(cx, item, render_mode),
2032 )?;
2033 if trait_.is_some() {
2034 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
2036 }
2037 write!(
2038 w,
2039 "<h4 class=\"code-header\">{}</h4></section>",
2040 assoc_const(
2041 item,
2042 &ci.generics,
2043 &ci.type_,
2044 match item.kind {
2045 clean::ProvidedAssocConstItem(_) =>
2046 AssocConstValue::TraitDefault(&ci.kind),
2047 clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
2048 _ => unreachable!(),
2049 },
2050 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2051 0,
2052 cx,
2053 ),
2054 )?;
2055 }
2056 clean::RequiredAssocTypeItem(generics, bounds) => {
2057 let source_id = format!("{item_type}.{name}");
2058 let id = cx.derive_id(&source_id);
2059 write!(
2060 w,
2061 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
2062 {}",
2063 render_rightside(cx, item, render_mode),
2064 )?;
2065 if trait_.is_some() {
2066 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
2068 }
2069 write!(
2070 w,
2071 "<h4 class=\"code-header\">{}</h4></section>",
2072 assoc_type(
2073 item,
2074 generics,
2075 bounds,
2076 None,
2077 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2078 0,
2079 cx,
2080 ),
2081 )?;
2082 }
2083 clean::AssocTypeItem(tydef, _bounds) => {
2084 let source_id = format!("{item_type}.{name}");
2085 let id = cx.derive_id(&source_id);
2086 write!(
2087 w,
2088 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
2089 {}",
2090 render_rightside(cx, item, render_mode),
2091 )?;
2092 if trait_.is_some() {
2093 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
2095 }
2096 write!(
2097 w,
2098 "<h4 class=\"code-header\">{}</h4></section>",
2099 assoc_type(
2100 item,
2101 &tydef.generics,
2102 &[], Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
2104 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2105 0,
2106 cx,
2107 ),
2108 )?;
2109 }
2110 clean::StrippedItem(..) => return Ok(()),
2111 _ => panic!("can't make docs for trait item with name {:?}", item.name),
2112 }
2113
2114 w.write_str(&info_buffer)?;
2115 if toggled {
2116 write!(w, "</summary>{doc_buffer}</details>")?;
2117 }
2118 Ok(())
2119 }
2120
2121 let mut impl_items = String::new();
2122 let mut default_impl_items = String::new();
2123 let impl_ = i.inner_impl();
2124
2125 let mut assoc_types = Vec::new();
2135 let mut methods = Vec::new();
2136
2137 if !impl_.is_negative_trait_impl() {
2138 for impl_item in &impl_.items {
2139 match impl_item.kind {
2140 clean::MethodItem(..) | clean::RequiredMethodItem(..) => {
2141 methods.push(impl_item)
2142 }
2143 clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => {
2144 assoc_types.push(impl_item)
2145 }
2146 clean::RequiredAssocConstItem(..)
2147 | clean::ProvidedAssocConstItem(_)
2148 | clean::ImplAssocConstItem(_) => {
2149 doc_impl_item(
2151 &mut default_impl_items,
2152 &mut impl_items,
2153 cx,
2154 impl_item,
2155 if trait_.is_some() { &i.impl_item } else { parent },
2156 link,
2157 render_mode,
2158 false,
2159 trait_,
2160 rendering_params,
2161 )?;
2162 }
2163 _ => {}
2164 }
2165 }
2166
2167 for assoc_type in assoc_types {
2168 doc_impl_item(
2169 &mut default_impl_items,
2170 &mut impl_items,
2171 cx,
2172 assoc_type,
2173 if trait_.is_some() { &i.impl_item } else { parent },
2174 link,
2175 render_mode,
2176 false,
2177 trait_,
2178 rendering_params,
2179 )?;
2180 }
2181 for method in methods {
2182 doc_impl_item(
2183 &mut default_impl_items,
2184 &mut impl_items,
2185 cx,
2186 method,
2187 if trait_.is_some() { &i.impl_item } else { parent },
2188 link,
2189 render_mode,
2190 false,
2191 trait_,
2192 rendering_params,
2193 )?;
2194 }
2195 }
2196
2197 fn render_default_items(
2198 mut boring: impl fmt::Write,
2199 mut interesting: impl fmt::Write,
2200 cx: &Context<'_>,
2201 t: &clean::Trait,
2202 i: &clean::Impl,
2203 parent: &clean::Item,
2204 render_mode: RenderMode,
2205 rendering_params: ImplRenderingParameters,
2206 ) -> fmt::Result {
2207 for trait_item in &t.items {
2208 if let Some(impl_def_id) = parent.item_id.as_def_id()
2211 && let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
2212 && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id))
2213 {
2214 continue;
2215 }
2216
2217 let n = trait_item.name;
2218 if i.items.iter().any(|m| m.name == n) {
2219 continue;
2220 }
2221 let did = i.trait_.as_ref().unwrap().def_id();
2222 let provided_methods = i.provided_trait_methods(cx.tcx());
2223 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
2224
2225 doc_impl_item(
2226 &mut boring,
2227 &mut interesting,
2228 cx,
2229 trait_item,
2230 parent,
2231 assoc_link,
2232 render_mode,
2233 true,
2234 Some(t),
2235 rendering_params,
2236 )?;
2237 }
2238 Ok(())
2239 }
2240
2241 if rendering_params.show_default_items
2246 && let Some(t) = trait_
2247 && !impl_.is_negative_trait_impl()
2248 {
2249 render_default_items(
2250 &mut default_impl_items,
2251 &mut impl_items,
2252 cx,
2253 t,
2254 impl_,
2255 &i.impl_item,
2256 render_mode,
2257 rendering_params,
2258 )?;
2259 }
2260 if render_mode == RenderMode::Normal {
2261 let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
2262 let deprecation_attr = if impl_.is_deprecated
2263 || trait_.is_some_and(|trait_| trait_.is_deprecated(cx.tcx()))
2264 {
2265 " deprecated"
2266 } else {
2267 ""
2268 };
2269 if toggled {
2270 close_tags.push("</details>");
2271 write!(
2272 w,
2273 "<details class=\"toggle implementors-toggle{deprecation_attr}\"{}>\
2274 <summary>",
2275 if rendering_params.toggle_open_by_default { " open" } else { "" }
2276 )?;
2277 }
2278
2279 let (before_dox, after_dox) = i
2280 .impl_item
2281 .opt_doc_value()
2282 .map(|dox| {
2283 Markdown {
2284 content: &dox,
2285 links: &i.impl_item.links(cx),
2286 ids: &mut cx.id_map.borrow_mut(),
2287 error_codes: cx.shared.codes,
2288 edition: cx.shared.edition(),
2289 playground: &cx.shared.playground,
2290 heading_offset: HeadingOffset::H4,
2291 }
2292 .split_summary_and_content()
2293 })
2294 .unwrap_or((None, None));
2295
2296 write!(
2297 w,
2298 "{}",
2299 render_impl_summary(
2300 cx,
2301 i,
2302 parent,
2303 rendering_params.show_def_docs,
2304 use_absolute,
2305 aliases,
2306 before_dox.as_deref(),
2307 trait_.is_none() && impl_.items.is_empty(),
2308 )
2309 )?;
2310 if toggled {
2311 w.write_str("</summary>")?;
2312 }
2313
2314 if before_dox.is_some()
2315 && let Some(after_dox) = after_dox
2316 {
2317 write!(w, "<div class=\"docblock\">{after_dox}</div>")?;
2318 }
2319
2320 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2321 w.write_str("<div class=\"impl-items\">")?;
2322 close_tags.push("</div>");
2323 }
2324 }
2325 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2326 w.write_str(&default_impl_items)?;
2327 w.write_str(&impl_items)?;
2328 }
2329 for tag in close_tags.into_iter().rev() {
2330 w.write_str(tag)?;
2331 }
2332 Ok(())
2333 })
2334}
2335
2336fn render_rightside(
2339 cx: &Context<'_>,
2340 item: &clean::Item,
2341 render_mode: RenderMode,
2342) -> impl fmt::Display {
2343 let tcx = cx.tcx();
2344
2345 fmt::from_fn(move |w| {
2346 let const_stability = match render_mode {
2349 RenderMode::Normal => item.const_stability(tcx),
2350 RenderMode::ForDeref { .. } => None,
2351 };
2352 let src_href = cx.src_href(item);
2353 let stability = render_stability_since_raw_with_extra(
2354 item.stable_since(tcx),
2355 const_stability,
2356 if src_href.is_some() { "" } else { " rightside" },
2357 );
2358
2359 match (stability, src_href) {
2360 (Some(stability), Some(link)) => {
2361 write!(
2362 w,
2363 "<span class=\"rightside\">{stability} · <a class=\"src\" href=\"{link}\">Source</a></span>",
2364 )
2365 }
2366 (Some(stability), None) => {
2367 write!(w, "{stability}")
2368 }
2369 (None, Some(link)) => {
2370 write!(w, "<a class=\"src rightside\" href=\"{link}\">Source</a>")
2371 }
2372 (None, None) => Ok(()),
2373 }
2374 })
2375}
2376
2377fn render_impl_summary(
2378 cx: &Context<'_>,
2379 i: &Impl,
2380 parent: &clean::Item,
2381 show_def_docs: bool,
2382 use_absolute: Option<bool>,
2383 aliases: &[String],
2386 doc: Option<&str>,
2387 impl_is_empty: bool,
2388) -> impl fmt::Display {
2389 fmt::from_fn(move |w| {
2390 let inner_impl = i.inner_impl();
2391 let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
2392 let aliases = (!aliases.is_empty())
2393 .then_some(fmt::from_fn(|f| {
2394 write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f)))
2395 }))
2396 .maybe_display();
2397 write!(
2398 w,
2399 "<section id=\"{id}\" class=\"impl\"{aliases}>\
2400 {}\
2401 <a href=\"#{id}\" class=\"anchor\">§</a>\
2402 <h3 class=\"code-header\">",
2403 render_rightside(cx, &i.impl_item, RenderMode::Normal)
2404 )?;
2405
2406 if let Some(use_absolute) = use_absolute {
2407 write!(w, "{}", print_impl(inner_impl, use_absolute, cx))?;
2408 if show_def_docs {
2409 for it in &inner_impl.items {
2410 if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
2411 write!(
2412 w,
2413 "<div class=\"where\"> {};</div>",
2414 assoc_type(
2415 it,
2416 &tydef.generics,
2417 &[], Some(&tydef.type_),
2419 AssocItemLink::Anchor(None),
2420 0,
2421 cx,
2422 )
2423 )?;
2424 }
2425 }
2426 }
2427 } else {
2428 write!(w, "{}", print_impl(inner_impl, false, cx))?;
2429 }
2430 w.write_str("</h3>")?;
2431
2432 let is_trait = inner_impl.trait_.is_some();
2433 if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) {
2434 write!(
2435 w,
2436 "<span class=\"item-info\">\
2437 <div class=\"stab portability\">{portability}</div>\
2438 </span>",
2439 )?;
2440 }
2441
2442 if let Some(doc) = doc {
2443 if impl_is_empty {
2444 w.write_str(
2445 "\
2446<div class=\"item-info\">\
2447 <div class=\"stab empty-impl\">This impl block contains no public items.</div>\
2448</div>",
2449 )?;
2450 }
2451 write!(w, "<div class=\"docblock\">{doc}</div>")?;
2452 }
2453
2454 w.write_str("</section>")
2455 })
2456}
2457
2458pub(crate) fn small_url_encode(s: String) -> String {
2459 fn dont_escape(c: u8) -> bool {
2464 c.is_ascii_alphanumeric()
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'?'
2479 || c == b'='
2483 }
2484 let mut st = String::new();
2485 let mut last_match = 0;
2486 for (idx, b) in s.bytes().enumerate() {
2487 if dont_escape(b) {
2488 continue;
2489 }
2490
2491 if last_match != idx {
2492 st += &s[last_match..idx];
2494 }
2495 if b == b' ' {
2496 st += "+";
2500 } else {
2501 write!(st, "%{b:02X}").unwrap();
2502 }
2503 last_match = idx + 1;
2509 }
2510
2511 if last_match != 0 {
2512 st += &s[last_match..];
2513 st
2514 } else {
2515 s
2516 }
2517}
2518
2519fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String {
2520 use rustc_middle::ty::print::with_forced_trimmed_paths;
2521 let (type_, trait_) = match impl_id {
2522 ItemId::Auto { trait_, for_ } => {
2523 let ty = tcx.type_of(for_).skip_binder();
2524 (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
2525 }
2526 ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
2527 if let Some(trait_ref) = tcx.impl_opt_trait_ref(impl_id) {
2528 let trait_ref = trait_ref.skip_binder();
2529 (trait_ref.self_ty(), Some(trait_ref))
2530 } else {
2531 (tcx.type_of(impl_id).skip_binder(), None)
2532 }
2533 }
2534 };
2535 with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
2536 format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
2537 } else {
2538 format!("impl-{type_}")
2539 }))
2540}
2541
2542fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2543 match item.kind {
2544 clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
2545 Some((
2548 format!("{:#}", print_type(&i.for_, cx)),
2549 get_id_for_impl(cx.tcx(), item.item_id),
2550 ))
2551 }
2552 _ => None,
2553 }
2554}
2555
2556pub(crate) fn get_filtered_impls_for_reference<'a>(
2560 shared: &'a SharedContext<'_>,
2561 it: &clean::Item,
2562) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
2563 let def_id = it.item_id.expect_def_id();
2564 let Some(v) = shared.cache.impls.get(&def_id) else {
2566 return (Vec::new(), Vec::new(), Vec::new());
2567 };
2568 let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
2571 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
2572 traits.partition(|t| t.inner_impl().kind.is_auto());
2573
2574 let (blanket_impl, concrete): (Vec<&Impl>, _) =
2575 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
2576 let concrete: Vec<_> = concrete
2578 .into_iter()
2579 .filter(|t| match t.inner_impl().for_ {
2580 clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
2581 _ => false,
2582 })
2583 .collect();
2584
2585 (concrete, synthetic, blanket_impl)
2586}
2587
2588#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2589pub(crate) enum ItemSection {
2590 Reexports,
2591 PrimitiveTypes,
2592 Modules,
2593 Macros,
2594 Structs,
2595 Enums,
2596 Constants,
2597 Statics,
2598 Traits,
2599 Functions,
2600 TypeAliases,
2601 Unions,
2602 Implementations,
2603 TypeMethods,
2604 Methods,
2605 StructFields,
2606 Variants,
2607 AssociatedTypes,
2608 AssociatedConstants,
2609 ForeignTypes,
2610 Keywords,
2611 Attributes,
2612 AttributeMacros,
2613 DeriveMacros,
2614 TraitAliases,
2615}
2616
2617impl ItemSection {
2618 const ALL: &'static [Self] = {
2619 use ItemSection::*;
2620 &[
2623 Reexports,
2624 PrimitiveTypes,
2625 Modules,
2626 Macros,
2627 Structs,
2628 Enums,
2629 Constants,
2630 Statics,
2631 Traits,
2632 Functions,
2633 TypeAliases,
2634 Unions,
2635 Implementations,
2636 TypeMethods,
2637 Methods,
2638 StructFields,
2639 Variants,
2640 AssociatedTypes,
2641 AssociatedConstants,
2642 ForeignTypes,
2643 Keywords,
2644 Attributes,
2645 AttributeMacros,
2646 DeriveMacros,
2647 TraitAliases,
2648 ]
2649 };
2650
2651 fn id(self) -> &'static str {
2652 match self {
2653 Self::Reexports => "reexports",
2654 Self::Modules => "modules",
2655 Self::Structs => "structs",
2656 Self::Unions => "unions",
2657 Self::Enums => "enums",
2658 Self::Functions => "functions",
2659 Self::TypeAliases => "types",
2660 Self::Statics => "statics",
2661 Self::Constants => "constants",
2662 Self::Traits => "traits",
2663 Self::Implementations => "impls",
2664 Self::TypeMethods => "tymethods",
2665 Self::Methods => "methods",
2666 Self::StructFields => "fields",
2667 Self::Variants => "variants",
2668 Self::Macros => "macros",
2669 Self::PrimitiveTypes => "primitives",
2670 Self::AssociatedTypes => "associated-types",
2671 Self::AssociatedConstants => "associated-consts",
2672 Self::ForeignTypes => "foreign-types",
2673 Self::Keywords => "keywords",
2674 Self::Attributes => "attribute-docs",
2675 Self::AttributeMacros => "attributes",
2676 Self::DeriveMacros => "derives",
2677 Self::TraitAliases => "trait-aliases",
2678 }
2679 }
2680
2681 fn name(self) -> &'static str {
2682 match self {
2683 Self::Reexports => "Re-exports",
2684 Self::Modules => "Modules",
2685 Self::Structs => "Structs",
2686 Self::Unions => "Unions",
2687 Self::Enums => "Enums",
2688 Self::Functions => "Functions",
2689 Self::TypeAliases => "Type Aliases",
2690 Self::Statics => "Statics",
2691 Self::Constants => "Constants",
2692 Self::Traits => "Traits",
2693 Self::Implementations => "Implementations",
2694 Self::TypeMethods => "Type Methods",
2695 Self::Methods => "Methods",
2696 Self::StructFields => "Struct Fields",
2697 Self::Variants => "Variants",
2698 Self::Macros => "Macros",
2699 Self::PrimitiveTypes => "Primitive Types",
2700 Self::AssociatedTypes => "Associated Types",
2701 Self::AssociatedConstants => "Associated Constants",
2702 Self::ForeignTypes => "Foreign Types",
2703 Self::Keywords => "Keywords",
2704 Self::Attributes => "Attributes",
2705 Self::AttributeMacros => "Attribute Macros",
2706 Self::DeriveMacros => "Derive Macros",
2707 Self::TraitAliases => "Trait Aliases",
2708 }
2709 }
2710}
2711
2712fn item_ty_to_section(ty: ItemType) -> ItemSection {
2713 match ty {
2714 ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
2715 ItemType::Module => ItemSection::Modules,
2716 ItemType::Struct => ItemSection::Structs,
2717 ItemType::Union => ItemSection::Unions,
2718 ItemType::Enum => ItemSection::Enums,
2719 ItemType::Function => ItemSection::Functions,
2720 ItemType::TypeAlias => ItemSection::TypeAliases,
2721 ItemType::Static => ItemSection::Statics,
2722 ItemType::Constant => ItemSection::Constants,
2723 ItemType::Trait => ItemSection::Traits,
2724 ItemType::Impl => ItemSection::Implementations,
2725 ItemType::TyMethod => ItemSection::TypeMethods,
2726 ItemType::Method => ItemSection::Methods,
2727 ItemType::StructField => ItemSection::StructFields,
2728 ItemType::Variant => ItemSection::Variants,
2729 ItemType::Macro => ItemSection::Macros,
2730 ItemType::Primitive => ItemSection::PrimitiveTypes,
2731 ItemType::AssocType => ItemSection::AssociatedTypes,
2732 ItemType::AssocConst => ItemSection::AssociatedConstants,
2733 ItemType::ForeignType => ItemSection::ForeignTypes,
2734 ItemType::Keyword => ItemSection::Keywords,
2735 ItemType::Attribute => ItemSection::Attributes,
2736 ItemType::ProcAttribute | ItemType::DeclMacroAttribute => ItemSection::AttributeMacros,
2737 ItemType::ProcDerive | ItemType::DeclMacroDerive => ItemSection::DeriveMacros,
2738 ItemType::TraitAlias => ItemSection::TraitAliases,
2739 }
2740}
2741
2742fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec<String> {
2749 let mut out = Vec::new();
2750 let mut visited = FxHashSet::default();
2751 let mut work = VecDeque::new();
2752
2753 let mut process_path = |did: DefId| {
2754 let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
2755 let fqp = cache.exact_paths.get(&did).or_else(get_extern);
2756
2757 if let Some(path) = fqp {
2758 out.push(join_path_syms(path));
2759 }
2760 };
2761
2762 work.push_back(first_ty);
2763
2764 while let Some(ty) = work.pop_front() {
2765 if !visited.insert(ty) {
2766 continue;
2767 }
2768
2769 match ty {
2770 clean::Type::Path { path } => process_path(path.def_id()),
2771 clean::Type::Tuple(tys) => {
2772 work.extend(tys.iter());
2773 }
2774 clean::Type::Slice(ty) => {
2775 work.push_back(ty);
2776 }
2777 clean::Type::Array(ty, _) => {
2778 work.push_back(ty);
2779 }
2780 clean::Type::RawPointer(_, ty) => {
2781 work.push_back(ty);
2782 }
2783 clean::Type::BorrowedRef { type_, .. } => {
2784 work.push_back(type_);
2785 }
2786 clean::Type::QPath(clean::QPathData { self_type, trait_, .. }) => {
2787 work.push_back(self_type);
2788 if let Some(trait_) = trait_ {
2789 process_path(trait_.def_id());
2790 }
2791 }
2792 _ => {}
2793 }
2794 }
2795 out
2796}
2797
2798const MAX_FULL_EXAMPLES: usize = 5;
2799const NUM_VISIBLE_LINES: usize = 10;
2800
2801fn render_call_locations<W: fmt::Write>(
2803 mut w: W,
2804 cx: &Context<'_>,
2805 item: &clean::Item,
2806) -> fmt::Result {
2807 let tcx = cx.tcx();
2808 let def_id = item.item_id.expect_def_id();
2809 let key = tcx.def_path_hash(def_id);
2810 let Some(call_locations) = cx.shared.call_locations.get(&key) else { return Ok(()) };
2811
2812 let id = cx.derive_id("scraped-examples");
2814 write!(
2815 &mut w,
2816 "<div class=\"docblock scraped-example-list\">\
2817 <span></span>\
2818 <h5 id=\"{id}\">\
2819 <a href=\"#{id}\">Examples found in repository</a>\
2820 <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
2821 </h5>",
2822 root_path = cx.root_path(),
2823 id = id
2824 )?;
2825
2826 let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
2828 let (line_lo, line_hi) = loc.call_expr.line_span;
2829 let (anchor, title) = if line_lo == line_hi {
2830 ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
2831 } else {
2832 (
2833 format!("{}-{}", line_lo + 1, line_hi + 1),
2834 format!("lines {}-{}", line_lo + 1, line_hi + 1),
2835 )
2836 };
2837 let url = format!("{}{}#{anchor}", cx.root_path(), call_data.url);
2838 (url, title)
2839 };
2840
2841 let write_example = |w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
2843 let contents = match fs::read_to_string(path) {
2844 Ok(contents) => contents,
2845 Err(err) => {
2846 let span = item.span(tcx).map_or(DUMMY_SP, |span| span.inner());
2847 tcx.dcx().span_err(span, format!("failed to read file {}: {err}", path.display()));
2848 return false;
2849 }
2850 };
2851
2852 assert!(!call_data.locations.is_empty());
2855 let min_loc =
2856 call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2857 let byte_min = min_loc.enclosing_item.byte_span.0;
2858 let line_min = min_loc.enclosing_item.line_span.0;
2859 let max_loc =
2860 call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
2861 let byte_max = max_loc.enclosing_item.byte_span.1;
2862 let line_max = max_loc.enclosing_item.line_span.1;
2863
2864 let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
2866
2867 let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
2870 .locations
2871 .iter()
2872 .map(|loc| {
2873 let (byte_lo, byte_hi) = loc.call_ident.byte_span;
2874 let (line_lo, line_hi) = loc.call_expr.line_span;
2875 let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
2876
2877 let line_range = (line_lo - line_min, line_hi - line_min);
2878 let (line_url, line_title) = link_to_loc(call_data, loc);
2879
2880 (byte_range, (line_range, line_url, line_title))
2881 })
2882 .unzip();
2883
2884 let (_, init_url, init_title) = &line_ranges[0];
2885 let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
2886 let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
2887
2888 let file_span = rustc_span::DUMMY_SP;
2894
2895 let mut decoration_info = FxIndexMap::default();
2896 decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
2897 decoration_info.insert("highlight", byte_ranges);
2898
2899 sources::print_src(
2900 w,
2901 contents_subset,
2902 file_span,
2903 cx,
2904 &cx.root_path(),
2905 &highlight::DecorationInfo(decoration_info),
2906 &sources::SourceContext::Embedded(sources::ScrapedInfo {
2907 needs_expansion,
2908 offset: line_min,
2909 name: &call_data.display_name,
2910 url: init_url,
2911 title: init_title,
2912 locations: locations_encoded,
2913 }),
2914 )
2915 .unwrap();
2916
2917 true
2918 };
2919
2920 let ordered_locations = {
2932 fn sort_criterion<'a>(
2933 (_, call_data): &(&PathBuf, &'a CallData),
2934 ) -> (bool, u32, &'a String) {
2935 let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2937 (!call_data.is_bin, hi - lo, &call_data.display_name)
2938 }
2939
2940 let mut locs = call_locations.iter().collect::<Vec<_>>();
2941 locs.sort_by_key(sort_criterion);
2942 locs
2943 };
2944
2945 let mut it = ordered_locations.into_iter().peekable();
2946
2947 let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
2950 for example in it.by_ref() {
2951 if write_example(&mut *w, example) {
2952 break;
2953 }
2954 }
2955 };
2956
2957 write_and_skip_failure(&mut w, &mut it);
2959
2960 if it.peek().is_some() {
2962 write!(
2963 w,
2964 "<details class=\"toggle more-examples-toggle\">\
2965 <summary class=\"hideme\">\
2966 <span>More examples</span>\
2967 </summary>\
2968 <div class=\"hide-more\">Hide additional examples</div>\
2969 <div class=\"more-scraped-examples\">\
2970 <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
2971 )?;
2972
2973 for _ in 0..MAX_FULL_EXAMPLES {
2976 write_and_skip_failure(&mut w, &mut it);
2977 }
2978
2979 if it.peek().is_some() {
2981 w.write_str(
2982 r#"<div class="example-links">Additional examples can be found in:<br><ul>"#,
2983 )?;
2984 it.try_for_each(|(_, call_data)| {
2985 let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
2986 write!(
2987 w,
2988 r#"<li><a href="{url}">{name}</a></li>"#,
2989 url = url,
2990 name = call_data.display_name
2991 )
2992 })?;
2993 w.write_str("</ul></div>")?;
2994 }
2995
2996 w.write_str("</div></details>")?;
2997 }
2998
2999 w.write_str("</div>")
3000}
3001
3002fn render_attributes_in_code(
3003 w: &mut impl fmt::Write,
3004 item: &clean::Item,
3005 prefix: impl fmt::Display,
3006 cx: &Context<'_>,
3007) -> fmt::Result {
3008 render_attributes_in_code_with_options(w, item, prefix, cx, true, "")
3009}
3010
3011pub(super) fn render_attributes_in_code_with_options(
3012 w: &mut impl fmt::Write,
3013 item: &clean::Item,
3014 prefix: impl fmt::Display,
3015 cx: &Context<'_>,
3016 render_doc_hidden: bool,
3017 open_tag: &str,
3018) -> fmt::Result {
3019 w.write_str(open_tag)?;
3020 if render_doc_hidden && item.is_doc_hidden() {
3021 render_code_attribute(&prefix, "#[doc(hidden)]", w)?;
3022 }
3023 for attr in &item.attrs.other_attrs {
3024 let hir::Attribute::Parsed(kind) = attr else { continue };
3025 let attr = match kind {
3026 AttributeKind::LinkSection { name, .. } => {
3027 Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}"))))
3028 }
3029 AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"),
3030 AttributeKind::ExportName { name, .. } => {
3031 Cow::Owned(format!("#[unsafe(export_name = {})]", Escape(&format!("{name:?}"))))
3032 }
3033 AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"),
3034 _ => continue,
3035 };
3036 render_code_attribute(&prefix, attr.as_ref(), w)?;
3037 }
3038
3039 if let Some(def_id) = item.def_id()
3040 && let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id)
3041 {
3042 render_code_attribute(prefix, &repr, w)?;
3043 }
3044 Ok(())
3045}
3046
3047fn render_repr_attribute_in_code(
3048 w: &mut impl fmt::Write,
3049 cx: &Context<'_>,
3050 def_id: DefId,
3051) -> fmt::Result {
3052 if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) {
3053 render_code_attribute("", &repr, w)?;
3054 }
3055 Ok(())
3056}
3057
3058fn render_code_attribute(
3059 prefix: impl fmt::Display,
3060 attr: impl fmt::Display,
3061 w: &mut impl fmt::Write,
3062) -> fmt::Result {
3063 write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>")
3064}
3065
3066fn repr_attribute<'tcx>(
3071 tcx: TyCtxt<'tcx>,
3072 cache: &Cache,
3073 def_id: DefId,
3074) -> Option<Cow<'static, str>> {
3075 let adt = match tcx.def_kind(def_id) {
3076 DefKind::Struct | DefKind::Enum | DefKind::Union => tcx.adt_def(def_id),
3077 _ => return None,
3078 };
3079 let repr = adt.repr();
3080
3081 let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id);
3082 let is_public_field = |field: &ty::FieldDef| {
3083 (cache.document_private || field.vis.is_public()) && is_visible(field.did)
3084 };
3085
3086 if repr.transparent() {
3087 let is_public = 'is_public: {
3090 let var = adt.variant(rustc_abi::FIRST_VARIANT); if !is_visible(var.def_id) {
3094 break 'is_public false;
3095 }
3096
3097 let non_1zst_field = var.fields.iter().find(|field| {
3099 let ty = ty::TypingEnv::post_analysis(tcx, field.did)
3100 .as_query_input(tcx.type_of(field.did).instantiate_identity().skip_norm_wip());
3101 tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst())
3102 });
3103
3104 match non_1zst_field {
3105 Some(field) => is_public_field(field),
3106 None => var.fields.is_empty() || var.fields.iter().any(is_public_field),
3107 }
3108 };
3109
3110 return is_public.then(|| "#[repr(transparent)]".into());
3113 }
3114
3115 let is_public = adt
3117 .variants()
3118 .iter()
3119 .all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field));
3120 if !is_public {
3121 return None;
3122 }
3123
3124 let mut result = Vec::<Cow<'_, _>>::new();
3125
3126 if repr.c() {
3127 result.push("C".into());
3128 }
3129 if repr.simd() {
3130 result.push("simd".into());
3131 }
3132 if let Some(int) = repr.int {
3133 let prefix = if int.is_signed() { 'i' } else { 'u' };
3134 let int = match int {
3135 rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"),
3136 rustc_abi::IntegerType::Fixed(int, _) => {
3137 format!("{prefix}{}", int.size().bytes() * 8)
3138 }
3139 };
3140 result.push(int.into());
3141 }
3142
3143 if let Some(pack) = repr.pack {
3145 result.push(format!("packed({})", pack.bytes()).into());
3146 }
3147 if let Some(align) = repr.align {
3148 result.push(format!("align({})", align.bytes()).into());
3149 }
3150
3151 (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into())
3152}