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, NotableTraitColor, RustcVersion,
56 StabilityLevel, 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, ItemKind, 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 if let ItemKind::Function(..)
832 | ItemKind::AssocFunction(clean::AssocFunction { body: Some(_), .. }) =
833 item.kind.unstripped()
834 {
835 render_call_locations(f, cx, item)?;
836 }
837 Ok(())
838 })
839}
840
841#[derive(Template)]
842#[template(path = "item_info.html")]
843struct ItemInfo {
844 items: Vec<ShortItemInfo>,
845}
846fn document_item_info(
852 cx: &Context<'_>,
853 item: &clean::Item,
854 parent: Option<&clean::Item>,
855) -> ItemInfo {
856 let items = short_item_info(item, cx, parent);
857 ItemInfo { items }
858}
859
860fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> {
861 let cfg = match (&item.cfg, parent.and_then(|p| p.cfg.as_ref())) {
862 (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
863 (cfg, _) => cfg.as_deref().cloned(),
864 };
865
866 debug!(
867 "Portability {name:?} {item_cfg:?} (parent: {parent:?}) - {parent_cfg:?} = {cfg:?}",
868 name = item.name,
869 item_cfg = item.cfg,
870 parent_cfg = parent.and_then(|p| p.cfg.as_ref()),
871 );
872
873 Some(cfg?.render_long_html())
874}
875
876#[derive(Template)]
877#[template(path = "short_item_info.html")]
878enum ShortItemInfo {
879 Deprecation {
881 message: String,
882 },
883 Unstable {
886 feature: String,
887 tracking: Option<(String, u32)>,
888 },
889 Portability {
890 message: String,
891 },
892}
893
894fn short_item_info(
897 item: &clean::Item,
898 cx: &Context<'_>,
899 parent: Option<&clean::Item>,
900) -> Vec<ShortItemInfo> {
901 let mut extra_info = vec![];
902
903 if let Some(depr @ Deprecation { note, since, suggestion: _ }) = item.deprecation(cx.tcx()) {
904 let mut message = match since {
907 DeprecatedSince::RustcVersion(version) => {
908 if depr.is_in_effect() {
909 format!("Deprecated since {version}")
910 } else {
911 format!("Deprecating in {version}")
912 }
913 }
914 DeprecatedSince::Future => String::from("Deprecating in a future version"),
915 DeprecatedSince::NonStandard(since) => {
916 format!("Deprecated since {}", Escape(since.as_str()))
917 }
918 DeprecatedSince::Unspecified | DeprecatedSince::Err => String::from("Deprecated"),
919 };
920
921 if let Some(note) = note {
922 let note = note.as_str();
923 let mut id_map = cx.id_map.borrow_mut();
924 let links = item.links(cx);
925 let html = MarkdownItemInfo::new(note, &links, &mut id_map);
926 message.push_str(": ");
927 html.write_into(&mut message).unwrap();
928 }
929 extra_info.push(ShortItemInfo::Deprecation { message });
930 }
931
932 if let Some((StabilityLevel::Unstable { reason: _, issue, .. }, feature)) = item
935 .stability(cx.tcx())
936 .as_ref()
937 .filter(|stab| stab.feature != sym::rustc_private)
938 .map(|stab| (stab.level, stab.feature))
939 {
940 let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue)
941 {
942 Some((url.clone(), issue.get()))
943 } else {
944 None
945 };
946 extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking });
947 }
948
949 if let Some(message) = portability(item, parent) {
950 extra_info.push(ShortItemInfo::Portability { message });
951 }
952
953 extra_info
954}
955
956fn impl_trait_key(cx: &Context<'_>, i: &Impl) -> Option<String> {
958 let trait_ = i.inner_impl().trait_.as_ref()?;
959 let prefix = match i.inner_impl().polarity {
960 ty::ImplPolarity::Positive => "",
961 ty::ImplPolarity::Negative => "!",
962 };
963 Some(format!("{prefix}{:#}", print_path(trait_, cx)))
964}
965
966fn render_impls<'a, 'cx>(
969 cx: &'a Context<'cx>,
970 mut impls: Vec<&'a Impl>,
971 containing_item: &'a clean::Item,
972 toggle_open_by_default: bool,
973) -> impl fmt::Display + use<'a, 'cx> {
974 impls.sort_by_cached_key(|imp| {
975 let prefix = match imp.inner_impl().polarity {
976 ty::ImplPolarity::Positive => Ordering::Greater,
977 ty::ImplPolarity::Negative => Ordering::Less,
978 };
979 (prefix, ImplString::new_path(imp, cx))
980 });
981 fmt::from_fn(move |f| {
985 impls
986 .iter()
987 .map(|i| {
988 fmt::from_fn(|f| {
989 let did = i.trait_did().unwrap();
990 let provided_trait_methods = i.inner_impl().provided_trait_methods(cx.tcx());
991 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods);
992 render_impl(
993 cx,
994 i,
995 containing_item,
996 assoc_link,
997 RenderMode::Normal,
998 None,
999 &[],
1000 ImplRenderingParameters {
1001 show_def_docs: true,
1002 show_default_items: true,
1003 show_non_assoc_items: true,
1004 toggle_open_by_default,
1005 },
1006 )
1007 .fmt(f)
1008 })
1009 })
1010 .joined("", f)
1011 })
1012}
1013
1014fn assoc_href_attr(
1016 it: &clean::Item,
1017 link: AssocItemLink<'_>,
1018 cx: &Context<'_>,
1019) -> Option<impl fmt::Display> {
1020 let name = it.name.unwrap();
1021 let item_type = it.type_();
1022
1023 enum Href<'a> {
1024 AnchorId(&'a str),
1025 Anchor(ItemType),
1026 Url(String, ItemType),
1027 }
1028
1029 let href = match link {
1030 AssocItemLink::Anchor(Some(id)) => Href::AnchorId(id),
1031 AssocItemLink::Anchor(None) => Href::Anchor(item_type),
1032 AssocItemLink::GotoSource(did, provided_methods) => {
1033 let item_type = match item_type {
1036 ItemType::AssocFnWithBody | ItemType::AssocFnWithoutBody => {
1037 if provided_methods.contains(&name) {
1038 ItemType::AssocFnWithBody
1039 } else {
1040 ItemType::AssocFnWithoutBody
1041 }
1042 }
1043 item_type => item_type,
1044 };
1045
1046 match href(did.expect_def_id(), cx) {
1047 Ok(HrefInfo { url, .. }) => Href::Url(url, item_type),
1048 Err(HrefError::DocumentationNotBuilt) => return None,
1060 Err(_) => Href::Anchor(item_type),
1061 }
1062 }
1063 };
1064
1065 let href = fmt::from_fn(move |f| match &href {
1066 Href::AnchorId(id) => write!(f, "#{id}"),
1067 Href::Url(url, item_type) => {
1068 write!(f, "{url}#{item_type}.{name}")
1069 }
1070 Href::Anchor(item_type) => {
1071 write!(f, "#{item_type}.{name}")
1072 }
1073 });
1074
1075 Some(fmt::from_fn(move |f| write!(f, " href=\"{href}\"")))
1078}
1079
1080fn assoc_const(
1081 it: &clean::Item,
1082 ct: &clean::AssocConstant,
1083 parent: ItemType,
1084 link: AssocItemLink<'_>,
1085 indent: usize,
1086 ending: Ending,
1087 cx: &Context<'_>,
1088) -> impl fmt::Display {
1089 let tcx = cx.tcx();
1090 fmt::from_fn(move |w| {
1091 render_attributes_in_code(w, it, &" ".repeat(indent), cx)?;
1092 write!(
1093 w,
1094 "{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
1095 indent = " ".repeat(indent),
1096 vis = visibility_print_with_space(it, cx),
1097 href = assoc_href_attr(it, link, cx).maybe_display(),
1098 name = it.name.as_ref().unwrap(),
1099 generics = print_generics(&ct.generics, cx),
1100 ty = print_type(&ct.type_, cx),
1101 )?;
1102 if let Some(body) = &ct.body {
1103 let body = body.rendered(tcx);
1104 if parent == ItemType::Trait || body != "_" {
1111 write!(w, " = {}", Escape(&body))?;
1112 }
1113 }
1114 let where_clause = print_where_clause(&ct.generics, cx, indent, ending).maybe_display();
1115 write!(w, "{where_clause}")
1116 })
1117}
1118
1119fn assoc_ty(
1120 it: &clean::Item,
1121 ty: &clean::AssocType,
1122 link: AssocItemLink<'_>,
1123 indent: usize,
1124 ending: Ending,
1125 cx: &Context<'_>,
1126) -> impl fmt::Display {
1127 fmt::from_fn(move |w| {
1128 render_attributes_in_code(w, it, &" ".repeat(indent), cx)?;
1129 write!(
1130 w,
1131 "{indent}{vis}type <a{href} class=\"associatedtype\">{name}</a>{generics}",
1132 indent = " ".repeat(indent),
1133 vis = visibility_print_with_space(it, cx),
1134 href = assoc_href_attr(it, link, cx).maybe_display(),
1135 name = it.name.as_ref().unwrap(),
1136 generics = print_generics(&ty.generics, cx),
1137 )?;
1138 if !ty.bounds.is_empty() {
1139 write!(w, ": {}", print_generic_bounds(&ty.bounds, cx))?;
1140 }
1141 if let Some(type_) = &ty.type_ {
1143 let type_ = type_.middle_type.as_ref().unwrap_or(&type_.type_);
1144 write!(w, " = {}", print_type(type_, cx))?;
1145 }
1146 write!(w, "{}", print_where_clause(&ty.generics, cx, indent, ending).maybe_display())
1147 })
1148}
1149
1150fn assoc_fn(
1151 it: &clean::Item,
1152 fn_: &clean::AssocFunction,
1153 link: AssocItemLink<'_>,
1154 indent: usize,
1155 ending: Ending,
1156 render_mode: RenderMode,
1157 cx: &Context<'_>,
1158) -> impl fmt::Display {
1159 let tcx = cx.tcx();
1160 let header = it.fn_header(tcx).expect("Trying to get header from a non-function item");
1161 let name = it.name.as_ref().unwrap();
1162 let vis = visibility_print_with_space(it, cx).to_string();
1163 let defaultness = match fn_.body {
1164 Some(Defaultness::Implicit) | None => "",
1165 Some(Defaultness::Final) => "final ",
1166 Some(Defaultness::Default) => "default ",
1167 };
1168 let constness = match render_mode {
1171 RenderMode::Normal => print_constness_with_space(
1172 &header.constness,
1173 it.stable_since(tcx),
1174 it.const_stability(tcx),
1175 ),
1176 RenderMode::ForDeref { .. } => "",
1177 };
1178
1179 fmt::from_fn(move |w| {
1180 let indent_str = " ".repeat(indent);
1181 render_attributes_in_code(w, it, &indent_str, cx)?;
1182
1183 let asyncness = header.asyncness.print_with_space();
1184 let safety = header.safety.print_with_space();
1185 let abi = print_abi_with_space(header.abi).to_string();
1186 let href = assoc_href_attr(it, link, cx).maybe_display();
1187
1188 let generics_len = format!("{:#}", print_generics(&fn_.generics, cx)).len();
1190 let header_len = indent
1191 + "fn ".len()
1192 + vis.len()
1193 + defaultness.len()
1194 + constness.len()
1195 + asyncness.len()
1196 + safety.len()
1197 + abi.len()
1198 + name.as_str().len()
1199 + generics_len;
1200
1201 let notable_traits = notable_traits_button(&fn_.decl.output, cx).maybe_display();
1202
1203 write!(
1204 w,
1205 "{indent}{vis}{defaultness}{constness}{asyncness}{safety}{abi}fn \
1206 <a{href} class=\"fn\">{name}</a>{generics}{decl}{notable_traits}{where_clause}",
1207 indent = indent_str,
1208 generics = print_generics(&fn_.generics, cx),
1209 decl = full_print_fn_decl(&fn_.decl, header_len, indent, cx),
1210 where_clause = print_where_clause(&fn_.generics, cx, indent, ending).maybe_display(),
1211 )
1212 })
1213}
1214
1215fn render_stability_since_raw_with_extra(
1230 stable_version: Option<StableSince>,
1231 const_stability: Option<ConstStability>,
1232 extra_class: &str,
1233) -> Option<impl fmt::Display> {
1234 let mut title = String::new();
1235 let mut stability = String::new();
1236
1237 if let Some(version) = stable_version.and_then(|version| since_to_string(&version)) {
1238 stability.push_str(&version);
1239 title.push_str(&format!("Stable since Rust version {version}"));
1240 }
1241
1242 let const_title_and_stability = match const_stability {
1243 Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) => {
1244 since_to_string(&since)
1245 .map(|since| (format!("const since {since}"), format!("const: {since}")))
1246 }
1247 Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => {
1248 if stable_version.is_none() {
1249 None
1251 } else {
1252 let unstable = if let Some(n) = issue {
1253 format!(
1254 "<a \
1255 href=\"https://github.com/rust-lang/rust/issues/{n}\" \
1256 title=\"Tracking issue for {feature}\"\
1257 >unstable</a>"
1258 )
1259 } else {
1260 String::from("unstable")
1261 };
1262
1263 Some((String::from("const unstable"), format!("const: {unstable}")))
1264 }
1265 }
1266 _ => None,
1267 };
1268
1269 if let Some((const_title, const_stability)) = const_title_and_stability {
1270 if !title.is_empty() {
1271 title.push_str(&format!(", {const_title}"));
1272 } else {
1273 title.push_str(&const_title);
1274 }
1275
1276 if !stability.is_empty() {
1277 stability.push_str(&format!(" ({const_stability})"));
1278 } else {
1279 stability.push_str(&const_stability);
1280 }
1281 }
1282
1283 (!stability.is_empty()).then_some(fmt::from_fn(move |w| {
1284 write!(w, r#"<span class="since{extra_class}" title="{title}">{stability}</span>"#)
1285 }))
1286}
1287
1288fn since_to_string(since: &StableSince) -> Option<String> {
1289 match since {
1290 StableSince::Version(since) => Some(since.to_string()),
1291 StableSince::Current => Some(RustcVersion::CURRENT.to_string()),
1292 StableSince::Err(_) => None,
1293 }
1294}
1295
1296#[inline]
1297fn render_stability_since_raw(
1298 ver: Option<StableSince>,
1299 const_stability: Option<ConstStability>,
1300) -> Option<impl fmt::Display> {
1301 render_stability_since_raw_with_extra(ver, const_stability, "")
1302}
1303
1304fn render_assoc_item(
1305 item: &clean::Item,
1306 link: AssocItemLink<'_>,
1307 parent: ItemType,
1308 indent: usize,
1309 ending: Ending,
1310 render_mode: RenderMode,
1311 cx: &Context<'_>,
1312) -> impl fmt::Display {
1313 fmt::from_fn(move |f| match &item.kind {
1314 ItemKind::Stripped(..) => Ok(()),
1315 ItemKind::AssocFunction(fn_) => {
1316 assoc_fn(item, fn_, link, indent, ending, render_mode, cx).fmt(f)
1317 }
1318 ItemKind::AssocConstant(ct) => {
1319 assoc_const(item, ct, parent, link, indent, ending, cx).fmt(f)
1320 }
1321 ItemKind::AssocType(ty) => assoc_ty(item, ty, link, indent, ending, cx).fmt(f),
1322 _ => panic!("render_assoc_item called on non-associated-item"),
1323 })
1324}
1325
1326#[derive(Copy, Clone)]
1327enum AssocItemLink<'a> {
1328 Anchor(Option<&'a str>),
1329 GotoSource(ItemId, &'a FxIndexSet<Symbol>),
1330}
1331
1332impl<'a> AssocItemLink<'a> {
1333 fn anchor(&self, id: &'a str) -> Self {
1334 match *self {
1335 AssocItemLink::Anchor(_) => AssocItemLink::Anchor(Some(id)),
1336 ref other => *other,
1337 }
1338 }
1339}
1340
1341fn write_section_heading(
1342 title: impl fmt::Display,
1343 id: &str,
1344 extra_class: Option<&str>,
1345 extra: impl fmt::Display,
1346) -> impl fmt::Display {
1347 fmt::from_fn(move |w| {
1348 let (extra_class, whitespace) = match extra_class {
1349 Some(extra) => (extra, " "),
1350 None => ("", ""),
1351 };
1352 write!(
1353 w,
1354 "<h2 id=\"{id}\" class=\"{extra_class}{whitespace}section-header\">\
1355 {title}\
1356 <a href=\"#{id}\" class=\"anchor\">§</a>\
1357 </h2>{extra}",
1358 )
1359 })
1360}
1361
1362fn write_impl_section_heading(title: impl fmt::Display, id: &str) -> impl fmt::Display {
1363 write_section_heading(title, id, None, "")
1364}
1365
1366fn render_all_impls(
1367 mut w: impl Write,
1368 cx: &Context<'_>,
1369 containing_item: &clean::Item,
1370 concrete_impls: Vec<&Impl>,
1371 auto_trait_impls: Vec<&Impl>,
1372 blanket_impls: Vec<&Impl>,
1373) -> fmt::Result {
1374 if !concrete_impls.is_empty() {
1375 let impls = render_impls(cx, concrete_impls, containing_item, true);
1376 write!(
1377 w,
1378 "{}<div id=\"trait-implementations-list\">{impls}</div>",
1379 write_impl_section_heading("Trait Implementations", "trait-implementations")
1380 )?;
1381 }
1382
1383 if !auto_trait_impls.is_empty() {
1384 let impls = render_impls(cx, auto_trait_impls, containing_item, false);
1385 write!(
1387 w,
1388 "{}<div id=\"synthetic-implementations-list\">{impls}</div>",
1389 write_impl_section_heading("Auto Trait Implementations", "synthetic-implementations",)
1390 )?;
1391 }
1392
1393 if !blanket_impls.is_empty() {
1394 let impls = render_impls(cx, blanket_impls, containing_item, false);
1395 write!(
1396 w,
1397 "{}<div id=\"blanket-implementations-list\">{impls}</div>",
1398 write_impl_section_heading("Blanket Implementations", "blanket-implementations")
1399 )?;
1400 }
1401
1402 Ok(())
1403}
1404
1405fn render_assoc_items(
1406 cx: &Context<'_>,
1407 containing_item: &clean::Item,
1408 it: DefId,
1409 what: AssocItemRender<'_>,
1410) -> impl fmt::Display {
1411 fmt::from_fn(move |f| {
1412 let mut derefs = DefIdSet::default();
1413 derefs.insert(it);
1414 render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs)
1415 })
1416}
1417
1418fn render_assoc_items_inner(
1419 mut w: &mut dyn fmt::Write,
1420 cx: &Context<'_>,
1421 containing_item: &clean::Item,
1422 it: DefId,
1423 what: AssocItemRender<'_>,
1424 derefs: &mut DefIdSet,
1425) -> fmt::Result {
1426 info!("Documenting associated items of {:?}", containing_item.name);
1427 let cache = &cx.shared.cache;
1428 let Some(impls) = cache.impls.get(&it) else { return Ok(()) };
1429 let (mut inherent_impls, trait_impls): (Vec<_>, _) =
1430 impls.iter().partition(|i| i.inner_impl().trait_.is_none());
1431 if !inherent_impls.is_empty() {
1432 let render_mode = what.render_mode(cx.tcx());
1433 let class_html = what
1434 .class()
1435 .map(|class| fmt::from_fn(move |f| write!(f, r#" class="{class}""#)))
1436 .maybe_display();
1437 let (section_heading, id) = match what {
1438 AssocItemRender::All => (
1439 Either::Left(write_impl_section_heading("Implementations", "implementations")),
1440 Cow::Borrowed("implementations-list"),
1441 ),
1442 AssocItemRender::DerefFor { trait_, type_, .. } => {
1443 let id = cx.derive_id(small_url_encode(format!(
1444 "deref-methods-{:#}",
1445 print_type(type_, cx)
1446 )));
1447 inherent_impls.retain(|impl_| {
1455 type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache)
1456 });
1457 let derived_id = cx.derive_id(&id);
1458 if let Some(def_id) = type_.def_id(cx.cache()) {
1459 cx.deref_id_map.borrow_mut().insert(def_id, id.clone());
1460 }
1461 (
1462 Either::Right(fmt::from_fn(move |f| {
1463 write!(
1464 f,
1465 "<details class=\"toggle big-toggle\" open><summary>{}</summary>",
1466 write_impl_section_heading(
1467 fmt::from_fn(|f| write!(
1468 f,
1469 "<span>Methods from {trait_}<Target = {type_}></span>",
1470 trait_ = print_path(trait_, cx),
1471 type_ = print_type(type_, cx),
1472 )),
1473 &id,
1474 )
1475 )
1476 })),
1477 Cow::Owned(derived_id),
1478 )
1479 }
1480 };
1481 let inherent_impls_buf = fmt::from_fn(|f| {
1482 inherent_impls
1483 .iter()
1484 .map(|i| {
1485 render_impl(
1486 cx,
1487 i,
1488 containing_item,
1489 AssocItemLink::Anchor(None),
1490 render_mode,
1491 None,
1492 &[],
1493 ImplRenderingParameters {
1494 show_def_docs: true,
1495 show_default_items: true,
1496 show_non_assoc_items: true,
1497 toggle_open_by_default: true,
1498 },
1499 )
1500 })
1501 .joined("", f)
1502 })
1503 .to_string();
1504
1505 if !inherent_impls_buf.is_empty() {
1506 write!(
1507 w,
1508 "{section_heading}<div id=\"{id}\"{class_html}>{inherent_impls_buf}</div>{}",
1509 matches!(what, AssocItemRender::DerefFor { .. })
1510 .then_some("</details>")
1511 .maybe_display(),
1512 )?;
1513 }
1514 }
1515
1516 if !trait_impls.is_empty() {
1517 let deref_impl = trait_impls.iter().find(|t| {
1518 t.trait_did() == cx.tcx().lang_items().deref_trait() && !t.is_negative_trait_impl()
1519 });
1520 if let Some(impl_) = deref_impl {
1521 let has_deref_mut = trait_impls
1522 .iter()
1523 .any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
1524 render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs)?;
1525 }
1526
1527 if let AssocItemRender::DerefFor { .. } = what {
1530 return Ok(());
1531 }
1532
1533 let (auto_trait_impls, trait_impls): (Vec<&Impl>, Vec<&Impl>) =
1534 trait_impls.into_iter().partition(|t| t.inner_impl().kind.is_auto());
1535 let (blanket_impls, concrete_impls): (Vec<&Impl>, _) =
1536 trait_impls.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
1537
1538 render_all_impls(w, cx, containing_item, concrete_impls, auto_trait_impls, blanket_impls)?;
1539 }
1540 Ok(())
1541}
1542
1543fn render_deref_methods(
1545 mut w: impl Write,
1546 cx: &Context<'_>,
1547 impl_: &Impl,
1548 container_item: &clean::Item,
1549 deref_mut: bool,
1550 derefs: &mut DefIdSet,
1551) -> fmt::Result {
1552 let cache = cx.cache();
1553 let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
1554 let (target, real_target) = impl_
1555 .inner_impl()
1556 .items
1557 .iter()
1558 .find_map(|item| match &item.kind {
1559 ItemKind::AssocType(clean::AssocType { type_: Some(type_), .. }) => {
1560 Some((type_.middle_type.as_ref().unwrap_or(&type_.type_), &type_.type_))
1561 }
1562 _ => None,
1563 })
1564 .expect("Expected associated type binding");
1565 debug!(
1566 "Render deref methods for {for_:#?}, target {target:#?}",
1567 for_ = impl_.inner_impl().for_
1568 );
1569 let what = AssocItemRender::DerefFor {
1570 trait_: deref_type,
1571 type_: real_target,
1572 deref_mut_: deref_mut,
1573 deref_impl_def_id: impl_.def_id(),
1574 };
1575 if let Some(did) = target.def_id(cache) {
1576 if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) {
1577 if did == type_did || !derefs.insert(did) {
1579 return Ok(());
1581 }
1582 }
1583 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1584 } else if let Some(prim) = target.primitive_type()
1585 && let Some(&did) = cache.primitive_locations.get(&prim)
1586 {
1587 render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
1588 }
1589 Ok(())
1590}
1591
1592fn should_render_item_for_deref(
1593 item: &clean::Item,
1594 deref_mut_: bool,
1595 tcx: TyCtxt<'_>,
1596 target_is_copy: bool,
1597) -> bool {
1598 let ItemKind::AssocFunction(fn_) = &item.kind else { return false };
1599 let Some(self_ty) = fn_.decl.receiver_type() else { return false };
1600
1601 let (by_mut_ref, by_box, by_value) = match *self_ty {
1602 clean::Type::BorrowedRef { mutability, .. } => {
1603 (mutability == Mutability::Mut, false, false)
1604 }
1605 clean::Type::Path { ref path } => {
1606 (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false)
1607 }
1608 clean::Type::SelfTy => (false, false, true),
1609 _ => (false, false, false),
1610 };
1611
1612 (deref_mut_ || !by_mut_ref) && !by_box && (!by_value || target_is_copy)
1613}
1614
1615fn is_notable_trait_passthrough(did: DefId, cx: &Context<'_>) -> bool {
1620 let lang_items = cx.tcx().lang_items();
1621 Some(did) == lang_items.owned_box() || Some(did) == lang_items.pin_type()
1622}
1623
1624fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option<impl fmt::Display> {
1625 if ty.is_unit() {
1626 return None;
1628 }
1629
1630 let did = ty.def_id(cx.cache())?;
1631
1632 if is_notable_trait_passthrough(did, cx) {
1633 return None;
1634 }
1635
1636 let impls = cx.cache().impls.get(&did)?;
1637 let has_notable_trait = impls
1638 .iter()
1639 .map(Impl::inner_impl)
1640 .filter(|impl_| {
1641 impl_.polarity == ty::ImplPolarity::Positive
1642 && ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1645 })
1646 .filter_map(|impl_| impl_.trait_.as_ref())
1647 .filter_map(|trait_| cx.cache().traits.get(&trait_.def_id()))
1648 .any(|t| t.is_notable_trait(cx.tcx()));
1649
1650 has_notable_trait.then(|| {
1651 cx.types_with_notable_traits.borrow_mut().insert(ty.clone());
1652 fmt::from_fn(|f| {
1653 write!(
1654 f,
1655 " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>",
1656 ty = Escape(&format!("{:#}", print_type(ty, cx))),
1657 )
1658 })
1659 })
1660}
1661
1662fn notable_traits_decl_write_html<'cx>(
1663 notable_impls: impl Iterator<Item = (&'cx clean::Impl, DefId)>,
1664 f: &mut fmt::Formatter<'_>,
1665 cx: &Context<'_>,
1666) -> Result<(), fmt::Error> {
1667 let mut notable_impls = notable_impls.peekable();
1668 let code_tags_closing = if let Some((impl_, _)) = notable_impls.peek() {
1669 write!(
1670 f,
1671 "<h3>Notable traits for <code>{}</code></h3>\
1672 <pre><code>",
1673 print_type(&impl_.for_, cx),
1674 )?;
1675 "</code></pre>"
1676 } else {
1677 ""
1678 };
1679
1680 for (impl_, trait_did) in notable_impls {
1681 write!(f, "<div class=\"where\">{}</div>", print_impl(impl_, false, cx))?;
1682 for it in &impl_.items {
1683 let ItemKind::AssocType(ty @ clean::AssocType { type_: Some(_), .. }) = &it.kind else {
1684 continue;
1685 };
1686
1687 let empty_set = FxIndexSet::default();
1688 let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
1689
1690 write!(
1691 f,
1692 "<div class=\"where\"> {};</div>",
1693 assoc_ty(it, ty, src_link, 0, Ending::Newline, cx)
1694 )?;
1695 }
1696 }
1697
1698 f.write_str(code_tags_closing)
1699}
1700
1701fn notable_traits_decl_for_type(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
1702 let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
1703
1704 let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");
1705
1706 let out = fmt::from_fn(|f| {
1707 let notable_impls = impls
1708 .iter()
1709 .map(|impl_| impl_.inner_impl())
1710 .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive)
1711 .filter(|impl_| {
1712 ty.is_doc_subtype_of(&impl_.for_, cx.cache())
1714 })
1715 .filter_map(|impl_| {
1716 if let Some(trait_) = &impl_.trait_
1717 && let trait_did = trait_.def_id()
1718 && let Some(trait_) = cx.cache().traits.get(&trait_did)
1719 && trait_.is_notable_trait(cx.tcx())
1720 {
1721 Some((impl_, trait_did))
1722 } else {
1723 None
1724 }
1725 });
1726
1727 notable_traits_decl_write_html(notable_impls, f, cx)
1728 })
1729 .to_string();
1730
1731 (format!("{:#}", print_type(ty, cx)), out)
1732}
1733
1734fn notable_traits_decl_for_item(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
1735 let out = fmt::from_fn(|f| {
1736 let notable_impls = if let Some(def_id) = item.def_id()
1737 && !is_notable_trait_passthrough(def_id, cx)
1738 && let Some(impls) = cx.cache().impls.get(&def_id)
1739 {
1740 impls
1741 .iter()
1742 .map(Impl::inner_impl)
1743 .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive)
1744 .filter_map(|impl_| {
1745 if let Some(trait_) = &impl_.trait_
1746 && let trait_did = trait_.def_id()
1747 && let Some(trait_) = cx.cache().traits.get(&trait_did)
1748 && trait_.is_notable_trait(cx.tcx())
1749 {
1750 Some((impl_, trait_did))
1751 } else {
1752 None
1753 }
1754 })
1755 } else {
1756 return Ok(());
1757 };
1758 notable_traits_decl_write_html(notable_impls, f, cx)
1759 })
1760 .to_string();
1761
1762 Some((item.name?.to_string(), out))
1763}
1764
1765fn notable_traits_json<'a>(
1766 tys: impl Iterator<Item = &'a clean::Type>,
1767 item: &clean::Item,
1768 cx: &Context<'_>,
1769) -> String {
1770 let mut mp = tys.map(|ty| notable_traits_decl_for_type(ty, cx)).collect::<IndexMap<_, _>>();
1771 if let Some((item_name, item_decl)) = notable_traits_decl_for_item(item, cx)
1772 && !item_decl.is_empty()
1773 {
1774 mp.insert(item_name, item_decl);
1775 }
1776 mp.sort_unstable_keys();
1777 serde_json::to_string(&mp).expect("serialize (string, string) -> json object cannot fail")
1778}
1779
1780pub(crate) struct NotableTraitBadge {
1781 pub name: String,
1782 pub full_path: String,
1783 pub href: Option<String>,
1785 pub color: NotableTraitColor,
1787}
1788
1789pub(crate) fn notable_trait_badges(item: &clean::Item, cx: &Context<'_>) -> Vec<NotableTraitBadge> {
1792 let tcx = cx.tcx();
1793 if let Some(def_id) = item.def_id()
1794 && !is_notable_trait_passthrough(def_id, cx)
1795 && let Some(impls) = cx.cache().impls.get(&def_id)
1796 {
1797 impls
1798 .iter()
1799 .map(Impl::inner_impl)
1800 .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive)
1801 .filter_map(|impl_| {
1802 if let Some(trait_) = &impl_.trait_
1803 && let trait_did = trait_.def_id()
1804 && cx.cache().traits.contains_key(&trait_did)
1805 && let Some(&color) = tcx.doc_notable_trait(trait_did)
1806 {
1807 let name = tcx.item_name(trait_did).to_string();
1808 let (full_path, href) = match href(trait_did, cx) {
1809 Ok(info) => (join_path_syms(&info.rust_path), Some(info.url)),
1810 Err(_) => (tcx.def_path_str(trait_did), None),
1811 };
1812 Some((name.clone(), NotableTraitBadge { name, full_path, href, color }))
1813 } else {
1814 None
1815 }
1816 })
1817 .collect::<BTreeMap<String, NotableTraitBadge>>()
1818 .into_values()
1819 .collect()
1820 } else {
1821 Vec::new()
1822 }
1823}
1824
1825#[derive(Clone, Copy, Debug)]
1826struct ImplRenderingParameters {
1827 show_def_docs: bool,
1828 show_default_items: bool,
1829 show_non_assoc_items: bool,
1831 toggle_open_by_default: bool,
1832}
1833
1834fn render_impl(
1835 cx: &Context<'_>,
1836 i: &Impl,
1837 parent: &clean::Item,
1838 link: AssocItemLink<'_>,
1839 render_mode: RenderMode,
1840 use_absolute: Option<bool>,
1841 aliases: &[String],
1842 rendering_params: ImplRenderingParameters,
1843) -> impl fmt::Display {
1844 fmt::from_fn(move |w| {
1845 let cache = &cx.shared.cache;
1846 let traits = &cache.traits;
1847 let trait_ = i.trait_did().map(|did| &traits[&did]);
1848 let mut close_tags = <Vec<&str>>::with_capacity(2);
1849
1850 fn doc_impl_item(
1856 boring: impl fmt::Write,
1857 interesting: impl fmt::Write,
1858 cx: &Context<'_>,
1859 item: &clean::Item,
1860 parent: &clean::Item,
1861 link: AssocItemLink<'_>,
1862 render_mode: RenderMode,
1863 is_default_item: bool,
1864 trait_: Option<&clean::Trait>,
1865 rendering_params: ImplRenderingParameters,
1866 ) -> fmt::Result {
1867 let item_type = item.type_();
1868 let name = item.name.as_ref().unwrap();
1869
1870 let render_assoc_fn = rendering_params.show_non_assoc_items
1871 && match render_mode {
1872 RenderMode::Normal => true,
1873 RenderMode::ForDeref { mut_: deref_mut_, is_deref_target_copy } => {
1874 should_render_item_for_deref(
1875 item,
1876 deref_mut_,
1877 cx.tcx(),
1878 is_deref_target_copy,
1879 )
1880 }
1881 };
1882
1883 let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
1884
1885 let mut doc_buffer = String::new();
1886 let mut info_buffer = String::new();
1887 let mut short_documented = true;
1888
1889 let mut trait_item_deprecated = false;
1890 if render_assoc_fn {
1891 if !is_default_item {
1892 if let Some(t) = trait_ {
1893 if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
1896 trait_item_deprecated = it.is_deprecated(cx.tcx());
1897 if !item.doc_value().is_empty() {
1900 document_item_info(cx, it, Some(parent))
1901 .render_into(&mut info_buffer)?;
1902 doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1903 short_documented = false;
1904 } else {
1905 doc_buffer = document_short(
1908 it,
1909 cx,
1910 link,
1911 parent,
1912 rendering_params.show_def_docs,
1913 )
1914 .to_string();
1915 }
1916 }
1917 } else {
1918 document_item_info(cx, item, Some(parent)).render_into(&mut info_buffer)?;
1919 if rendering_params.show_def_docs {
1920 doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
1921 short_documented = false;
1922 }
1923 }
1924 } else {
1925 doc_buffer =
1926 document_short(item, cx, link, parent, rendering_params.show_def_docs)
1927 .to_string();
1928 }
1929 }
1930 let mut w = if short_documented && trait_.is_some() {
1931 Either::Left(interesting)
1932 } else {
1933 Either::Right(boring)
1934 };
1935
1936 let mut deprecation_class = if trait_item_deprecated || item.is_deprecated(cx.tcx()) {
1937 " deprecated"
1938 } else {
1939 ""
1940 };
1941
1942 let toggled = !doc_buffer.is_empty();
1943 if toggled {
1944 let method_toggle_class =
1945 if item_type.is_assoc_fn() { " method-toggle" } else { "" };
1946 write!(
1947 w,
1948 "<details class=\"toggle{method_toggle_class}{deprecation_class}\" open><summary>"
1949 )?;
1950 deprecation_class = "";
1951 }
1952 match &item.kind {
1953 ItemKind::AssocFunction(_) if !render_assoc_fn => {}
1954 ItemKind::AssocConstant(_) | ItemKind::AssocType(_)
1955 if let RenderMode::ForDeref { .. } = render_mode => {}
1956
1957 ItemKind::AssocFunction(_) => {
1958 let id = cx.derive_id(format!("{item_type}.{name}"));
1959 let source_id = trait_
1960 .and_then(|trait_| {
1961 trait_
1962 .items
1963 .iter()
1964 .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
1965 })
1966 .map(|item| format!("{}.{name}", item.type_()));
1967 write!(
1968 w,
1969 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1970 {}",
1971 render_rightside(cx, item, render_mode)
1972 )?;
1973 if trait_.is_some() {
1974 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
1976 }
1977 write!(
1978 w,
1979 "<h4 class=\"code-header\">{}</h4></section>",
1980 render_assoc_item(
1981 item,
1982 link.anchor(source_id.as_ref().unwrap_or(&id)),
1983 ItemType::Impl,
1984 0,
1985 Ending::Newline,
1986 render_mode,
1987 cx,
1988 ),
1989 )?;
1990 }
1991 ItemKind::AssocConstant(ct) => {
1992 let source_id = format!("{item_type}.{name}");
1993 let id = cx.derive_id(&source_id);
1994 write!(
1995 w,
1996 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
1997 {}",
1998 render_rightside(cx, item, render_mode),
1999 )?;
2000 if trait_.is_some() {
2001 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
2003 }
2004 write!(
2005 w,
2006 "<h4 class=\"code-header\">{}</h4></section>",
2007 assoc_const(
2008 item,
2009 ct,
2010 ItemType::Impl,
2011 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2012 0,
2013 Ending::Newline,
2014 cx,
2015 ),
2016 )?;
2017 }
2018 ItemKind::AssocType(ty) => {
2019 let source_id = format!("{item_type}.{name}");
2020 let id = cx.derive_id(&source_id);
2021 write!(
2022 w,
2023 "<section id=\"{id}\" class=\"{item_type}{in_trait_class}{deprecation_class}\">\
2024 {}",
2025 render_rightside(cx, item, render_mode),
2026 )?;
2027 if trait_.is_some() {
2028 write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
2030 }
2031 write!(
2032 w,
2033 "<h4 class=\"code-header\">{}</h4></section>",
2034 assoc_ty(
2035 item,
2036 ty,
2037 link.anchor(if trait_.is_some() { &source_id } else { &id }),
2038 0,
2039 Ending::Newline,
2040 cx,
2041 ),
2042 )?;
2043 }
2044 ItemKind::Stripped(..) => return Ok(()),
2045 _ => panic!("can't make docs for trait item with name {:?}", item.name),
2046 }
2047
2048 w.write_str(&info_buffer)?;
2049 if toggled {
2050 write!(w, "</summary>{doc_buffer}</details>")?;
2051 }
2052 Ok(())
2053 }
2054
2055 let mut impl_items = String::new();
2056 let mut default_impl_items = String::new();
2057 let impl_ = i.inner_impl();
2058
2059 let mut assoc_tys = Vec::new();
2069 let mut assoc_fns = Vec::new();
2070
2071 if !impl_.is_negative_trait_impl() {
2072 for impl_item in &impl_.items {
2073 match impl_item.kind {
2074 ItemKind::AssocFunction(..) => assoc_fns.push(impl_item),
2075 ItemKind::AssocType(..) => assoc_tys.push(impl_item),
2076 ItemKind::AssocConstant(_) => {
2077 doc_impl_item(
2079 &mut default_impl_items,
2080 &mut impl_items,
2081 cx,
2082 impl_item,
2083 if trait_.is_some() { &i.impl_item } else { parent },
2084 link,
2085 render_mode,
2086 false,
2087 trait_,
2088 rendering_params,
2089 )?;
2090 }
2091 _ => {}
2092 }
2093 }
2094
2095 for assoc_ty in assoc_tys {
2096 doc_impl_item(
2097 &mut default_impl_items,
2098 &mut impl_items,
2099 cx,
2100 assoc_ty,
2101 if trait_.is_some() { &i.impl_item } else { parent },
2102 link,
2103 render_mode,
2104 false,
2105 trait_,
2106 rendering_params,
2107 )?;
2108 }
2109 for assoc_fn in assoc_fns {
2110 doc_impl_item(
2111 &mut default_impl_items,
2112 &mut impl_items,
2113 cx,
2114 assoc_fn,
2115 if trait_.is_some() { &i.impl_item } else { parent },
2116 link,
2117 render_mode,
2118 false,
2119 trait_,
2120 rendering_params,
2121 )?;
2122 }
2123 }
2124
2125 fn render_default_items(
2126 mut boring: impl fmt::Write,
2127 mut interesting: impl fmt::Write,
2128 cx: &Context<'_>,
2129 t: &clean::Trait,
2130 i: &clean::Impl,
2131 parent: &clean::Item,
2132 render_mode: RenderMode,
2133 rendering_params: ImplRenderingParameters,
2134 ) -> fmt::Result {
2135 for trait_item in &t.items {
2136 if let Some(impl_def_id) = parent.item_id.as_def_id()
2139 && let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
2140 && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id))
2141 {
2142 continue;
2143 }
2144
2145 let n = trait_item.name;
2146 if i.items.iter().any(|m| m.name == n) {
2147 continue;
2148 }
2149 let did = i.trait_.as_ref().unwrap().def_id();
2150 let provided_methods = i.provided_trait_methods(cx.tcx());
2151 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
2152
2153 doc_impl_item(
2154 &mut boring,
2155 &mut interesting,
2156 cx,
2157 trait_item,
2158 parent,
2159 assoc_link,
2160 render_mode,
2161 true,
2162 Some(t),
2163 rendering_params,
2164 )?;
2165 }
2166 Ok(())
2167 }
2168
2169 if rendering_params.show_default_items
2174 && let Some(t) = trait_
2175 && !impl_.is_negative_trait_impl()
2176 {
2177 render_default_items(
2178 &mut default_impl_items,
2179 &mut impl_items,
2180 cx,
2181 t,
2182 impl_,
2183 &i.impl_item,
2184 render_mode,
2185 rendering_params,
2186 )?;
2187 }
2188 if render_mode == RenderMode::Normal {
2189 let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
2190 let deprecation_attr = if impl_.is_deprecated
2191 || trait_.is_some_and(|trait_| trait_.is_deprecated(cx.tcx()))
2192 {
2193 " deprecated"
2194 } else {
2195 ""
2196 };
2197 if toggled {
2198 close_tags.push("</details>");
2199 write!(
2200 w,
2201 "<details class=\"toggle implementors-toggle{deprecation_attr}\"{}>\
2202 <summary>",
2203 if rendering_params.toggle_open_by_default { " open" } else { "" }
2204 )?;
2205 }
2206
2207 let (before_dox, after_dox) = i
2208 .impl_item
2209 .opt_doc_value()
2210 .map(|dox| {
2211 Markdown {
2212 content: &dox,
2213 links: &i.impl_item.links(cx),
2214 ids: &mut cx.id_map.borrow_mut(),
2215 error_codes: cx.shared.codes,
2216 edition: cx.shared.edition(),
2217 playground: &cx.shared.playground,
2218 heading_offset: HeadingOffset::H4,
2219 }
2220 .split_summary_and_content()
2221 })
2222 .unwrap_or((None, None));
2223
2224 write!(
2225 w,
2226 "{}",
2227 render_impl_summary(
2228 cx,
2229 i,
2230 parent,
2231 rendering_params.show_def_docs,
2232 use_absolute,
2233 aliases,
2234 before_dox.as_deref(),
2235 trait_.is_none() && impl_.items.is_empty(),
2236 )
2237 )?;
2238 if toggled {
2239 w.write_str("</summary>")?;
2240 }
2241
2242 if before_dox.is_some()
2243 && let Some(after_dox) = after_dox
2244 {
2245 write!(w, "<div class=\"docblock\">{after_dox}</div>")?;
2246 }
2247
2248 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2249 w.write_str("<div class=\"impl-items\">")?;
2250 close_tags.push("</div>");
2251 }
2252 }
2253 if !default_impl_items.is_empty() || !impl_items.is_empty() {
2254 w.write_str(&default_impl_items)?;
2255 w.write_str(&impl_items)?;
2256 }
2257 for tag in close_tags.into_iter().rev() {
2258 w.write_str(tag)?;
2259 }
2260 Ok(())
2261 })
2262}
2263
2264fn render_rightside(
2267 cx: &Context<'_>,
2268 item: &clean::Item,
2269 render_mode: RenderMode,
2270) -> impl fmt::Display {
2271 let tcx = cx.tcx();
2272
2273 fmt::from_fn(move |w| {
2274 let const_stability = match render_mode {
2277 RenderMode::Normal => item.const_stability(tcx),
2278 RenderMode::ForDeref { .. } => None,
2279 };
2280 let src_href = cx.src_href(item);
2281 let stability = render_stability_since_raw_with_extra(
2282 item.stable_since(tcx),
2283 const_stability,
2284 if src_href.is_some() { "" } else { " rightside" },
2285 );
2286
2287 match (stability, src_href) {
2288 (Some(stability), Some(link)) => {
2289 write!(
2290 w,
2291 "<span class=\"rightside\">{stability} · <a class=\"src\" href=\"{link}\">Source</a></span>",
2292 )
2293 }
2294 (Some(stability), None) => {
2295 write!(w, "{stability}")
2296 }
2297 (None, Some(link)) => {
2298 write!(w, "<a class=\"src rightside\" href=\"{link}\">Source</a>")
2299 }
2300 (None, None) => Ok(()),
2301 }
2302 })
2303}
2304
2305fn render_impl_summary(
2306 cx: &Context<'_>,
2307 i: &Impl,
2308 parent: &clean::Item,
2309 show_def_docs: bool,
2310 use_absolute: Option<bool>,
2311 aliases: &[String],
2314 doc: Option<&str>,
2315 impl_is_empty: bool,
2316) -> impl fmt::Display {
2317 fmt::from_fn(move |w| {
2318 let inner_impl = i.inner_impl();
2319 let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
2320 let aliases = (!aliases.is_empty())
2321 .then_some(fmt::from_fn(|f| {
2322 write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f)))
2323 }))
2324 .maybe_display();
2325 write!(
2326 w,
2327 "<section id=\"{id}\" class=\"impl\"{aliases}>\
2328 {}\
2329 <a href=\"#{id}\" class=\"anchor\">§</a>\
2330 <h3 class=\"code-header\">",
2331 render_rightside(cx, &i.impl_item, RenderMode::Normal)
2332 )?;
2333
2334 if let Some(use_absolute) = use_absolute {
2335 write!(w, "{}", print_impl(inner_impl, use_absolute, cx))?;
2336 if show_def_docs {
2337 for it in &inner_impl.items {
2338 if let ItemKind::AssocType(ty) = &it.kind {
2339 write!(
2340 w,
2341 "<div class=\"where\"> {};</div>",
2342 assoc_ty(it, ty, AssocItemLink::Anchor(None), 0, Ending::Newline, cx,)
2343 )?;
2344 }
2345 }
2346 }
2347 } else {
2348 write!(w, "{}", print_impl(inner_impl, false, cx))?;
2349 }
2350 w.write_str("</h3>")?;
2351
2352 let is_trait = inner_impl.trait_.is_some();
2353 if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) {
2354 write!(
2355 w,
2356 "<span class=\"item-info\">\
2357 <div class=\"stab portability\">{portability}</div>\
2358 </span>",
2359 )?;
2360 }
2361
2362 if let Some(doc) = doc {
2363 if impl_is_empty {
2364 w.write_str(
2365 "\
2366<div class=\"item-info\">\
2367 <div class=\"stab empty-impl\">This impl block contains no public items.</div>\
2368</div>",
2369 )?;
2370 }
2371 write!(w, "<div class=\"docblock\">{doc}</div>")?;
2372 }
2373
2374 w.write_str("</section>")
2375 })
2376}
2377
2378pub(crate) fn small_url_encode(s: String) -> String {
2379 fn dont_escape(c: u8) -> bool {
2384 c.is_ascii_alphanumeric()
2385 || c == b'-'
2386 || c == b'_'
2387 || c == b'.'
2388 || c == b','
2389 || c == b'~'
2390 || c == b'!'
2391 || c == b'\''
2392 || c == b'('
2393 || c == b')'
2394 || c == b'*'
2395 || c == b'/'
2396 || c == b';'
2397 || c == b':'
2398 || c == b'?'
2399 || c == b'='
2403 }
2404 let mut st = String::new();
2405 let mut last_match = 0;
2406 for (idx, b) in s.bytes().enumerate() {
2407 if dont_escape(b) {
2408 continue;
2409 }
2410
2411 if last_match != idx {
2412 st += &s[last_match..idx];
2414 }
2415 if b == b' ' {
2416 st += "+";
2420 } else {
2421 write!(st, "%{b:02X}").unwrap();
2422 }
2423 last_match = idx + 1;
2429 }
2430
2431 if last_match != 0 {
2432 st += &s[last_match..];
2433 st
2434 } else {
2435 s
2436 }
2437}
2438
2439fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String {
2440 use rustc_middle::ty::print::with_forced_trimmed_paths;
2441 let (type_, trait_) = match impl_id {
2442 ItemId::Auto { trait_, for_ } => {
2443 let ty = tcx.type_of(for_).skip_binder();
2444 (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
2445 }
2446 ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
2447 if let Some(trait_ref) = tcx.impl_opt_trait_ref(impl_id) {
2448 let trait_ref = trait_ref.skip_binder();
2449 (trait_ref.self_ty(), Some(trait_ref))
2450 } else {
2451 (tcx.type_of(impl_id).skip_binder(), None)
2452 }
2453 }
2454 };
2455 with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
2456 format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
2457 } else {
2458 format!("impl-{type_}")
2459 }))
2460}
2461
2462fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
2463 match item.kind {
2464 clean::ItemKind::Impl(ref i) if i.trait_.is_some() => {
2465 Some((
2468 format!("{:#}", print_type(&i.for_, cx)),
2469 get_id_for_impl(cx.tcx(), item.item_id),
2470 ))
2471 }
2472 _ => None,
2473 }
2474}
2475
2476pub(crate) fn get_filtered_impls_for_reference<'a>(
2480 shared: &'a SharedContext<'_>,
2481 it: &clean::Item,
2482) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
2483 let def_id = it.item_id.expect_def_id();
2484 let Some(v) = shared.cache.impls.get(&def_id) else {
2486 return (Vec::new(), Vec::new(), Vec::new());
2487 };
2488 let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
2491 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
2492 traits.partition(|t| t.inner_impl().kind.is_auto());
2493
2494 let (blanket_impl, concrete): (Vec<&Impl>, _) =
2495 concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
2496 let concrete: Vec<_> = concrete
2498 .into_iter()
2499 .filter(|t| match t.inner_impl().for_ {
2500 clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
2501 _ => false,
2502 })
2503 .collect();
2504
2505 (concrete, synthetic, blanket_impl)
2506}
2507
2508#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2509pub(crate) enum ItemSection {
2510 Reexports,
2511 PrimitiveTypes,
2512 Modules,
2513 Macros,
2514 Structs,
2515 Enums,
2516 Constants,
2517 Statics,
2518 Traits,
2519 Functions,
2520 TypeAliases,
2521 Unions,
2522 Implementations,
2523 AssocFnsWithoutBody,
2524 AssocFnsWithBody,
2525 StructFields,
2526 Variants,
2527 AssociatedTypes,
2528 AssociatedConstants,
2529 ForeignTypes,
2530 Keywords,
2531 Attributes,
2532 AttributeMacros,
2533 DeriveMacros,
2534 TraitAliases,
2535}
2536
2537impl ItemSection {
2538 const ALL: &'static [Self] = {
2539 use ItemSection::*;
2540 &[
2543 Reexports,
2544 PrimitiveTypes,
2545 Modules,
2546 Macros,
2547 Structs,
2548 Enums,
2549 Constants,
2550 Statics,
2551 Traits,
2552 Functions,
2553 TypeAliases,
2554 Unions,
2555 Implementations,
2556 AssocFnsWithoutBody,
2557 AssocFnsWithBody,
2558 StructFields,
2559 Variants,
2560 AssociatedTypes,
2561 AssociatedConstants,
2562 ForeignTypes,
2563 Keywords,
2564 Attributes,
2565 AttributeMacros,
2566 DeriveMacros,
2567 TraitAliases,
2568 ]
2569 };
2570
2571 fn id(self) -> &'static str {
2572 match self {
2573 Self::Reexports => "reexports",
2574 Self::Modules => "modules",
2575 Self::Structs => "structs",
2576 Self::Unions => "unions",
2577 Self::Enums => "enums",
2578 Self::Functions => "functions",
2579 Self::TypeAliases => "types",
2580 Self::Statics => "statics",
2581 Self::Constants => "constants",
2582 Self::Traits => "traits",
2583 Self::Implementations => "impls",
2584 Self::AssocFnsWithoutBody => "tymethods",
2585 Self::AssocFnsWithBody => "methods",
2586 Self::StructFields => "fields",
2587 Self::Variants => "variants",
2588 Self::Macros => "macros",
2589 Self::PrimitiveTypes => "primitives",
2590 Self::AssociatedTypes => "associated-types",
2591 Self::AssociatedConstants => "associated-consts",
2592 Self::ForeignTypes => "foreign-types",
2593 Self::Keywords => "keywords",
2594 Self::Attributes => "attribute-docs",
2595 Self::AttributeMacros => "attributes",
2596 Self::DeriveMacros => "derives",
2597 Self::TraitAliases => "trait-aliases",
2598 }
2599 }
2600
2601 fn name(self) -> &'static str {
2602 match self {
2603 Self::Reexports => "Re-exports",
2604 Self::Modules => "Modules",
2605 Self::Structs => "Structs",
2606 Self::Unions => "Unions",
2607 Self::Enums => "Enums",
2608 Self::Functions => "Functions",
2609 Self::TypeAliases => "Type Aliases",
2610 Self::Statics => "Statics",
2611 Self::Constants => "Constants",
2612 Self::Traits => "Traits",
2613 Self::Implementations => "Implementations",
2614 Self::AssocFnsWithoutBody => "Type Methods",
2615 Self::AssocFnsWithBody => "Methods",
2616 Self::StructFields => "Struct Fields",
2617 Self::Variants => "Variants",
2618 Self::Macros => "Macros",
2619 Self::PrimitiveTypes => "Primitive Types",
2620 Self::AssociatedTypes => "Associated Types",
2621 Self::AssociatedConstants => "Associated Constants",
2622 Self::ForeignTypes => "Foreign Types",
2623 Self::Keywords => "Keywords",
2624 Self::Attributes => "Attributes",
2625 Self::AttributeMacros => "Attribute Macros",
2626 Self::DeriveMacros => "Derive Macros",
2627 Self::TraitAliases => "Trait Aliases",
2628 }
2629 }
2630}
2631
2632fn item_ty_to_section(ty: ItemType) -> ItemSection {
2633 match ty {
2634 ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
2635 ItemType::Module => ItemSection::Modules,
2636 ItemType::Struct => ItemSection::Structs,
2637 ItemType::Union => ItemSection::Unions,
2638 ItemType::Enum => ItemSection::Enums,
2639 ItemType::Function => ItemSection::Functions,
2640 ItemType::TypeAlias => ItemSection::TypeAliases,
2641 ItemType::Static => ItemSection::Statics,
2642 ItemType::Constant => ItemSection::Constants,
2643 ItemType::Trait => ItemSection::Traits,
2644 ItemType::Impl => ItemSection::Implementations,
2645 ItemType::AssocFnWithoutBody => ItemSection::AssocFnsWithoutBody,
2646 ItemType::AssocFnWithBody => ItemSection::AssocFnsWithBody,
2647 ItemType::StructField => ItemSection::StructFields,
2648 ItemType::Variant => ItemSection::Variants,
2649 ItemType::Macro => ItemSection::Macros,
2650 ItemType::Primitive => ItemSection::PrimitiveTypes,
2651 ItemType::AssocType => ItemSection::AssociatedTypes,
2652 ItemType::AssocConst => ItemSection::AssociatedConstants,
2653 ItemType::ForeignType => ItemSection::ForeignTypes,
2654 ItemType::Keyword => ItemSection::Keywords,
2655 ItemType::Attribute => ItemSection::Attributes,
2656 ItemType::ProcAttribute | ItemType::DeclMacroAttribute => ItemSection::AttributeMacros,
2657 ItemType::ProcDerive | ItemType::DeclMacroDerive => ItemSection::DeriveMacros,
2658 ItemType::TraitAlias => ItemSection::TraitAliases,
2659 }
2660}
2661
2662fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec<String> {
2669 let mut out = Vec::new();
2670 let mut visited = FxHashSet::default();
2671 let mut work = VecDeque::new();
2672
2673 let mut process_path = |did: DefId| {
2674 let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
2675 let fqp = cache.exact_paths.get(&did).or_else(get_extern);
2676
2677 if let Some(path) = fqp {
2678 out.push(join_path_syms(path));
2679 }
2680 };
2681
2682 work.push_back(first_ty);
2683
2684 while let Some(ty) = work.pop_front() {
2685 if !visited.insert(ty) {
2686 continue;
2687 }
2688
2689 match ty {
2690 clean::Type::Path { path } => process_path(path.def_id()),
2691 clean::Type::Tuple(tys) => {
2692 work.extend(tys.iter());
2693 }
2694 clean::Type::Slice(ty) => {
2695 work.push_back(ty);
2696 }
2697 clean::Type::Array(ty, _) => {
2698 work.push_back(ty);
2699 }
2700 clean::Type::RawPointer(_, ty) => {
2701 work.push_back(ty);
2702 }
2703 clean::Type::BorrowedRef { type_, .. } => {
2704 work.push_back(type_);
2705 }
2706 clean::Type::QPath(clean::QPathData { self_type, trait_, .. }) => {
2707 work.push_back(self_type);
2708 if let Some(trait_) = trait_ {
2709 process_path(trait_.def_id());
2710 }
2711 }
2712 _ => {}
2713 }
2714 }
2715 out
2716}
2717
2718const MAX_FULL_EXAMPLES: usize = 5;
2719const NUM_VISIBLE_LINES: usize = 10;
2720
2721fn render_call_locations<W: fmt::Write>(
2723 mut w: W,
2724 cx: &Context<'_>,
2725 item: &clean::Item,
2726) -> fmt::Result {
2727 let tcx = cx.tcx();
2728 let def_id = item.item_id.expect_def_id();
2729 let key = tcx.def_path_hash(def_id);
2730 let Some(call_locations) = cx.shared.call_locations.get(&key) else { return Ok(()) };
2731
2732 let id = cx.derive_id("scraped-examples");
2734 write!(
2735 &mut w,
2736 "<div class=\"docblock scraped-example-list\">\
2737 <span></span>\
2738 <h5 id=\"{id}\">\
2739 <a href=\"#{id}\">Examples found in repository</a>\
2740 <a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
2741 </h5>",
2742 root_path = cx.root_path(),
2743 id = id
2744 )?;
2745
2746 let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
2748 let (line_lo, line_hi) = loc.call_expr.line_span;
2749 let (anchor, title) = if line_lo == line_hi {
2750 ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
2751 } else {
2752 (
2753 format!("{}-{}", line_lo + 1, line_hi + 1),
2754 format!("lines {}-{}", line_lo + 1, line_hi + 1),
2755 )
2756 };
2757 let url = format!("{}{}#{anchor}", cx.root_path(), call_data.url);
2758 (url, title)
2759 };
2760
2761 let write_example = |w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
2763 let contents = match fs::read_to_string(path) {
2764 Ok(contents) => contents,
2765 Err(err) => {
2766 let span = item.span(tcx).map_or(DUMMY_SP, |span| span.inner());
2767 tcx.dcx().span_err(span, format!("failed to read file {}: {err}", path.display()));
2768 return false;
2769 }
2770 };
2771
2772 assert!(!call_data.locations.is_empty());
2775 let min_loc =
2776 call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2777 let byte_min = min_loc.enclosing_item.byte_span.0;
2778 let line_min = min_loc.enclosing_item.line_span.0;
2779 let max_loc =
2780 call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
2781 let byte_max = max_loc.enclosing_item.byte_span.1;
2782 let line_max = max_loc.enclosing_item.line_span.1;
2783
2784 let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
2786
2787 let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
2790 .locations
2791 .iter()
2792 .map(|loc| {
2793 let (byte_lo, byte_hi) = loc.call_ident.byte_span;
2794 let (line_lo, line_hi) = loc.call_expr.line_span;
2795 let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
2796
2797 let line_range = (line_lo - line_min, line_hi - line_min);
2798 let (line_url, line_title) = link_to_loc(call_data, loc);
2799
2800 (byte_range, (line_range, line_url, line_title))
2801 })
2802 .unzip();
2803
2804 let (_, init_url, init_title) = &line_ranges[0];
2805 let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
2806 let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
2807
2808 let file_span = rustc_span::DUMMY_SP;
2814
2815 let mut decoration_info = FxIndexMap::default();
2816 decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
2817 decoration_info.insert("highlight", byte_ranges);
2818
2819 sources::print_src(
2820 w,
2821 contents_subset,
2822 file_span,
2823 cx,
2824 cx.current.len(),
2825 &highlight::DecorationInfo(decoration_info),
2826 &sources::SourceContext::Embedded(sources::ScrapedInfo {
2827 needs_expansion,
2828 offset: line_min,
2829 name: &call_data.display_name,
2830 url: init_url,
2831 title: init_title,
2832 locations: locations_encoded,
2833 }),
2834 )
2835 .unwrap();
2836
2837 true
2838 };
2839
2840 let ordered_locations = {
2852 fn sort_criterion<'a>(
2853 (_, call_data): &(&PathBuf, &'a CallData),
2854 ) -> (bool, u32, &'a String) {
2855 let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2857 (!call_data.is_bin, hi - lo, &call_data.display_name)
2858 }
2859
2860 let mut locs = call_locations.iter().collect::<Vec<_>>();
2861 locs.sort_by_key(sort_criterion);
2862 locs
2863 };
2864
2865 let mut it = ordered_locations.into_iter().peekable();
2866
2867 let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
2870 for example in it.by_ref() {
2871 if write_example(&mut *w, example) {
2872 break;
2873 }
2874 }
2875 };
2876
2877 write_and_skip_failure(&mut w, &mut it);
2879
2880 if it.peek().is_some() {
2882 write!(
2883 w,
2884 "<details class=\"toggle more-examples-toggle\">\
2885 <summary class=\"hideme\">\
2886 <span>More examples</span>\
2887 </summary>\
2888 <div class=\"hide-more\">Hide additional examples</div>\
2889 <div class=\"more-scraped-examples\">\
2890 <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
2891 )?;
2892
2893 for _ in 0..MAX_FULL_EXAMPLES {
2896 write_and_skip_failure(&mut w, &mut it);
2897 }
2898
2899 if it.peek().is_some() {
2901 w.write_str(
2902 r#"<div class="example-links">Additional examples can be found in:<br><ul>"#,
2903 )?;
2904 it.try_for_each(|(_, call_data)| {
2905 let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
2906 write!(
2907 w,
2908 r#"<li><a href="{url}">{name}</a></li>"#,
2909 url = url,
2910 name = call_data.display_name
2911 )
2912 })?;
2913 w.write_str("</ul></div>")?;
2914 }
2915
2916 w.write_str("</div></details>")?;
2917 }
2918
2919 w.write_str("</div>")
2920}
2921
2922fn render_attributes_in_code(
2923 w: &mut impl fmt::Write,
2924 item: &clean::Item,
2925 prefix: impl fmt::Display,
2926 cx: &Context<'_>,
2927) -> fmt::Result {
2928 render_attributes_in_code_with_options(w, item, prefix, cx, true, "")
2929}
2930
2931pub(super) fn render_attributes_in_code_with_options(
2932 w: &mut impl fmt::Write,
2933 item: &clean::Item,
2934 prefix: impl fmt::Display,
2935 cx: &Context<'_>,
2936 render_doc_hidden: bool,
2937 open_tag: &str,
2938) -> fmt::Result {
2939 w.write_str(open_tag)?;
2940 if render_doc_hidden && item.is_doc_hidden() {
2941 render_code_attribute(&prefix, "#[doc(hidden)]", w)?;
2942 }
2943 for attr in &item.attrs.other_attrs {
2944 let rustc_attr_ir::Attribute::Parsed(kind) = attr else { continue };
2945 let attr = match kind {
2946 AttributeKind::LinkSection { name, .. } => {
2947 Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}"))))
2948 }
2949 AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"),
2950 AttributeKind::ExportName { name, .. } => {
2951 Cow::Owned(format!("#[unsafe(export_name = {})]", Escape(&format!("{name:?}"))))
2952 }
2953 AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"),
2954 _ => continue,
2955 };
2956 render_code_attribute(&prefix, attr.as_ref(), w)?;
2957 }
2958
2959 if let Some(def_id) = item.def_id()
2960 && let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id)
2961 {
2962 render_code_attribute(prefix, &repr, w)?;
2963 }
2964 Ok(())
2965}
2966
2967fn render_repr_attribute_in_code(
2968 w: &mut impl fmt::Write,
2969 cx: &Context<'_>,
2970 def_id: DefId,
2971) -> fmt::Result {
2972 if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) {
2973 render_code_attribute("", &repr, w)?;
2974 }
2975 Ok(())
2976}
2977
2978fn render_code_attribute(
2979 prefix: impl fmt::Display,
2980 attr: impl fmt::Display,
2981 w: &mut impl fmt::Write,
2982) -> fmt::Result {
2983 write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>")
2984}
2985
2986fn repr_attribute<'tcx>(
2991 tcx: TyCtxt<'tcx>,
2992 cache: &Cache,
2993 def_id: DefId,
2994) -> Option<Cow<'static, str>> {
2995 let adt = match tcx.def_kind(def_id) {
2996 DefKind::Struct | DefKind::Enum | DefKind::Union => tcx.adt_def(def_id),
2997 _ => return None,
2998 };
2999 let repr = adt.repr();
3000
3001 let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id);
3002 let is_public_field = |field: &ty::FieldDef| {
3003 (cache.document_private || field.vis.is_public()) && is_visible(field.did)
3004 };
3005
3006 if repr.transparent() {
3011 let is_public = 'is_public: {
3012 if find_attr!(tcx, def_id, RustcPubTransparent(_)) {
3013 break 'is_public true;
3014 }
3015
3016 let var = adt.variant(rustc_abi::FIRST_VARIANT); if !is_visible(var.def_id) {
3020 break 'is_public false;
3021 }
3022
3023 let non_1zst_field = var.fields.iter().find(|field| {
3025 let ty = ty::TypingEnv::post_analysis(tcx, field.did)
3026 .as_query_input(tcx.type_of(field.did).instantiate_identity().skip_norm_wip());
3027 tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst())
3028 });
3029
3030 match non_1zst_field {
3031 Some(field) => is_public_field(field),
3032 None => var.fields.is_empty() || var.fields.iter().any(is_public_field),
3033 }
3034 };
3035
3036 return is_public.then(|| "#[repr(transparent)]".into());
3039 }
3040
3041 let is_public = adt
3043 .variants()
3044 .iter()
3045 .all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field));
3046 if !is_public {
3047 return None;
3048 }
3049
3050 let mut result = Vec::<Cow<'_, _>>::new();
3051
3052 if repr.c() {
3053 result.push("C".into());
3054 }
3055 if repr.simd() {
3056 result.push("simd".into());
3057 }
3058 if let Some(int) = repr.int {
3059 let prefix = if int.is_signed() { 'i' } else { 'u' };
3060 let int = match int {
3061 rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"),
3062 rustc_abi::IntegerType::Fixed(int, _) => {
3063 format!("{prefix}{}", int.size().bytes() * 8)
3064 }
3065 };
3066 result.push(int.into());
3067 }
3068
3069 if let Some(pack) = repr.pack {
3071 result.push(format!("packed({})", pack.bytes()).into());
3072 }
3073 if let Some(align) = repr.align {
3074 result.push(format!("align({})", align.bytes()).into());
3075 }
3076
3077 (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into())
3078}
3079
3080pub(crate) fn compute_if_deref_target_implements_copy(tcx: TyCtxt<'_>, impl_def_id: DefId) -> bool {
3081 if let Some(impl_def_id) = impl_def_id.as_local()
3082 && let item = tcx.hir_expect_item(impl_def_id)
3083 && let rustc_hir::ItemKind::Impl(impl_item) = item.kind
3084 {
3085 for item in impl_item.items {
3086 let item = tcx.hir_impl_item(*item);
3087 if matches!(item.kind, rustc_hir::ImplItemKind::Type(_)) {
3088 let item_def_id = item.owner_id.to_def_id();
3089 let ty = tcx.type_of(item_def_id).instantiate_identity().skip_norm_wip();
3090 let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id);
3091 return tcx.type_is_copy_modulo_regions(typing_env, ty);
3092 }
3093 }
3094 }
3095 false
3096}