Generate documentation for auto-trait impls
A new section is added to both both struct and trait doc pages.
On struct/enum pages, a new 'Auto Trait Implementations' section displays any
synthetic implementations for auto traits. Currently, this is only done
for Send and Sync.
On trait pages, a new 'Auto Implementors' section displays all types
which automatically implement the trait. Effectively, this is a list of
all public types in the standard library.
Synthesized impls for a particular auto trait ('synthetic impls') take
into account generic bounds. For example, a type 'struct Foo<T>(T)' will
have 'impl<T> Send for Foo<T> where T: Send' generated for it.
Manual implementations of auto traits are also taken into account. If we have
the following types:
'struct Foo<T>(T)'
'struct Wrapper<T>(Foo<T>)'
'unsafe impl<T> Send for Wrapper<T>' // pretend that Wrapper<T> makes
this sound somehow
Then Wrapper will have the following impl generated:
'impl<T> Send for Wrapper<T>'
reflecting the fact that 'T: Send' need not hold for 'Wrapper<T>: Send'
to hold
Lifetimes, HRTBS, and projections (e.g. '<T as Iterator>::Item') are
taken into account by synthetic impls
However, if a type can *never* implement a particular auto trait
(e.g. 'struct MyStruct<T>(*const T)'), then a negative impl will be
generated (in this case, 'impl<T> !Send for MyStruct<T>')
All of this means that a user should be able to copy-paste a synthetic
impl into their code, without any observable changes in behavior
(assuming the rest of the program remains unchanged).
This commit is contained in:
@@ -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 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>")?;
|
||||
}
|
||||
}
|
||||
writeln!(w, "</code></li>")?;
|
||||
for implementor in concrete {
|
||||
render_implementor(cx, implementor, w, &implementor_dups)?;
|
||||
}
|
||||
write!(w, "</ul>")?;
|
||||
|
||||
if t.auto {
|
||||
write!(w, "{}", synthetic_impl_header)?;
|
||||
for implementor in synthetic {
|
||||
render_implementor(cx, implementor, w, &implementor_dups)?;
|
||||
}
|
||||
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, .. } => {
|
||||
|
||||
Reference in New Issue
Block a user