summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/html/render
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/librustdoc/html/render/context.rs102
-rw-r--r--src/librustdoc/html/render/mod.rs609
-rw-r--r--src/librustdoc/html/render/print_item.rs141
-rw-r--r--src/librustdoc/html/render/search_index.rs13
-rw-r--r--src/librustdoc/html/render/span_map.rs34
-rw-r--r--src/librustdoc/html/render/write_shared.rs85
6 files changed, 615 insertions, 369 deletions
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 2ed7a6f1b..5733d1f9c 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -17,8 +17,8 @@ use super::print_item::{full_path, item_path, print_item};
use super::search_index::build_index;
use super::write_shared::write_shared;
use super::{
- collect_spans_and_sources, print_sidebar, scrape_examples_help, AllTypes, LinkFromSrc, NameDoc,
- StylePath, BASIC_KEYWORDS,
+ collect_spans_and_sources, print_sidebar, scrape_examples_help, sidebar_module_like, AllTypes,
+ LinkFromSrc, NameDoc, StylePath, BASIC_KEYWORDS,
};
use crate::clean::{self, types::ExternalLocation, ExternalCrate};
@@ -31,6 +31,7 @@ use crate::formats::FormatRenderer;
use crate::html::escape::Escape;
use crate::html::format::{join_with_double_colon, Buffer};
use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
+use crate::html::url_parts_builder::UrlPartsBuilder;
use crate::html::{layout, sources};
use crate::scrape_examples::AllCallLocations;
use crate::try_err;
@@ -71,7 +72,7 @@ pub(crate) struct Context<'tcx> {
}
// `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(not(windows), target_arch = "x86_64", target_pointer_width = "64"))]
rustc_data_structures::static_assert_size!(Context<'_>, 128);
/// Shared mutable state used in [`Context`] and elsewhere.
@@ -301,13 +302,10 @@ impl<'tcx> Context<'tcx> {
/// may happen, for example, with externally inlined items where the source
/// of their crate documentation isn't known.
pub(super) fn src_href(&self, item: &clean::Item) -> Option<String> {
- self.href_from_span(item.span(self.tcx()), true)
+ self.href_from_span(item.span(self.tcx())?, true)
}
pub(crate) fn href_from_span(&self, span: clean::Span, with_lines: bool) -> Option<String> {
- if span.is_dummy() {
- return None;
- }
let mut root = self.root_path();
let mut path = String::new();
let cnum = span.cnum(self.sess());
@@ -373,6 +371,35 @@ impl<'tcx> Context<'tcx> {
anchor = anchor
))
}
+
+ pub(crate) fn href_from_span_relative(
+ &self,
+ span: clean::Span,
+ relative_to: &str,
+ ) -> Option<String> {
+ self.href_from_span(span, false).map(|s| {
+ let mut url = UrlPartsBuilder::new();
+ let mut dest_href_parts = s.split('/');
+ let mut cur_href_parts = relative_to.split('/');
+ for (cur_href_part, dest_href_part) in (&mut cur_href_parts).zip(&mut dest_href_parts) {
+ if cur_href_part != dest_href_part {
+ url.push(dest_href_part);
+ break;
+ }
+ }
+ for dest_href_part in dest_href_parts {
+ url.push(dest_href_part);
+ }
+ let loline = span.lo(self.sess()).line;
+ let hiline = span.hi(self.sess()).line;
+ format!(
+ "{}{}#{}",
+ "../".repeat(cur_href_parts.count()),
+ url.finish(),
+ if loline == hiline { loline.to_string() } else { format!("{loline}-{hiline}") }
+ )
+ })
+ }
}
/// Generates the documentation for `crate` into the directory `dst`
@@ -403,7 +430,6 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
extension_css,
resource_suffix,
static_root_path,
- unstable_features,
generate_redirect_map,
show_type_layout,
generate_link_to_definition,
@@ -484,7 +510,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
resource_suffix,
static_root_path,
fs: DocFS::new(sender),
- codes: ErrorCodes::from(unstable_features.is_nightly_build()),
+ codes: ErrorCodes::from(options.unstable_features.is_nightly_build()),
playground,
all: RefCell::new(AllTypes::new()),
errors: receiver,
@@ -554,6 +580,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
let crate_name = self.tcx().crate_name(LOCAL_CRATE);
let final_file = self.dst.join(crate_name.as_str()).join("all.html");
let settings_file = self.dst.join("settings.html");
+ let help_file = self.dst.join("help.html");
let scrape_examples_help_file = self.dst.join("scrape-examples-help.html");
let mut root_path = self.dst.to_str().expect("invalid path").to_owned();
@@ -570,16 +597,24 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
keywords: BASIC_KEYWORDS,
resource_suffix: &shared.resource_suffix,
};
- let sidebar = if shared.cache.crate_version.is_some() {
- format!("<h2 class=\"location\">Crate {}</h2>", crate_name)
- } else {
- String::new()
- };
let all = shared.all.replace(AllTypes::new());
+ let mut sidebar = Buffer::html();
+ if shared.cache.crate_version.is_some() {
+ write!(sidebar, "<h2 class=\"location\">Crate {}</h2>", crate_name)
+ };
+
+ let mut items = Buffer::html();
+ sidebar_module_like(&mut items, all.item_sections());
+ if !items.is_empty() {
+ sidebar.push_str("<div class=\"sidebar-elems\">");
+ sidebar.push_buffer(items);
+ sidebar.push_str("</div>");
+ }
+
let v = layout::render(
&shared.layout,
&page,
- sidebar,
+ sidebar.into_inner(),
|buf: &mut Buffer| all.print(buf),
&shared.style_files,
);
@@ -599,9 +634,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
write!(
buf,
"<div class=\"main-heading\">\
- <h1 class=\"fqn\">\
- <span class=\"in-band\">Rustdoc settings</span>\
- </h1>\
+ <h1 class=\"fqn\">Rustdoc settings</h1>\
<span class=\"out-of-band\">\
<a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\
Back\
@@ -624,6 +657,39 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
);
shared.fs.write(settings_file, v)?;
+ // Generating help page.
+ page.title = "Rustdoc help";
+ page.description = "Documentation for Rustdoc";
+ page.root_path = "./";
+
+ let sidebar = "<h2 class=\"location\">Help</h2><div class=\"sidebar-elems\"></div>";
+ let v = layout::render(
+ &shared.layout,
+ &page,
+ sidebar,
+ |buf: &mut Buffer| {
+ write!(
+ buf,
+ "<div class=\"main-heading\">\
+ <h1 class=\"fqn\">Rustdoc help</h1>\
+ <span class=\"out-of-band\">\
+ <a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\
+ Back\
+ </a>\
+ </span>\
+ </div>\
+ <noscript>\
+ <section>\
+ <p>You need to enable Javascript to use keyboard commands or search.</p>\
+ <p>For more information, browse the <a href=\"https://doc.rust-lang.org/rustdoc/\">rustdoc handbook</a>.</p>\
+ </section>\
+ </noscript>",
+ )
+ },
+ &shared.style_files,
+ );
+ shared.fs.write(help_file, v)?;
+
if shared.layout.scrape_examples_extension {
page.title = "About scraped examples";
page.description = "How the scraped examples feature works in Rustdoc";
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_}&lt;Target = {type_}&gt;</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_}&lt;Target = {type_}&gt;</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>");
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 99cf42919..632781736 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -16,10 +16,10 @@ use std::fmt;
use std::rc::Rc;
use super::{
- collect_paths_for_type, document, ensure_trailing_slash, item_ty_to_section,
- notable_traits_decl, render_assoc_item, render_assoc_items, render_attributes_in_code,
- render_attributes_in_pre, render_impl, render_stability_since_raw, write_srclink,
- AssocItemLink, Context, ImplRenderingParameters,
+ collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference,
+ item_ty_to_section, notable_traits_decl, render_all_impls, render_assoc_item,
+ render_assoc_items, render_attributes_in_code, render_attributes_in_pre, render_impl,
+ render_rightside, render_stability_since_raw, AssocItemLink, Context, ImplRenderingParameters,
};
use crate::clean;
use crate::config::ModuleSorting;
@@ -371,16 +371,21 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
}
clean::ImportKind::Glob => String::new(),
};
+ let stab_tags = stab_tags.unwrap_or_default();
+ let (stab_tags_before, stab_tags_after) = if stab_tags.is_empty() {
+ ("", "")
+ } else {
+ ("<div class=\"item-right docblock-short\">", "</div>")
+ };
write!(
w,
"<div class=\"item-left {stab}{add}import-item\"{id}>\
<code>{vis}{imp}</code>\
</div>\
- <div class=\"item-right docblock-short\">{stab_tags}</div>",
+ {stab_tags_before}{stab_tags}{stab_tags_after}",
stab = stab.unwrap_or_default(),
vis = myitem.visibility.print_with_space(myitem.item_id, cx),
imp = import.print(cx),
- stab_tags = stab_tags.unwrap_or_default(),
);
w.write_str(ITEM_TABLE_ROW_CLOSE);
}
@@ -395,7 +400,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
if myitem.fn_header(cx.tcx()).unwrap().unsafety
== hir::Unsafety::Unsafe =>
{
- "<a title=\"unsafe function\" href=\"#\"><sup>โš </sup></a>"
+ "<sup title=\"unsafe function\">โš </sup>"
}
_ => "",
};
@@ -412,6 +417,12 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
let doc_value = myitem.doc_value().unwrap_or_default();
w.write_str(ITEM_TABLE_ROW_OPEN);
+ let docs = MarkdownSummaryLine(&doc_value, &myitem.links(cx)).into_string();
+ let (docs_before, docs_after) = if docs.is_empty() {
+ ("", "")
+ } else {
+ ("<div class=\"item-right docblock-short\">", "</div>")
+ };
write!(
w,
"<div class=\"item-left {stab}{add}module-item\">\
@@ -420,11 +431,10 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
{unsafety_flag}\
{stab_tags}\
</div>\
- <div class=\"item-right docblock-short\">{docs}</div>",
+ {docs_before}{docs}{docs_after}",
name = myitem.name.unwrap(),
visibility_emoji = visibility_emoji,
stab_tags = extra_info_tags(myitem, item, cx.tcx()),
- docs = MarkdownSummaryLine(&doc_value, &myitem.links(cx)).into_string(),
class = myitem.type_(),
add = add,
stab = stab.unwrap_or_default(),
@@ -477,7 +487,7 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) ->
(cfg, _) => cfg.as_deref().cloned(),
};
- debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.cfg, cfg);
+ debug!("Portability name={:?} {:?} - {:?} = {:?}", item.name, item.cfg, parent.cfg, cfg);
if let Some(ref cfg) = cfg {
tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html());
}
@@ -504,7 +514,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
+ name.as_str().len()
+ generics_len;
- wrap_into_docblock(w, |w| {
+ wrap_into_item_decl(w, |w| {
wrap_item(w, "fn", |w| {
render_attributes_in_pre(w, it, "");
w.reserve(header_len);
@@ -520,7 +530,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
name = name,
generics = f.generics.print(cx),
where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline),
- decl = f.decl.full_print(header_len, 0, header.asyncness, cx),
+ decl = f.decl.full_print(header_len, 0, cx),
notable_traits = notable_traits_decl(&f.decl, cx),
);
});
@@ -543,7 +553,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
cx.tcx().trait_def(t.def_id).must_implement_one_of.clone();
// Output the trait definition
- wrap_into_docblock(w, |w| {
+ wrap_into_item_decl(w, |w| {
wrap_item(w, "trait", |w| {
render_attributes_in_pre(w, it, "");
write!(
@@ -706,17 +716,10 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
document(&mut content, cx, m, Some(t), HeadingOffset::H5);
let toggled = !content.is_empty();
if toggled {
- write!(w, "<details class=\"rustdoc-toggle\" open><summary>");
- }
- write!(w, "<div id=\"{}\" class=\"method has-srclink\">", id);
- write!(w, "<div class=\"rightside\">");
-
- let has_stability = render_stability_since(w, m, t, cx.tcx());
- if has_stability {
- w.write_str(" ยท ");
+ write!(w, "<details class=\"rustdoc-toggle method-toggle\" open><summary>");
}
- write_srclink(cx, m, w);
- write!(w, "</div>");
+ write!(w, "<section id=\"{}\" class=\"method has-srclink\">", id);
+ render_rightside(w, cx, m, t, RenderMode::Normal);
write!(w, "<h4 class=\"code-header\">");
render_assoc_item(
w,
@@ -727,7 +730,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
RenderMode::Normal,
);
w.write_str("</h4>");
- w.write_str("</div>");
+ w.write_str("</section>");
if toggled {
write!(w, "</summary>");
w.push_buffer(content);
@@ -887,7 +890,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
w,
"implementors",
"Implementors",
- "<div class=\"item-list\" id=\"implementors-list\">",
+ "<div id=\"implementors-list\">",
);
for implementor in concrete {
render_implementor(cx, implementor, it, w, &implementor_dups, &[]);
@@ -899,7 +902,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
w,
"synthetic-implementors",
"Auto implementors",
- "<div class=\"item-list\" id=\"synthetic-implementors-list\">",
+ "<div id=\"synthetic-implementors-list\">",
);
for implementor in synthetic {
render_implementor(
@@ -920,7 +923,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
w,
"implementors",
"Implementors",
- "<div class=\"item-list\" id=\"implementors-list\"></div>",
+ "<div id=\"implementors-list\"></div>",
);
if t.is_auto(cx.tcx()) {
@@ -928,7 +931,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
w,
"synthetic-implementors",
"Auto implementors",
- "<div class=\"item-list\" id=\"synthetic-implementors-list\"></div>",
+ "<div id=\"synthetic-implementors-list\"></div>",
);
}
}
@@ -994,7 +997,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// So C's HTML will have something like this:
//
// ```html
- // <script type="text/javascript" src="/implementors/A/trait.Foo.js"
+ // <script src="/implementors/A/trait.Foo.js"
// data-ignore-extern-crates="A,B" async></script>
// ```
//
@@ -1020,15 +1023,17 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
.map(|cnum| cx.shared.tcx.crate_name(cnum).to_string())
.collect::<Vec<_>>()
.join(",");
+ let (extern_before, extern_after) =
+ if extern_crates.is_empty() { ("", "") } else { (" data-ignore-extern-crates=\"", "\"") };
write!(
w,
- "<script type=\"text/javascript\" src=\"{src}\" data-ignore-extern-crates=\"{extern_crates}\" async></script>",
+ "<script src=\"{src}\"{extern_before}{extern_crates}{extern_after} async></script>",
src = js_src_path.finish(),
);
}
fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TraitAlias) {
- wrap_into_docblock(w, |w| {
+ wrap_into_item_decl(w, |w| {
wrap_item(w, "trait-alias", |w| {
render_attributes_in_pre(w, it, "");
write!(
@@ -1052,7 +1057,7 @@ fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &
}
fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) {
- wrap_into_docblock(w, |w| {
+ wrap_into_item_decl(w, |w| {
wrap_item(w, "opaque", |w| {
render_attributes_in_pre(w, it, "");
write!(
@@ -1091,7 +1096,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea
});
}
- wrap_into_docblock(w, |w| write_content(w, cx, it, t));
+ wrap_into_item_decl(w, |w| write_content(w, cx, it, t));
document(w, cx, it, None, HeadingOffset::H2);
@@ -1105,7 +1110,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea
}
fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) {
- wrap_into_docblock(w, |w| {
+ wrap_into_item_decl(w, |w| {
wrap_item(w, "union", |w| {
render_attributes_in_pre(w, it, "");
render_union(w, it, Some(&s.generics), &s.fields, "", cx);
@@ -1169,7 +1174,7 @@ fn print_tuple_struct_fields(w: &mut Buffer, cx: &Context<'_>, s: &[clean::Item]
fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) {
let count_variants = e.variants().count();
- wrap_into_docblock(w, |w| {
+ wrap_into_item_decl(w, |w| {
wrap_item(w, "enum", |w| {
render_attributes_in_pre(w, it, "");
write!(
@@ -1198,7 +1203,8 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
let name = v.name.unwrap();
match *v.kind {
clean::VariantItem(ref var) => match var {
- clean::Variant::CLike => write!(w, "{}", name),
+ // FIXME(#101337): Show discriminant
+ clean::Variant::CLike(..) => write!(w, "{}", name),
clean::Variant::Tuple(ref s) => {
write!(w, "{}(", name);
print_tuple_struct_fields(w, cx, s);
@@ -1260,7 +1266,13 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
w.write_str(")");
}
w.write_str("</code>");
- render_stability_since(w, variant, it, cx.tcx());
+ render_stability_since_raw(
+ w,
+ variant.stable_since(cx.tcx()),
+ variant.const_stability(cx.tcx()),
+ it.stable_since(cx.tcx()),
+ it.const_stable_since(cx.tcx()),
+ );
w.write_str("</h3>");
use crate::clean::Variant;
@@ -1321,24 +1333,14 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
}
fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) {
- wrap_into_docblock(w, |w| {
- highlight::render_with_highlighting(
- &t.source,
- w,
- Some("macro"),
- None,
- None,
- it.span(cx.tcx()).inner().edition(),
- None,
- None,
- None,
- );
+ wrap_into_item_decl(w, |w| {
+ highlight::render_macro_with_highlighting(&t.source, w);
});
document(w, cx, it, None, HeadingOffset::H2)
}
fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &clean::ProcMacro) {
- wrap_into_docblock(w, |w| {
+ wrap_into_item_decl(w, |w| {
let name = it.name.expect("proc-macros always have names");
match m.kind {
MacroKind::Bang => {
@@ -1370,12 +1372,22 @@ fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &c
}
fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
+ let def_id = it.item_id.expect_def_id();
document(w, cx, it, None, HeadingOffset::H2);
- render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
+ if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
+ render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
+ } else {
+ // We handle the "reference" primitive type on its own because we only want to list
+ // implementations on generic types.
+ let shared = Rc::clone(&cx.shared);
+ let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it);
+
+ render_all_impls(w, cx, it, &concrete, &synthetic, &blanket_impl);
+ }
}
fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) {
- wrap_into_docblock(w, |w| {
+ wrap_into_item_decl(w, |w| {
wrap_item(w, "const", |w| {
render_attributes_in_code(w, it);
@@ -1424,7 +1436,7 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle
}
fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) {
- wrap_into_docblock(w, |w| {
+ wrap_into_item_decl(w, |w| {
wrap_item(w, "struct", |w| {
render_attributes_in_code(w, it);
render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx);
@@ -1477,7 +1489,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
}
fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) {
- wrap_into_docblock(w, |w| {
+ wrap_into_item_decl(w, |w| {
wrap_item(w, "static", |w| {
render_attributes_in_code(w, it);
write!(
@@ -1494,7 +1506,7 @@ fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
}
fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
- wrap_into_docblock(w, |w| {
+ wrap_into_item_decl(w, |w| {
wrap_item(w, "foreigntype", |w| {
w.write_str("extern {\n");
render_attributes_in_code(w, it);
@@ -1583,11 +1595,11 @@ fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>)
bounds
}
-fn wrap_into_docblock<F>(w: &mut Buffer, f: F)
+fn wrap_into_item_decl<F>(w: &mut Buffer, f: F)
where
F: FnOnce(&mut Buffer),
{
- w.write_str("<div class=\"docblock item-decl\">");
+ w.write_str("<div class=\"item-decl\">");
f(w);
w.write_str("</div>")
}
@@ -1601,21 +1613,6 @@ where
w.write_str("</code></pre>");
}
-fn render_stability_since(
- w: &mut Buffer,
- item: &clean::Item,
- containing_item: &clean::Item,
- tcx: TyCtxt<'_>,
-) -> bool {
- render_stability_since_raw(
- w,
- item.stable_since(tcx),
- item.const_stability(tcx),
- containing_item.stable_since(tcx),
- containing_item.const_stable_since(tcx),
- )
-}
-
fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cx: &Context<'_>) -> Ordering {
let lhss = format!("{}", lhs.inner_impl().print(false, cx));
let rhss = format!("{}", rhs.inner_impl().print(false, cx));
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index d672f0bb5..8eb9c07f8 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -544,10 +544,15 @@ fn get_fn_inputs_and_outputs<'tcx>(
(true, _) => (Some(impl_self), &func.generics),
(_, true) => (Some(impl_self), impl_generics),
(false, false) => {
- let mut params = func.generics.params.clone();
- params.extend(impl_generics.params.clone());
- let mut where_predicates = func.generics.where_predicates.clone();
- where_predicates.extend(impl_generics.where_predicates.clone());
+ let params =
+ func.generics.params.iter().chain(&impl_generics.params).cloned().collect();
+ let where_predicates = func
+ .generics
+ .where_predicates
+ .iter()
+ .chain(&impl_generics.where_predicates)
+ .cloned()
+ .collect();
combined_generics = clean::Generics { params, where_predicates };
(Some(impl_self), &combined_generics)
}
diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs
index 34d590fb2..151ec2b28 100644
--- a/src/librustdoc/html/render/span_map.rs
+++ b/src/librustdoc/html/render/span_map.rs
@@ -166,25 +166,23 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
if let ExprKind::MethodCall(segment, ..) = expr.kind {
- if let Some(hir_id) = segment.hir_id {
- let hir = self.tcx.hir();
- let body_id = hir.enclosing_body_owner(hir_id);
- // FIXME: this is showing error messages for parts of the code that are not
- // compiled (because of cfg)!
- //
- // See discussion in https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352
- let typeck_results = self.tcx.typeck_body(
- hir.maybe_body_owned_by(body_id).expect("a body which isn't a body"),
+ let hir = self.tcx.hir();
+ let body_id = hir.enclosing_body_owner(segment.hir_id);
+ // FIXME: this is showing error messages for parts of the code that are not
+ // compiled (because of cfg)!
+ //
+ // See discussion in https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352
+ let typeck_results = self
+ .tcx
+ .typeck_body(hir.maybe_body_owned_by(body_id).expect("a body which isn't a body"));
+ if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) {
+ self.matches.insert(
+ segment.ident.span,
+ match hir.span_if_local(def_id) {
+ Some(span) => LinkFromSrc::Local(clean::Span::new(span)),
+ None => LinkFromSrc::External(def_id),
+ },
);
- if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) {
- self.matches.insert(
- segment.ident.span,
- match hir.span_if_local(def_id) {
- Some(span) => LinkFromSrc::Local(clean::Span::new(span)),
- None => LinkFromSrc::External(def_id),
- },
- );
- }
}
} else if self.handle_macro(expr.span) {
// We don't want to go deeper into the macro.
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 6fb41ff32..85f63c985 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -1,5 +1,4 @@
use std::ffi::OsStr;
-use std::fmt::Write;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{self, BufReader};
@@ -10,7 +9,8 @@ use std::sync::LazyLock as Lazy;
use itertools::Itertools;
use rustc_data_structures::flock;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use serde::Serialize;
+use serde::ser::SerializeSeq;
+use serde::{Serialize, Serializer};
use super::{collect_paths_for_type, ensure_trailing_slash, Context, BASIC_KEYWORDS};
use crate::clean::Crate;
@@ -284,25 +284,43 @@ pub(super) fn write_shared(
cx.write_shared(SharedResource::Unversioned { name }, contents, &options.emit)?;
}
- fn collect(path: &Path, krate: &str, key: &str) -> io::Result<(Vec<String>, Vec<String>)> {
+ /// Read a file and return all lines that match the `"{crate}":{data},` format,
+ /// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`.
+ ///
+ /// This forms the payload of files that look like this:
+ ///
+ /// ```javascript
+ /// var data = {
+ /// "{crate1}":{data},
+ /// "{crate2}":{data}
+ /// };
+ /// use_data(data);
+ /// ```
+ ///
+ /// The file needs to be formatted so that *only crate data lines start with `"`*.
+ fn collect(path: &Path, krate: &str) -> io::Result<(Vec<String>, Vec<String>)> {
let mut ret = Vec::new();
let mut krates = Vec::new();
if path.exists() {
- let prefix = format!(r#"{}["{}"]"#, key, krate);
+ let prefix = format!("\"{}\"", krate);
for line in BufReader::new(File::open(path)?).lines() {
let line = line?;
- if !line.starts_with(key) {
+ if !line.starts_with('"') {
continue;
}
if line.starts_with(&prefix) {
continue;
}
- ret.push(line.to_string());
+ if line.ends_with(',') {
+ ret.push(line[..line.len() - 1].to_string());
+ } else {
+ // No comma (it's the case for the last added crate line)
+ ret.push(line.to_string());
+ }
krates.push(
- line[key.len() + 2..]
- .split('"')
- .next()
+ line.split('"')
+ .find(|s| !s.is_empty())
.map(|s| s.to_owned())
.unwrap_or_else(String::new),
);
@@ -311,6 +329,20 @@ pub(super) fn write_shared(
Ok((ret, krates))
}
+ /// Read a file and return all lines that match the <code>"{crate}":{data},\</code> format,
+ /// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`.
+ ///
+ /// This forms the payload of files that look like this:
+ ///
+ /// ```javascript
+ /// var data = JSON.parse('{\
+ /// "{crate1}":{data},\
+ /// "{crate2}":{data}\
+ /// }');
+ /// use_data(data);
+ /// ```
+ ///
+ /// The file needs to be formatted so that *only crate data lines start with `"`*.
fn collect_json(path: &Path, krate: &str) -> io::Result<(Vec<String>, Vec<String>)> {
let mut ret = Vec::new();
let mut krates = Vec::new();
@@ -485,14 +517,12 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
};
let content = format!(
- "<h1 class=\"fqn\">\
- <span class=\"in-band\">List of all crates</span>\
- </h1><ul class=\"crate mod\">{}</ul>",
+ "<h1 class=\"fqn\">List of all crates</h1><ul class=\"all-items\">{}</ul>",
krates
.iter()
.map(|s| {
format!(
- "<li><a class=\"crate mod\" href=\"{}index.html\">{}</a></li>",
+ "<li><a href=\"{}index.html\">{}</a></li>",
ensure_trailing_slash(s),
s
)
@@ -526,13 +556,27 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
},
};
- #[derive(Serialize)]
struct Implementor {
text: String,
synthetic: bool,
types: Vec<String>,
}
+ impl Serialize for Implementor {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let mut seq = serializer.serialize_seq(None)?;
+ seq.serialize_element(&self.text)?;
+ if self.synthetic {
+ seq.serialize_element(&1)?;
+ seq.serialize_element(&self.types)?;
+ }
+ seq.end()
+ }
+ }
+
let implementors = imps
.iter()
.filter_map(|imp| {
@@ -563,9 +607,9 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
}
let implementors = format!(
- r#"implementors["{}"] = {};"#,
+ r#""{}":{}"#,
krate.name(cx.tcx()),
- serde_json::to_string(&implementors).unwrap()
+ serde_json::to_string(&implementors).expect("failed serde conversion"),
);
let mut mydst = dst.clone();
@@ -576,16 +620,15 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
mydst.push(&format!("{}.{}.js", remote_item_type, remote_path[remote_path.len() - 1]));
let (mut all_implementors, _) =
- try_err!(collect(&mydst, krate.name(cx.tcx()).as_str(), "implementors"), &mydst);
+ try_err!(collect(&mydst, krate.name(cx.tcx()).as_str()), &mydst);
all_implementors.push(implementors);
// Sort the implementors by crate so the file will be generated
// identically even with rustdoc running in parallel.
all_implementors.sort();
- let mut v = String::from("(function() {var implementors = {};\n");
- for implementor in &all_implementors {
- writeln!(v, "{}", *implementor).unwrap();
- }
+ let mut v = String::from("(function() {var implementors = {\n");
+ v.push_str(&all_implementors.join(",\n"));
+ v.push_str("\n};");
v.push_str(
"if (window.register_implementors) {\
window.register_implementors(implementors);\