diff options
Diffstat (limited to 'src/librustdoc/html/render.rs')
| -rw-r--r-- | src/librustdoc/html/render.rs | 295 |
1 files changed, 235 insertions, 60 deletions
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index d6025920e78..6e265ed0776 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -37,7 +37,7 @@ pub use self::ExternalLocation::*; use std::borrow::Cow; use std::cell::RefCell; use std::cmp::Ordering; -use std::collections::{BTreeMap, HashSet}; +use std::collections::{BTreeMap, HashSet, VecDeque}; use std::default::Default; use std::error; use std::fmt::{self, Display, Formatter, Write as FmtWrite}; @@ -169,6 +169,7 @@ pub enum ExternalLocation { Unknown, } + /// Metadata about implementations for a type or trait. #[derive(Clone)] pub struct Impl { @@ -270,6 +271,18 @@ pub struct Cache { /// generating explicit hyperlinks to other crates. pub external_paths: FxHashMap<DefId, (Vec<String>, ItemType)>, + /// Maps local def ids of exported types to fully qualified paths. + /// Unlike 'paths', this mapping ignores any renames that occur + /// due to 'use' statements. + /// + /// This map is used when writing out the special 'implementors' + /// javascript file. By using the exact path that the type + /// is declared with, we ensure that each path will be identical + /// to the path used if the corresponding type is inlined. By + /// doing this, we can detect duplicate impls on a trait page, and only display + /// the impl for the inlined type. + pub exact_paths: FxHashMap<DefId, Vec<String>>, + /// This map contains information about all known traits of this crate. /// Implementations of a crate should inherit the documentation of the /// parent trait if no extra documentation is specified, and default methods @@ -322,6 +335,7 @@ pub struct RenderInfo { pub inlined: FxHashSet<DefId>, pub external_paths: ::core::ExternalPaths, pub external_typarams: FxHashMap<DefId, String>, + pub exact_paths: FxHashMap<DefId, Vec<String>>, pub deref_trait_did: Option<DefId>, pub deref_mut_trait_did: Option<DefId>, pub owned_box_did: Option<DefId>, @@ -436,7 +450,9 @@ fn init_ids() -> FxHashMap<String, usize> { "required-methods", "provided-methods", "implementors", + "synthetic-implementors", "implementors-list", + "synthetic-implementors-list", "methods", "deref-methods", "implementations", @@ -507,6 +523,7 @@ pub fn run(mut krate: clean::Crate, themes, }; + // If user passed in `--playground-url` arg, we fill in crate name here if let Some(url) = playground_url { markdown::PLAYGROUND.with(|slot| { @@ -556,6 +573,7 @@ pub fn run(mut krate: clean::Crate, inlined: _, external_paths, external_typarams, + exact_paths, deref_trait_did, deref_mut_trait_did, owned_box_did, @@ -568,6 +586,7 @@ pub fn run(mut krate: clean::Crate, let mut cache = Cache { impls: FxHashMap(), external_paths, + exact_paths, paths: FxHashMap(), implementors: FxHashMap(), stack: Vec::new(), @@ -873,7 +892,10 @@ themePicker.onclick = function() {{ // should add it. if !imp.impl_item.def_id.is_local() { continue } have_impls = true; - write!(implementors, "{},", as_json(&imp.inner_impl().to_string())).unwrap(); + write!(implementors, "{{text:{},synthetic:{},types:{}}},", + as_json(&imp.inner_impl().to_string()), + imp.inner_impl().synthetic, + as_json(&collect_paths_for_type(imp.inner_impl().for_.clone()))).unwrap(); } implementors.push_str("];"); @@ -1856,8 +1878,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item, items: &[clean::Item]) -> fmt::Result { document(w, cx, item)?; - let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()) - .collect::<Vec<usize>>(); + let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::<Vec<usize>>(); // the order of item types in the listing fn reorder(ty: ItemType) -> u8 { @@ -2201,6 +2222,50 @@ fn item_function(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, document(w, cx, it) } +fn render_implementor(cx: &Context, implementor: &Impl, w: &mut fmt::Formatter, + implementor_dups: &FxHashMap<&str, (DefId, bool)>) -> Result<(), fmt::Error> { + write!(w, "<li>")?; + if let Some(l) = (Item { cx, item: &implementor.impl_item }).src_href() { + write!(w, "<div class='out-of-band'>")?; + write!(w, "<a class='srclink' href='{}' title='{}'>[src]</a>", + l, "goto source code")?; + write!(w, "</div>")?; + } + write!(w, "<code>")?; + // If there's already another implementor that has the same abbridged name, use the + // full path, for example in `std::iter::ExactSizeIterator` + let use_absolute = match implementor.inner_impl().for_ { + clean::ResolvedPath { ref path, is_generic: false, .. } | + clean::BorrowedRef { + type_: box clean::ResolvedPath { ref path, is_generic: false, .. }, + .. + } => implementor_dups[path.last_name()].1, + _ => false, + }; + fmt_impl_for_trait_page(&implementor.inner_impl(), w, use_absolute)?; + for it in &implementor.inner_impl().items { + if let clean::TypedefItem(ref tydef, _) = it.inner { + write!(w, "<span class=\"where fmt-newline\"> ")?; + assoc_type(w, it, &vec![], Some(&tydef.type_), AssocItemLink::Anchor(None))?; + write!(w, ";</span>")?; + } + } + writeln!(w, "</code></li>")?; + Ok(()) +} + +fn render_impls(cx: &Context, w: &mut fmt::Formatter, + traits: Vec<&&Impl>, + containing_item: &clean::Item) -> Result<(), fmt::Error> { + for i in &traits { + let did = i.trait_did().unwrap(); + let assoc_link = AssocItemLink::GotoSource(did, &i.inner_impl().provided_trait_methods); + render_impl(w, cx, i, assoc_link, + RenderMode::Normal, containing_item.stable_since(), true)?; + } + Ok(()) +} + fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, t: &clean::Trait) -> fmt::Result { let mut bounds = String::new(); @@ -2380,6 +2445,14 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, </h2> <ul class='item-list' id='implementors-list'> "; + + let synthetic_impl_header = " + <h2 id='synthetic-implementors' class='small-section-header'> + Auto implementors<a href='#synthetic-implementors' class='anchor'></a> + </h2> + <ul class='item-list' id='synthetic-implementors-list'> + "; + if let Some(implementors) = cache.implementors.get(&it.def_id) { // The DefId is for the first Type found with that name. The bool is // if any Types with the same name but different DefId have been found. @@ -2405,6 +2478,11 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, .partition::<Vec<_>, _>(|i| i.inner_impl().for_.def_id() .map_or(true, |d| cache.paths.contains_key(&d))); + + let (synthetic, concrete) = local.iter() + .partition::<Vec<_>, _>(|i| i.inner_impl().synthetic); + + if !foreign.is_empty() { write!(w, " <h2 id='foreign-impls' class='small-section-header'> @@ -2422,42 +2500,48 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, } write!(w, "{}", impl_header)?; + for implementor in concrete { + render_implementor(cx, implementor, w, &implementor_dups)?; + } + write!(w, "</ul>")?; - for implementor in local { - write!(w, "<li>")?; - if let Some(l) = (Item { cx, item: &implementor.impl_item }).src_href() { - write!(w, "<div class='out-of-band'>")?; - write!(w, "<a class='srclink' href='{}' title='{}'>[src]</a>", - l, "goto source code")?; - write!(w, "</div>")?; - } - write!(w, "<code>")?; - // If there's already another implementor that has the same abbridged name, use the - // full path, for example in `std::iter::ExactSizeIterator` - let use_absolute = match implementor.inner_impl().for_ { - clean::ResolvedPath { ref path, is_generic: false, .. } | - clean::BorrowedRef { - type_: box clean::ResolvedPath { ref path, is_generic: false, .. }, - .. - } => implementor_dups[path.last_name()].1, - _ => false, - }; - fmt_impl_for_trait_page(&implementor.inner_impl(), w, use_absolute)?; - for it in &implementor.inner_impl().items { - if let clean::TypedefItem(ref tydef, _) = it.inner { - write!(w, "<span class=\"where fmt-newline\"> ")?; - assoc_type(w, it, &vec![], Some(&tydef.type_), AssocItemLink::Anchor(None))?; - write!(w, ";</span>")?; - } + if t.auto { + write!(w, "{}", synthetic_impl_header)?; + for implementor in synthetic { + render_implementor(cx, implementor, w, &implementor_dups)?; } - writeln!(w, "</code></li>")?; + write!(w, "</ul>")?; } + + write!(w, r#"<script type="text/javascript"> + window.inlined_types=new Set();"#)?; + + write!(w, r#"<script type="text/javascript" async + src="{root_path}/implementors/{path}/{ty}.{name}.js"> + </script>"#, + root_path = vec![".."; cx.current.len()].join("/"), + path = if it.def_id.is_local() { + cx.current.join("/") + } else { + let (ref path, _) = cache.external_paths[&it.def_id]; + path[..path.len() - 1].join("/") + }, + ty = it.type_().css_class(), + name = *it.name.as_ref().unwrap())?; } else { // even without any implementations to write in, we still want the heading and list, so the // implementors javascript file pulled in below has somewhere to write the impls into write!(w, "{}", impl_header)?; + write!(w, "</ul>")?; + + write!(w, r#"<script type="text/javascript">window.inlined_types=new Set();</script>"#)?; + + if t.auto { + write!(w, "{}", synthetic_impl_header)?; + write!(w, "</ul>")?; + } } - write!(w, "</ul>")?; + write!(w, r#"<script type="text/javascript" async src="{root_path}/implementors/{path}/{ty}.{name}.js"> </script>"#, @@ -3075,17 +3159,28 @@ fn render_assoc_items(w: &mut fmt::Formatter, }).is_some(); render_deref_methods(w, cx, impl_, containing_item, has_deref_mut)?; } + + let (synthetic, concrete) = traits + .iter() + .partition::<Vec<_>, _>(|t| t.inner_impl().synthetic); + write!(w, " <h2 id='implementations' class='small-section-header'> Trait Implementations<a href='#implementations' class='anchor'></a> </h2> + <div id='implementations-list'> ")?; - for i in &traits { - let did = i.trait_did().unwrap(); - let assoc_link = AssocItemLink::GotoSource(did, &i.inner_impl().provided_trait_methods); - render_impl(w, cx, i, assoc_link, - RenderMode::Normal, containing_item.stable_since(), true)?; - } + render_impls(cx, w, concrete, containing_item)?; + write!(w, "</div>")?; + + write!(w, " + <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)?; + write!(w, "</div>")?; } Ok(()) } @@ -3586,32 +3681,48 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { } } } - let mut links = HashSet::new(); - let ret = v.iter() - .filter_map(|i| { - let is_negative_impl = is_negative_impl(i.inner_impl()); - if let Some(ref i) = i.inner_impl().trait_ { - let i_display = format!("{:#}", i); - let out = Escape(&i_display); - let encoded = small_url_encode(&format!("{:#}", i)); - let generated = format!("<a href=\"#impl-{}\">{}{}</a>", - encoded, - if is_negative_impl { "!" } else { "" }, - out); - if !links.contains(&generated) && links.insert(generated.clone()) { - Some(generated) + let format_impls = |impls: Vec<&Impl>| { + let mut links = HashSet::new(); + impls.iter() + .filter_map(|i| { + let is_negative_impl = is_negative_impl(i.inner_impl()); + if let Some(ref i) = i.inner_impl().trait_ { + let i_display = format!("{:#}", i); + let out = Escape(&i_display); + let encoded = small_url_encode(&format!("{:#}", i)); + let generated = format!("<a href=\"#impl-{}\">{}{}</a>", + encoded, + if is_negative_impl { "!" } else { "" }, + out); + if links.insert(generated.clone()) { + Some(generated) + } else { + None + } } else { None } - } else { - None - } - }) - .collect::<String>(); - if !ret.is_empty() { + }) + .collect::<String>() + }; + + let (synthetic, concrete) = v + .iter() + .partition::<Vec<_>, _>(|i| i.inner_impl().synthetic); + + let concrete_format = format_impls(concrete); + let synthetic_format = format_impls(synthetic); + + if !concrete_format.is_empty() { out.push_str("<a class=\"sidebar-title\" href=\"#implementations\">\ Trait Implementations</a>"); - out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", ret)); + out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", concrete_format)); + } + + if !synthetic_format.is_empty() { + out.push_str("<a class=\"sidebar-title\" href=\"#synthetic-implementations\">\ + Auto Trait Implementations</a>"); + out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", synthetic_format)); } } } @@ -3734,7 +3845,7 @@ fn sidebar_trait(fmt: &mut fmt::Formatter, it: &clean::Item, if let Some(implementors) = c.implementors.get(&it.def_id) { let res = implementors.iter() .filter(|i| i.inner_impl().for_.def_id() - .map_or(false, |d| !c.paths.contains_key(&d))) + .map_or(false, |d| !c.paths.contains_key(&d))) .filter_map(|i| { match extract_for_impl_name(&i.impl_item) { Some((ref name, ref url)) => { @@ -3755,6 +3866,10 @@ fn sidebar_trait(fmt: &mut fmt::Formatter, it: &clean::Item, } sidebar.push_str("<a class=\"sidebar-title\" href=\"#implementors\">Implementors</a>"); + if t.auto { + sidebar.push_str("<a class=\"sidebar-title\" \ + href=\"#synthetic-implementors\">Auto Implementors</a>"); + } sidebar.push_str(&sidebar_assoc_items(it)); @@ -3969,6 +4084,66 @@ fn get_index_type(clean_type: &clean::Type) -> Type { t } +/// Returns a list of all paths used in the type. +/// This is used to help deduplicate imported impls +/// for reexported types. If any of the contained +/// types are re-exported, we don't use the corresponding +/// entry from the js file, as inlining will have already +/// picked up the impl +fn collect_paths_for_type(first_ty: clean::Type) -> Vec<String> { + let mut out = Vec::new(); + let mut visited = FxHashSet(); + let mut work = VecDeque::new(); + let cache = cache(); + + work.push_back(first_ty); + + while let Some(ty) = work.pop_front() { + if !visited.insert(ty.clone()) { + continue; + } + + match ty { + clean::Type::ResolvedPath { did, .. } => { + let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone()); + let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern); + + match fqp { + Some(path) => { + out.push(path.join("::")); + }, + _ => {} + }; + + }, + clean::Type::Tuple(tys) => { + work.extend(tys.into_iter()); + }, + clean::Type::Slice(ty) => { + work.push_back(*ty); + } + clean::Type::Array(ty, _) => { + work.push_back(*ty); + }, + clean::Type::Unique(ty) => { + work.push_back(*ty); + }, + clean::Type::RawPointer(_, ty) => { + work.push_back(*ty); + }, + clean::Type::BorrowedRef { type_, .. } => { + work.push_back(*type_); + }, + clean::Type::QPath { self_type, trait_, .. } => { + work.push_back(*self_type); + work.push_back(*trait_); + }, + _ => {} + } + }; + out +} + fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<String> { match *clean_type { clean::ResolvedPath { ref path, .. } => { |
