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