diff options
Diffstat (limited to '')
-rw-r--r-- | src/librustdoc/html/render/mod.rs | 609 |
1 files changed, 373 insertions, 236 deletions
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index a262c8f7d..96c57c8c8 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -74,7 +74,9 @@ use crate::html::format::{ PrintWithSpace, }; use crate::html::highlight; -use crate::html::markdown::{HeadingOffset, IdMap, Markdown, MarkdownHtml, MarkdownSummaryLine}; +use crate::html::markdown::{ + HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine, +}; use crate::html::sources; use crate::html::static_files::SCRAPE_EXAMPLES_HELP_MD; use crate::scrape_examples::{CallData, CallLocation}; @@ -191,12 +193,6 @@ impl StylePath { } } -fn write_srclink(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) { - if let Some(l) = cx.src_href(item) { - write!(buf, "<a class=\"srclink\" href=\"{}\">source</a>", l) - } -} - #[derive(Debug, Eq, PartialEq, Hash)] struct ItemEntry { url: String, @@ -245,8 +241,8 @@ struct AllTypes { opaque_tys: FxHashSet<ItemEntry>, statics: FxHashSet<ItemEntry>, constants: FxHashSet<ItemEntry>, - attributes: FxHashSet<ItemEntry>, - derives: FxHashSet<ItemEntry>, + attribute_macros: FxHashSet<ItemEntry>, + derive_macros: FxHashSet<ItemEntry>, trait_aliases: FxHashSet<ItemEntry>, } @@ -265,8 +261,8 @@ impl AllTypes { opaque_tys: new_set(100), statics: new_set(100), constants: new_set(100), - attributes: new_set(100), - derives: new_set(100), + attribute_macros: new_set(100), + derive_macros: new_set(100), trait_aliases: new_set(100), } } @@ -289,27 +285,75 @@ impl AllTypes { ItemType::OpaqueTy => self.opaque_tys.insert(ItemEntry::new(new_url, name)), ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)), ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)), - ItemType::ProcAttribute => self.attributes.insert(ItemEntry::new(new_url, name)), - ItemType::ProcDerive => self.derives.insert(ItemEntry::new(new_url, name)), + ItemType::ProcAttribute => { + self.attribute_macros.insert(ItemEntry::new(new_url, name)) + } + ItemType::ProcDerive => self.derive_macros.insert(ItemEntry::new(new_url, name)), ItemType::TraitAlias => self.trait_aliases.insert(ItemEntry::new(new_url, name)), _ => true, }; } } -} -impl AllTypes { + fn item_sections(&self) -> FxHashSet<ItemSection> { + let mut sections = FxHashSet::default(); + + if !self.structs.is_empty() { + sections.insert(ItemSection::Structs); + } + if !self.enums.is_empty() { + sections.insert(ItemSection::Enums); + } + if !self.unions.is_empty() { + sections.insert(ItemSection::Unions); + } + if !self.primitives.is_empty() { + sections.insert(ItemSection::PrimitiveTypes); + } + if !self.traits.is_empty() { + sections.insert(ItemSection::Traits); + } + if !self.macros.is_empty() { + sections.insert(ItemSection::Macros); + } + if !self.functions.is_empty() { + sections.insert(ItemSection::Functions); + } + if !self.typedefs.is_empty() { + sections.insert(ItemSection::TypeDefinitions); + } + if !self.opaque_tys.is_empty() { + sections.insert(ItemSection::OpaqueTypes); + } + if !self.statics.is_empty() { + sections.insert(ItemSection::Statics); + } + if !self.constants.is_empty() { + sections.insert(ItemSection::Constants); + } + if !self.attribute_macros.is_empty() { + sections.insert(ItemSection::AttributeMacros); + } + if !self.derive_macros.is_empty() { + sections.insert(ItemSection::DeriveMacros); + } + if !self.trait_aliases.is_empty() { + sections.insert(ItemSection::TraitAliases); + } + + sections + } + fn print(self, f: &mut Buffer) { - fn print_entries(f: &mut Buffer, e: &FxHashSet<ItemEntry>, title: &str, class: &str) { + fn print_entries(f: &mut Buffer, e: &FxHashSet<ItemEntry>, kind: ItemSection) { if !e.is_empty() { let mut e: Vec<&ItemEntry> = e.iter().collect(); e.sort(); write!( f, - "<h3 id=\"{}\">{}</h3><ul class=\"{} docblock\">", - title.replace(' ', "-"), // IDs cannot contain whitespaces. - title, - class + "<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">", + id = kind.id(), + title = kind.name(), ); for s in e.iter() { @@ -320,27 +364,23 @@ impl AllTypes { } } - f.write_str( - "<h1 class=\"fqn\">\ - <span class=\"in-band\">List of all items</span>\ - </h1>", - ); + f.write_str("<h1 class=\"fqn\">List of all items</h1>"); // Note: print_entries does not escape the title, because we know the current set of titles // doesn't require escaping. - print_entries(f, &self.structs, "Structs", "structs"); - print_entries(f, &self.enums, "Enums", "enums"); - print_entries(f, &self.unions, "Unions", "unions"); - print_entries(f, &self.primitives, "Primitives", "primitives"); - print_entries(f, &self.traits, "Traits", "traits"); - print_entries(f, &self.macros, "Macros", "macros"); - print_entries(f, &self.attributes, "Attribute Macros", "attributes"); - print_entries(f, &self.derives, "Derive Macros", "derives"); - print_entries(f, &self.functions, "Functions", "functions"); - print_entries(f, &self.typedefs, "Typedefs", "typedefs"); - print_entries(f, &self.trait_aliases, "Trait Aliases", "trait-aliases"); - print_entries(f, &self.opaque_tys, "Opaque Types", "opaque-types"); - print_entries(f, &self.statics, "Statics", "statics"); - print_entries(f, &self.constants, "Constants", "constants") + print_entries(f, &self.structs, ItemSection::Structs); + print_entries(f, &self.enums, ItemSection::Enums); + print_entries(f, &self.unions, ItemSection::Unions); + print_entries(f, &self.primitives, ItemSection::PrimitiveTypes); + print_entries(f, &self.traits, ItemSection::Traits); + print_entries(f, &self.macros, ItemSection::Macros); + print_entries(f, &self.attribute_macros, ItemSection::AttributeMacros); + print_entries(f, &self.derive_macros, ItemSection::DeriveMacros); + print_entries(f, &self.functions, ItemSection::Functions); + print_entries(f, &self.typedefs, ItemSection::TypeDefinitions); + print_entries(f, &self.trait_aliases, ItemSection::TraitAliases); + print_entries(f, &self.opaque_tys, ItemSection::OpaqueTypes); + print_entries(f, &self.statics, ItemSection::Statics); + print_entries(f, &self.constants, ItemSection::Constants); } } @@ -354,9 +394,7 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String { let mut ids = IdMap::default(); format!( "<div class=\"main-heading\">\ - <h1 class=\"fqn\">\ - <span class=\"in-band\">About scraped examples</span>\ - </h1>\ + <h1 class=\"fqn\">About scraped examples</h1>\ </div>\ <div>{}</div>", Markdown { @@ -522,7 +560,14 @@ fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<Strin (cfg, _) => cfg.as_deref().cloned(), }; - debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.and_then(|p| p.cfg.as_ref()), cfg); + debug!( + "Portability {:?} {:?} (parent: {:?}) - {:?} = {:?}", + item.name, + item.cfg, + parent, + parent.and_then(|p| p.cfg.as_ref()), + cfg + ); Some(format!("<div class=\"stab portability\">{}</div>", cfg?.render_long_html())) } @@ -535,7 +580,6 @@ fn short_item_info( parent: Option<&clean::Item>, ) -> Vec<String> { let mut extra_info = vec![]; - let error_codes = cx.shared.codes; if let Some(depr @ Deprecation { note, since, is_since_rustc_version: _, suggestion: _ }) = item.deprecation(cx.tcx()) @@ -559,17 +603,14 @@ fn short_item_info( if let Some(note) = note { let note = note.as_str(); - let html = MarkdownHtml( - note, - &mut cx.id_map, - error_codes, - cx.shared.edition(), - &cx.shared.playground, - ); + let html = MarkdownItemInfo(note, &mut cx.id_map); message.push_str(&format!(": {}", html.into_string())); } extra_info.push(format!( - "<div class=\"stab deprecated\"><span class=\"emoji\">๐</span> {}</div>", + "<div class=\"stab deprecated\">\ + <span class=\"emoji\">๐</span>\ + <span>{}</span>\ + </div>", message, )); } @@ -582,8 +623,9 @@ fn short_item_info( .filter(|stab| stab.feature != sym::rustc_private) .map(|stab| (stab.level, stab.feature)) { - let mut message = - "<span class=\"emoji\">๐ฌ</span> This is a nightly-only experimental API.".to_owned(); + let mut message = "<span class=\"emoji\">๐ฌ</span>\ + <span>This is a nightly-only experimental API." + .to_owned(); let mut feature = format!("<code>{}</code>", Escape(feature.as_str())); if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) { @@ -594,7 +636,7 @@ fn short_item_info( )); } - message.push_str(&format!(" ({})", feature)); + message.push_str(&format!(" ({})</span>", feature)); extra_info.push(format!("<div class=\"stab unstable\">{}</div>", message)); } @@ -608,10 +650,10 @@ fn short_item_info( // Render the list of items inside one of the sections "Trait Implementations", // "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages). -fn render_impls( +pub(crate) fn render_impls( cx: &mut Context<'_>, w: &mut Buffer, - impls: &[&&Impl], + impls: &[&Impl], containing_item: &clean::Item, toggle_open_by_default: bool, ) { @@ -816,7 +858,7 @@ fn assoc_method( href = href, name = name, generics = g.print(cx), - decl = d.full_print(header_len, indent, header.asyncness, cx), + decl = d.full_print(header_len, indent, cx), notable_traits = notable_traits_decl(d, cx), where_clause = print_where_clause(g, cx, indent, end_newline), ) @@ -836,12 +878,13 @@ fn assoc_method( /// Note that it is possible for an unstable function to be const-stable. In that case, the span /// will include the const-stable version, but no stable version will be emitted, as a natural /// consequence of the above rules. -fn render_stability_since_raw( +fn render_stability_since_raw_with_extra( w: &mut Buffer, ver: Option<Symbol>, const_stability: Option<ConstStability>, containing_ver: Option<Symbol>, containing_const_ver: Option<Symbol>, + extra_class: &str, ) -> bool { let stable_version = ver.filter(|inner| !inner.is_empty() && Some(*inner) != containing_ver); @@ -889,12 +932,30 @@ fn render_stability_since_raw( } if !stability.is_empty() { - write!(w, r#"<span class="since" title="{}">{}</span>"#, title, stability); + write!(w, r#"<span class="since{extra_class}" title="{title}">{stability}</span>"#); } !stability.is_empty() } +#[inline] +fn render_stability_since_raw( + w: &mut Buffer, + ver: Option<Symbol>, + const_stability: Option<ConstStability>, + containing_ver: Option<Symbol>, + containing_const_ver: Option<Symbol>, +) -> bool { + render_stability_since_raw_with_extra( + w, + ver, + const_stability, + containing_ver, + containing_const_ver, + "", + ) +} + fn render_assoc_item( w: &mut Buffer, item: &clean::Item, @@ -1001,6 +1062,47 @@ impl<'a> AssocItemLink<'a> { } } +fn write_impl_section_heading(w: &mut Buffer, title: &str, id: &str) { + write!( + w, + "<h2 id=\"{id}\" class=\"small-section-header\">\ + {title}\ + <a href=\"#{id}\" class=\"anchor\"></a>\ + </h2>" + ); +} + +pub(crate) fn render_all_impls( + w: &mut Buffer, + cx: &mut Context<'_>, + containing_item: &clean::Item, + concrete: &[&Impl], + synthetic: &[&Impl], + blanket_impl: &[&Impl], +) { + let mut impls = Buffer::empty_from(w); + render_impls(cx, &mut impls, concrete, containing_item, true); + let impls = impls.into_inner(); + if !impls.is_empty() { + write_impl_section_heading(w, "Trait Implementations", "trait-implementations"); + write!(w, "<div id=\"trait-implementations-list\">{}</div>", impls); + } + + if !synthetic.is_empty() { + write_impl_section_heading(w, "Auto Trait Implementations", "synthetic-implementations"); + w.write_str("<div id=\"synthetic-implementations-list\">"); + render_impls(cx, w, synthetic, containing_item, false); + w.write_str("</div>"); + } + + if !blanket_impl.is_empty() { + write_impl_section_heading(w, "Blanket Implementations", "blanket-implementations"); + w.write_str("<div id=\"blanket-implementations-list\">"); + render_impls(cx, w, blanket_impl, containing_item, false); + w.write_str("</div>"); + } +} + fn render_assoc_items( w: &mut Buffer, cx: &mut Context<'_>, @@ -1030,12 +1132,7 @@ fn render_assoc_items_inner( let mut tmp_buf = Buffer::empty_from(w); let (render_mode, id) = match what { AssocItemRender::All => { - tmp_buf.write_str( - "<h2 id=\"implementations\" class=\"small-section-header\">\ - Implementations\ - <a href=\"#implementations\" class=\"anchor\"></a>\ - </h2>", - ); + write_impl_section_heading(&mut tmp_buf, "Implementations", "implementations"); (RenderMode::Normal, "implementations-list".to_owned()) } AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { @@ -1044,15 +1141,14 @@ fn render_assoc_items_inner( if let Some(def_id) = type_.def_id(cx.cache()) { cx.deref_id_map.insert(def_id, id.clone()); } - write!( - tmp_buf, - "<h2 id=\"{id}\" class=\"small-section-header\">\ - <span>Methods from {trait_}<Target = {type_}></span>\ - <a href=\"#{id}\" class=\"anchor\"></a>\ - </h2>", - id = id, - trait_ = trait_.print(cx), - type_ = type_.print(cx), + write_impl_section_heading( + &mut tmp_buf, + &format!( + "<span>Methods from {trait_}<Target = {type_}></span>", + trait_ = trait_.print(cx), + type_ = type_.print(cx), + ), + &id, ); (RenderMode::ForDeref { mut_: deref_mut_ }, cx.derive_id(id)) } @@ -1099,49 +1195,12 @@ fn render_assoc_items_inner( return; } - let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) = - traits.iter().partition(|t| t.inner_impl().kind.is_auto()); - let (blanket_impl, concrete): (Vec<&&Impl>, _) = + let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = + traits.into_iter().partition(|t| t.inner_impl().kind.is_auto()); + let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket()); - let mut impls = Buffer::empty_from(w); - render_impls(cx, &mut impls, &concrete, containing_item, true); - let impls = impls.into_inner(); - if !impls.is_empty() { - write!( - w, - "<h2 id=\"trait-implementations\" class=\"small-section-header\">\ - Trait Implementations\ - <a href=\"#trait-implementations\" class=\"anchor\"></a>\ - </h2>\ - <div id=\"trait-implementations-list\">{}</div>", - impls - ); - } - - if !synthetic.is_empty() { - w.write_str( - "<h2 id=\"synthetic-implementations\" class=\"small-section-header\">\ - Auto Trait Implementations\ - <a href=\"#synthetic-implementations\" class=\"anchor\"></a>\ - </h2>\ - <div id=\"synthetic-implementations-list\">", - ); - render_impls(cx, w, &synthetic, containing_item, false); - w.write_str("</div>"); - } - - if !blanket_impl.is_empty() { - w.write_str( - "<h2 id=\"blanket-implementations\" class=\"small-section-header\">\ - Blanket Implementations\ - <a href=\"#blanket-implementations\" class=\"anchor\"></a>\ - </h2>\ - <div id=\"blanket-implementations-list\">", - ); - render_impls(cx, w, &blanket_impl, containing_item, false); - w.write_str("</div>"); - } + render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl); } } @@ -1217,6 +1276,15 @@ fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String { if let Some((did, ty)) = decl.output.as_return().and_then(|t| Some((t.def_id(cx.cache())?, t))) { + // Box has pass-through impls for Read, Write, Iterator, and Future when the + // boxed type implements one of those. We don't want to treat every Box return + // as being notably an Iterator (etc), though, so we exempt it. Pin has the same + // issue, with a pass-through impl for Future. + if Some(did) == cx.tcx().lang_items().owned_box() + || Some(did) == cx.tcx().lang_items().pin_type() + { + return "".to_string(); + } if let Some(impls) = cx.cache().impls.get(&did) { for i in impls { let impl_ = i.inner_impl(); @@ -1229,7 +1297,12 @@ fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String { if let Some(trait_) = &impl_.trait_ { let trait_did = trait_.def_id(); - if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable) { + if cx + .cache() + .traits + .get(&trait_did) + .map_or(false, |t| t.is_notable_trait(cx.tcx())) + { if out.is_empty() { write!( &mut out, @@ -1533,7 +1606,7 @@ fn render_impl( link, render_mode, false, - trait_.map(|t| &t.trait_), + trait_, rendering_params, ); } @@ -1550,6 +1623,15 @@ fn render_impl( rendering_params: ImplRenderingParameters, ) { for trait_item in &t.items { + // Skip over any default trait items that are impossible to call + // (e.g. if it has a `Self: Sized` bound on an unsized type). + if let Some(impl_def_id) = parent.item_id.as_def_id() + && let Some(trait_item_def_id) = trait_item.item_id.as_def_id() + && cx.tcx().is_impossible_method((impl_def_id, trait_item_def_id)) + { + continue; + } + let n = trait_item.name; if i.items.iter().any(|m| m.name == n) { continue; @@ -1584,7 +1666,7 @@ fn render_impl( &mut default_impl_items, &mut impl_items, cx, - &t.trait_, + t, i.inner_impl(), &i.impl_item, parent, @@ -1668,23 +1750,29 @@ fn render_rightside( RenderMode::Normal => (item.const_stability(tcx), containing_item.const_stable_since(tcx)), RenderMode::ForDeref { .. } => (None, None), }; + let src_href = cx.src_href(item); + let has_src_ref = src_href.is_some(); let mut rightside = Buffer::new(); - let has_stability = render_stability_since_raw( + let has_stability = render_stability_since_raw_with_extra( &mut rightside, item.stable_since(tcx), const_stability, containing_item.stable_since(tcx), const_stable_since, + if has_src_ref { "" } else { " rightside" }, ); - let mut srclink = Buffer::empty_from(w); - write_srclink(cx, item, &mut srclink); - if has_stability && !srclink.is_empty() { - rightside.write_str(" ยท "); + if let Some(l) = src_href { + if has_stability { + write!(rightside, " ยท <a class=\"srclink\" href=\"{}\">source</a>", l) + } else { + write!(rightside, "<a class=\"srclink rightside\" href=\"{}\">source</a>", l) + } } - rightside.push_buffer(srclink); - if !rightside.is_empty() { + if has_stability && has_src_ref { write!(w, "<span class=\"rightside\">{}</span>", rightside.into_inner()); + } else { + w.push_buffer(rightside); } } @@ -1700,8 +1788,8 @@ pub(crate) fn render_impl_summary( // in documentation pages for trait with automatic implementations like "Send" and "Sync". aliases: &[String], ) { - let id = - cx.derive_id(get_id_for_impl(&i.inner_impl().for_, i.inner_impl().trait_.as_ref(), cx)); + let inner_impl = i.inner_impl(); + let id = cx.derive_id(get_id_for_impl(&inner_impl.for_, inner_impl.trait_.as_ref(), cx)); let aliases = if aliases.is_empty() { String::new() } else { @@ -1710,12 +1798,12 @@ pub(crate) fn render_impl_summary( write!(w, "<section id=\"{}\" class=\"impl has-srclink\"{}>", id, aliases); render_rightside(w, cx, &i.impl_item, containing_item, RenderMode::Normal); write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id); - write!(w, "<h3 class=\"code-header in-band\">"); + write!(w, "<h3 class=\"code-header\">"); if let Some(use_absolute) = use_absolute { - write!(w, "{}", i.inner_impl().print(use_absolute, cx)); + write!(w, "{}", inner_impl.print(use_absolute, cx)); if show_def_docs { - for it in &i.inner_impl().items { + for it in &inner_impl.items { if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind { w.write_str("<span class=\"where fmt-newline\"> "); assoc_type( @@ -1733,11 +1821,11 @@ pub(crate) fn render_impl_summary( } } } else { - write!(w, "{}", i.inner_impl().print(false, cx)); + write!(w, "{}", inner_impl.print(false, cx)); } write!(w, "</h3>"); - let is_trait = i.inner_impl().trait_.is_some(); + let is_trait = inner_impl.trait_.is_some(); if is_trait { if let Some(portability) = portability(&i.impl_item, Some(parent)) { write!(w, "<span class=\"item-info\">{}</span>", portability); @@ -1774,12 +1862,12 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { buffer.write_str("<div class=\"sidebar-elems\">"); if it.is_crate() { - write!(buffer, "<div class=\"block\"><ul>"); + write!(buffer, "<ul class=\"block\">"); if let Some(ref version) = cx.cache().crate_version { write!(buffer, "<li class=\"version\">Version {}</li>", Escape(version)); } write!(buffer, "<li><a id=\"all-types\" href=\"all.html\">All Items</a></li>"); - buffer.write_str("</ul></div>"); + buffer.write_str("</ul>"); } match *it.kind { @@ -1805,7 +1893,7 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { if !it.is_mod() { let path: String = cx.current.iter().map(|s| s.as_str()).intersperse("::").collect(); - write!(buffer, "<h2 class=\"location\"><a href=\"index.html\">In {}</a></h2>", path); + write!(buffer, "<h2><a href=\"index.html\">In {}</a></h2>", path); } // Closes sidebar-elems div. @@ -1931,6 +2019,70 @@ fn small_url_encode(s: String) -> String { } } +pub(crate) fn sidebar_render_assoc_items( + cx: &Context<'_>, + out: &mut Buffer, + id_map: &mut IdMap, + concrete: Vec<&Impl>, + synthetic: Vec<&Impl>, + blanket_impl: Vec<&Impl>, +) { + let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| { + let mut links = FxHashSet::default(); + + let mut ret = impls + .iter() + .filter_map(|it| { + let trait_ = it.inner_impl().trait_.as_ref()?; + let encoded = + id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx)); + + let i_display = format!("{:#}", trait_.print(cx)); + let out = Escape(&i_display); + let prefix = match it.inner_impl().polarity { + ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "", + ty::ImplPolarity::Negative => "!", + }; + let generated = format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out); + if links.insert(generated.clone()) { Some(generated) } else { None } + }) + .collect::<Vec<String>>(); + ret.sort(); + ret + }; + + let concrete_format = format_impls(concrete, id_map); + let synthetic_format = format_impls(synthetic, id_map); + let blanket_format = format_impls(blanket_impl, id_map); + + if !concrete_format.is_empty() { + print_sidebar_block( + out, + "trait-implementations", + "Trait Implementations", + concrete_format.iter(), + ); + } + + if !synthetic_format.is_empty() { + print_sidebar_block( + out, + "synthetic-implementations", + "Auto Trait Implementations", + synthetic_format.iter(), + ); + } + + if !blanket_format.is_empty() { + print_sidebar_block( + out, + "blanket-implementations", + "Blanket Implementations", + blanket_format.iter(), + ); + } +} + fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { let did = it.item_id.expect_def_id(); let cache = cx.cache(); @@ -1976,68 +2128,15 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { { let mut derefs = FxHashSet::default(); derefs.insert(did); - sidebar_deref_methods(cx, out, impl_, v, &mut derefs); + sidebar_deref_methods(cx, out, impl_, v, &mut derefs, &mut used_links); } - let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| { - let mut links = FxHashSet::default(); - - let mut ret = impls - .iter() - .filter_map(|it| { - let trait_ = it.inner_impl().trait_.as_ref()?; - let encoded = - id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx)); - - let i_display = format!("{:#}", trait_.print(cx)); - let out = Escape(&i_display); - let prefix = match it.inner_impl().polarity { - ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "", - ty::ImplPolarity::Negative => "!", - }; - let generated = format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out); - if links.insert(generated.clone()) { Some(generated) } else { None } - }) - .collect::<Vec<String>>(); - ret.sort(); - ret - }; - let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto()); let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) = concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket()); - let concrete_format = format_impls(concrete, &mut id_map); - let synthetic_format = format_impls(synthetic, &mut id_map); - let blanket_format = format_impls(blanket_impl, &mut id_map); - - if !concrete_format.is_empty() { - print_sidebar_block( - out, - "trait-implementations", - "Trait Implementations", - concrete_format.iter(), - ); - } - - if !synthetic_format.is_empty() { - print_sidebar_block( - out, - "synthetic-implementations", - "Auto Trait Implementations", - synthetic_format.iter(), - ); - } - - if !blanket_format.is_empty() { - print_sidebar_block( - out, - "blanket-implementations", - "Blanket Implementations", - blanket_format.iter(), - ); - } + sidebar_render_assoc_items(cx, out, &mut id_map, concrete, synthetic, blanket_impl); } } } @@ -2048,6 +2147,7 @@ fn sidebar_deref_methods( impl_: &Impl, v: &[Impl], derefs: &mut FxHashSet<DefId>, + used_links: &mut FxHashSet<String>, ) { let c = cx.cache(); @@ -2080,13 +2180,10 @@ fn sidebar_deref_methods( .and_then(|did| c.impls.get(&did)); if let Some(impls) = inner_impl { debug!("found inner_impl: {:?}", impls); - let mut used_links = FxHashSet::default(); let mut ret = impls .iter() .filter(|i| i.inner_impl().trait_.is_none()) - .flat_map(|i| { - get_methods(i.inner_impl(), true, &mut used_links, deref_mut, cx.tcx()) - }) + .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx())) .collect::<Vec<_>>(); if !ret.is_empty() { let id = if let Some(target_def_id) = real_target.def_id(c) { @@ -2115,7 +2212,14 @@ fn sidebar_deref_methods( .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait()) .unwrap_or(false) }) { - sidebar_deref_methods(cx, out, target_deref_impl, target_impls, derefs); + sidebar_deref_methods( + cx, + out, + target_deref_impl, + target_impls, + derefs, + used_links, + ); } } } @@ -2163,21 +2267,8 @@ fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String } } -/// Don't call this function directly!!! Use `print_sidebar_title` or `print_sidebar_block` instead! -fn print_sidebar_title_inner(buf: &mut Buffer, id: &str, title: &str) { - write!( - buf, - "<h3 class=\"sidebar-title\">\ - <a href=\"#{}\">{}</a>\ - </h3>", - id, title - ); -} - fn print_sidebar_title(buf: &mut Buffer, id: &str, title: &str) { - buf.push_str("<div class=\"block\">"); - print_sidebar_title_inner(buf, id, title); - buf.push_str("</div>"); + write!(buf, "<h3><a href=\"#{}\">{}</a></h3>", id, title); } fn print_sidebar_block( @@ -2186,13 +2277,12 @@ fn print_sidebar_block( title: &str, items: impl Iterator<Item = impl fmt::Display>, ) { - buf.push_str("<div class=\"block\">"); - print_sidebar_title_inner(buf, id, title); - buf.push_str("<ul>"); + print_sidebar_title(buf, id, title); + buf.push_str("<ul class=\"block\">"); for item in items { write!(buf, "<li>{}</li>", item); } - buf.push_str("</ul></div>"); + buf.push_str("</ul>"); } fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { @@ -2302,9 +2392,54 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean buf.push_str("</section>") } +/// Returns the list of implementations for the primitive reference type, filtering out any +/// implementations that are on concrete or partially generic types, only keeping implementations +/// of the form `impl<T> Trait for &T`. +pub(crate) fn get_filtered_impls_for_reference<'a>( + shared: &'a Rc<SharedContext<'_>>, + it: &clean::Item, +) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) { + let def_id = it.item_id.expect_def_id(); + // If the reference primitive is somehow not defined, exit early. + let Some(v) = shared.cache.impls.get(&def_id) else { return (Vec::new(), Vec::new(), Vec::new()) }; + // Since there is no "direct implementation" on the reference primitive type, we filter out + // every implementation which isn't a trait implementation. + let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some()); + let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = + traits.partition(|t| t.inner_impl().kind.is_auto()); + + let (blanket_impl, concrete): (Vec<&Impl>, _) = + concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket()); + // Now we keep only references over full generic types. + let concrete: Vec<_> = concrete + .into_iter() + .filter(|t| match t.inner_impl().for_ { + clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(), + _ => false, + }) + .collect(); + + (concrete, synthetic, blanket_impl) +} + fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { let mut sidebar = Buffer::new(); - sidebar_assoc_items(cx, &mut sidebar, it); + + if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { + sidebar_assoc_items(cx, &mut sidebar, it); + } else { + let shared = Rc::clone(&cx.shared); + let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it); + + sidebar_render_assoc_items( + cx, + &mut sidebar, + &mut IdMap::new(), + concrete, + synthetic, + blanket_impl, + ); + } if !sidebar.is_empty() { write!(buf, "<section>{}</section>", sidebar.into_inner()); @@ -2371,7 +2506,7 @@ fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean: } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -enum ItemSection { +pub(crate) enum ItemSection { Reexports, PrimitiveTypes, Modules, @@ -2523,11 +2658,27 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection { } } -fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { +pub(crate) fn sidebar_module_like(buf: &mut Buffer, item_sections_in_use: FxHashSet<ItemSection>) { use std::fmt::Write as _; let mut sidebar = String::new(); + for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) { + let _ = write!(sidebar, "<li><a href=\"#{}\">{}</a></li>", sec.id(), sec.name()); + } + + if !sidebar.is_empty() { + write!( + buf, + "<section>\ + <ul class=\"block\">{}</ul>\ + </section>", + sidebar + ); + } +} + +fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { let item_sections_in_use: FxHashSet<_> = items .iter() .filter(|it| { @@ -2542,21 +2693,8 @@ fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { }) .map(|it| item_ty_to_section(it.type_())) .collect(); - for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) { - let _ = write!(sidebar, "<li><a href=\"#{}\">{}</a></li>", sec.id(), sec.name()); - } - if !sidebar.is_empty() { - write!( - buf, - "<section>\ - <div class=\"block\">\ - <ul>{}</ul>\ - </div>\ - </section>", - sidebar - ); - } + sidebar_module_like(buf, item_sections_in_use); } fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { @@ -2614,8 +2752,8 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> { clean::Type::BorrowedRef { type_, .. } => { work.push_back(*type_); } - clean::Type::QPath { self_type, trait_, .. } => { - work.push_back(*self_type); + clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => { + work.push_back(self_type); process_path(trait_.def_id()); } _ => {} @@ -2668,7 +2806,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite let contents = match fs::read_to_string(&path) { Ok(contents) => contents, Err(err) => { - let span = item.span(tcx).inner(); + let span = item.span(tcx).map_or(rustc_span::DUMMY_SP, |span| span.inner()); tcx.sess .span_err(span, &format!("failed to read file {}: {}", path.display(), err)); return false; @@ -2764,11 +2902,10 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite sources::print_src( w, contents_subset, - call_data.edition, file_span, cx, &root_path, - Some(highlight::DecorationInfo(decoration_info)), + highlight::DecorationInfo(decoration_info), sources::SourceContext::Embedded { offset: line_min }, ); write!(w, "</div></div>"); |