diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2015-04-13 16:23:32 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2015-04-16 13:28:15 -0700 |
| commit | 71c1b5b704ca3955e5ca22d77a0d59b55c9af4f1 (patch) | |
| tree | b233e253ba43b1fc0077464a2bba0ebf50162639 /src/librustdoc | |
| parent | 8fb31f75c95c52cfef40c6ffe2f1452c959ca5e4 (diff) | |
| download | rust-71c1b5b704ca3955e5ca22d77a0d59b55c9af4f1.tar.gz rust-71c1b5b704ca3955e5ca22d77a0d59b55c9af4f1.zip | |
rustdoc: Inline methods inhereted through Deref
Whenever a type implements Deref, rustdoc will now add a section to the "methods available" sections for "Methods from Deref<Target=Foo>", listing all the inherent methods of the type `Foo`. Closes #19190
Diffstat (limited to 'src/librustdoc')
| -rw-r--r-- | src/librustdoc/clean/inline.rs | 49 | ||||
| -rw-r--r-- | src/librustdoc/clean/mod.rs | 84 | ||||
| -rw-r--r-- | src/librustdoc/core.rs | 9 | ||||
| -rw-r--r-- | src/librustdoc/html/render.rs | 133 | ||||
| -rw-r--r-- | src/librustdoc/html/static/main.css | 8 | ||||
| -rw-r--r-- | src/librustdoc/test.rs | 3 |
6 files changed, 220 insertions, 66 deletions
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index f14437b71b4..4fc86cf181b 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -218,15 +218,17 @@ fn build_type(cx: &DocContext, tcx: &ty::ctxt, did: ast::DefId) -> clean::ItemEn }) } -fn build_impls(cx: &DocContext, tcx: &ty::ctxt, - did: ast::DefId) -> Vec<clean::Item> { +pub fn build_impls(cx: &DocContext, tcx: &ty::ctxt, + did: ast::DefId) -> Vec<clean::Item> { ty::populate_implementations_for_type_if_necessary(tcx, did); let mut impls = Vec::new(); match tcx.inherent_impls.borrow().get(&did) { None => {} Some(i) => { - impls.extend(i.iter().map(|&did| { build_impl(cx, tcx, did) })); + for &did in i.iter() { + build_impl(cx, tcx, did, &mut impls); + } } } @@ -247,9 +249,9 @@ fn build_impls(cx: &DocContext, tcx: &ty::ctxt, fn populate_impls(cx: &DocContext, tcx: &ty::ctxt, def: decoder::DefLike, - impls: &mut Vec<Option<clean::Item>>) { + impls: &mut Vec<clean::Item>) { match def { - decoder::DlImpl(did) => impls.push(build_impl(cx, tcx, did)), + decoder::DlImpl(did) => build_impl(cx, tcx, did, impls), decoder::DlDef(def::DefMod(did)) => { csearch::each_child_of_item(&tcx.sess.cstore, did, @@ -262,14 +264,15 @@ fn build_impls(cx: &DocContext, tcx: &ty::ctxt, } } - impls.into_iter().filter_map(|a| a).collect() + return impls; } -fn build_impl(cx: &DocContext, - tcx: &ty::ctxt, - did: ast::DefId) -> Option<clean::Item> { +pub fn build_impl(cx: &DocContext, + tcx: &ty::ctxt, + did: ast::DefId, + ret: &mut Vec<clean::Item>) { if !cx.inlined.borrow_mut().as_mut().unwrap().insert(did) { - return None + return } let attrs = load_attrs(cx, tcx, did); @@ -278,13 +281,13 @@ fn build_impl(cx: &DocContext, // If this is an impl for a #[doc(hidden)] trait, be sure to not inline let trait_attrs = load_attrs(cx, tcx, t.def_id); if trait_attrs.iter().any(|a| is_doc_hidden(a)) { - return None + return } } // If this is a defaulted impl, then bail out early here if csearch::is_default_impl(&tcx.sess.cstore, did) { - return Some(clean::Item { + return ret.push(clean::Item { inner: clean::DefaultImplItem(clean::DefaultImpl { // FIXME: this should be decoded unsafety: ast::Unsafety::Normal, @@ -352,19 +355,25 @@ fn build_impl(cx: &DocContext, }) } } - }).collect(); + }).collect::<Vec<_>>(); let polarity = csearch::get_impl_polarity(tcx, did); let ty = ty::lookup_item_type(tcx, did); - return Some(clean::Item { + let trait_ = associated_trait.clean(cx).map(|bound| { + match bound { + clean::TraitBound(polyt, _) => polyt.trait_, + clean::RegionBound(..) => unreachable!(), + } + }); + if let Some(clean::ResolvedPath { did, .. }) = trait_ { + if Some(did) == cx.deref_trait_did.get() { + super::build_deref_target_impls(cx, &trait_items, ret); + } + } + ret.push(clean::Item { inner: clean::ImplItem(clean::Impl { unsafety: ast::Unsafety::Normal, // FIXME: this should be decoded derived: clean::detect_derived(&attrs), - trait_: associated_trait.clean(cx).map(|bound| { - match bound { - clean::TraitBound(polyt, _) => polyt.trait_, - clean::RegionBound(..) => unreachable!(), - } - }), + trait_: trait_, for_: ty.ty.clean(cx), generics: (&ty.generics, &predicates, subst::TypeSpace).clean(cx), items: trait_items, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 263a93a9d67..f7fbb67e08a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -128,6 +128,10 @@ impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> { fn clean(&self, cx: &DocContext) -> Crate { use rustc::session::config::Input; + if let Some(t) = cx.tcx_opt() { + cx.deref_trait_did.set(t.lang_items.deref_trait()); + } + let mut externs = Vec::new(); cx.sess().cstore.iter_crate_data(|n, meta| { externs.push((n, meta.clean(cx))); @@ -321,7 +325,7 @@ impl Item { attr::Unstable => "unstable".to_string(), attr::Stable => String::new(), }; - if s.deprecated_since.len() > 0 { + if !s.deprecated_since.is_empty() { base.push_str(" deprecated"); } base @@ -387,7 +391,7 @@ impl Clean<Item> for doctree::Module { items.extend(self.statics.iter().map(|x| x.clean(cx))); items.extend(self.constants.iter().map(|x| x.clean(cx))); items.extend(self.traits.iter().map(|x| x.clean(cx))); - items.extend(self.impls.iter().map(|x| x.clean(cx))); + items.extend(self.impls.iter().flat_map(|x| x.clean(cx).into_iter())); items.extend(self.macros.iter().map(|x| x.clean(cx))); items.extend(self.def_traits.iter().map(|x| x.clean(cx))); @@ -2186,9 +2190,21 @@ fn detect_derived<M: AttrMetaMethods>(attrs: &[M]) -> bool { attr::contains_name(attrs, "automatically_derived") } -impl Clean<Item> for doctree::Impl { - fn clean(&self, cx: &DocContext) -> Item { - Item { +impl Clean<Vec<Item>> for doctree::Impl { + fn clean(&self, cx: &DocContext) -> Vec<Item> { + let mut ret = Vec::new(); + let trait_ = self.trait_.clean(cx); + let items = self.items.clean(cx); + + // If this impl block is an implementation of the Deref trait, then we + // need to try inlining the target's inherent impl blocks as well. + if let Some(ResolvedPath { did, .. }) = trait_ { + if Some(did) == cx.deref_trait_did.get() { + build_deref_target_impls(cx, &items, &mut ret); + } + } + + ret.push(Item { name: None, attrs: self.attrs.clean(cx), source: self.whence.clean(cx), @@ -2198,12 +2214,66 @@ impl Clean<Item> for doctree::Impl { inner: ImplItem(Impl { unsafety: self.unsafety, generics: self.generics.clean(cx), - trait_: self.trait_.clean(cx), + trait_: trait_, for_: self.for_.clean(cx), - items: self.items.clean(cx), + items: items, derived: detect_derived(&self.attrs), polarity: Some(self.polarity.clean(cx)), }), + }); + return ret; + } +} + +fn build_deref_target_impls(cx: &DocContext, + items: &[Item], + ret: &mut Vec<Item>) { + let tcx = match cx.tcx_opt() { + Some(t) => t, + None => return, + }; + + for item in items { + let target = match item.inner { + TypedefItem(ref t) => &t.type_, + _ => continue, + }; + let primitive = match *target { + ResolvedPath { did, .. } if ast_util::is_local(did) => continue, + ResolvedPath { did, .. } => { + ret.extend(inline::build_impls(cx, tcx, did)); + continue + } + _ => match target.primitive_type() { + Some(prim) => prim, + None => continue, + } + }; + let did = match primitive { + Isize => tcx.lang_items.isize_impl(), + I8 => tcx.lang_items.i8_impl(), + I16 => tcx.lang_items.i16_impl(), + I32 => tcx.lang_items.i32_impl(), + I64 => tcx.lang_items.i64_impl(), + Usize => tcx.lang_items.usize_impl(), + U8 => tcx.lang_items.u8_impl(), + U16 => tcx.lang_items.u16_impl(), + U32 => tcx.lang_items.u32_impl(), + U64 => tcx.lang_items.u64_impl(), + F32 => tcx.lang_items.f32_impl(), + F64 => tcx.lang_items.f64_impl(), + Char => tcx.lang_items.char_impl(), + Bool => None, + Str => tcx.lang_items.str_impl(), + Slice => tcx.lang_items.slice_impl(), + Array => tcx.lang_items.slice_impl(), + PrimitiveTuple => None, + PrimitiveRawPointer => tcx.lang_items.const_ptr_impl(), + }; + if let Some(did) = did { + if !ast_util::is_local(did) { + inline::build_impl(cx, tcx, did, ret); + } } } } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index a637ba9f297..1b74123c4ad 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -20,7 +20,7 @@ use rustc_resolve as resolve; use syntax::{ast, ast_map, codemap, diagnostic}; -use std::cell::RefCell; +use std::cell::{RefCell, Cell}; use std::collections::{HashMap, HashSet}; use visit_ast::RustdocVisitor; @@ -48,6 +48,7 @@ pub struct DocContext<'tcx> { pub external_typarams: RefCell<Option<HashMap<ast::DefId, String>>>, pub inlined: RefCell<Option<HashSet<ast::DefId>>>, pub populated_crate_impls: RefCell<HashSet<ast::CrateNum>>, + pub deref_trait_did: Cell<Option<ast::DefId>>, } impl<'tcx> DocContext<'tcx> { @@ -77,6 +78,7 @@ pub struct CrateAnalysis { pub external_paths: ExternalPaths, pub external_typarams: RefCell<Option<HashMap<ast::DefId, String>>>, pub inlined: RefCell<Option<HashSet<ast::DefId>>>, + pub deref_trait_did: Option<ast::DefId>, } pub type Externs = HashMap<String, Vec<String>>; @@ -147,15 +149,17 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs, external_paths: RefCell::new(Some(HashMap::new())), inlined: RefCell::new(Some(HashSet::new())), populated_crate_impls: RefCell::new(HashSet::new()), + deref_trait_did: Cell::new(None), }; debug!("crate: {:?}", ctxt.krate); - let analysis = CrateAnalysis { + let mut analysis = CrateAnalysis { exported_items: exported_items, public_items: public_items, external_paths: RefCell::new(None), external_typarams: RefCell::new(None), inlined: RefCell::new(None), + deref_trait_did: None, }; let krate = { @@ -170,5 +174,6 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs, *analysis.external_typarams.borrow_mut() = map; let map = ctxt.inlined.borrow_mut().take(); *analysis.inlined.borrow_mut() = map; + analysis.deref_trait_did = ctxt.deref_trait_did.get(); (krate, analysis) } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index e3df0fc9ba6..f87a86eb3a6 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -209,6 +209,7 @@ pub struct Cache { privmod: bool, remove_priv: bool, public_items: NodeSet, + deref_trait_did: Option<ast::DefId>, // In rare case where a structure is defined in one module but implemented // in another, if the implementing module is parsed before defining module, @@ -396,6 +397,7 @@ pub fn run(mut krate: clean::Crate, public_items: public_items, orphan_methods: Vec::new(), traits: mem::replace(&mut krate.external_traits, HashMap::new()), + deref_trait_did: analysis.as_ref().and_then(|a| a.deref_trait_did), typarams: analysis.as_ref().map(|a| { a.external_typarams.borrow_mut().take().unwrap() }).unwrap_or(HashMap::new()), @@ -403,8 +405,6 @@ pub fn run(mut krate: clean::Crate, a.inlined.borrow_mut().take().unwrap() }).unwrap_or(HashSet::new()), }; - cache.stack.push(krate.name.clone()); - krate = cache.fold_crate(krate); // Cache where all our extern crates are located for &(n, ref e) in &krate.externs { @@ -427,6 +427,9 @@ pub fn run(mut krate: clean::Crate, cache.primitive_locations.insert(prim, ast::LOCAL_CRATE); } + cache.stack.push(krate.name.clone()); + krate = cache.fold_crate(krate); + // Build our search index let index = try!(build_index(&krate, &mut cache)); @@ -1069,8 +1072,11 @@ impl DocFolder for Cache { } ref t => { - t.primitive_type().map(|p| { - ast_util::local_def(p.to_node_id()) + t.primitive_type().and_then(|t| { + self.primitive_locations.get(&t).map(|n| { + let id = t.to_node_id(); + ast::DefId { krate: *n, node: id } + }) }) } }; @@ -1684,12 +1690,12 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, fn short_stability(item: &clean::Item, show_reason: bool) -> Option<String> { item.stability.as_ref().and_then(|stab| { - let reason = if show_reason && stab.reason.len() > 0 { + let reason = if show_reason && !stab.reason.is_empty() { format!(": {}", stab.reason) } else { String::new() }; - let text = if stab.deprecated_since.len() > 0 { + let text = if !stab.deprecated_since.is_empty() { let since = if show_reason { format!(" since {}", Escape(&stab.deprecated_since)) } else { @@ -1865,7 +1871,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, } // If there are methods directly on this trait object, render them here. - try!(render_methods(w, it)); + try!(render_methods(w, it.def_id, MethodRender::All)); let cache = cache(); try!(write!(w, " @@ -1995,7 +2001,7 @@ fn item_struct(w: &mut fmt::Formatter, it: &clean::Item, try!(write!(w, "</table>")); } } - render_methods(w, it) + render_methods(w, it.def_id, MethodRender::All) } fn item_enum(w: &mut fmt::Formatter, it: &clean::Item, @@ -2094,7 +2100,7 @@ fn item_enum(w: &mut fmt::Formatter, it: &clean::Item, try!(write!(w, "</table>")); } - try!(render_methods(w, it)); + try!(render_methods(w, it.def_id, MethodRender::All)); Ok(()) } @@ -2183,27 +2189,61 @@ enum MethodLink { GotoSource(ast::DefId), } -fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result { - let v = match cache().impls.get(&it.def_id) { - Some(v) => v.clone(), +enum MethodRender<'a> { + All, + DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type }, +} + +fn render_methods(w: &mut fmt::Formatter, + it: ast::DefId, + what: MethodRender) -> fmt::Result { + let c = cache(); + let v = match c.impls.get(&it) { + Some(v) => v, None => return Ok(()), }; - let (non_trait, traits): (Vec<_>, _) = v.into_iter() - .partition(|i| i.impl_.trait_.is_none()); + let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| { + i.impl_.trait_.is_none() + }); if !non_trait.is_empty() { - try!(write!(w, "<h2 id='methods'>Methods</h2>")); + let render_header = match what { + MethodRender::All => { + try!(write!(w, "<h2 id='methods'>Methods</h2>")); + true + } + MethodRender::DerefFor { trait_, type_ } => { + try!(write!(w, "<h2 id='deref-methods'>Methods from \ + {}<Target={}></h2>", trait_, type_)); + false + } + }; for i in &non_trait { - try!(render_impl(w, i, MethodLink::Anchor)); + try!(render_impl(w, i, MethodLink::Anchor, render_header)); } } + if let MethodRender::DerefFor { .. } = what { + return Ok(()) + } if !traits.is_empty() { + let deref_impl = traits.iter().find(|t| { + match *t.impl_.trait_.as_ref().unwrap() { + clean::ResolvedPath { did, .. } => { + Some(did) == c.deref_trait_did + } + _ => false + } + }); + if let Some(impl_) = deref_impl { + try!(render_deref_methods(w, impl_)); + } try!(write!(w, "<h2 id='implementations'>Trait \ Implementations</h2>")); - let (derived, manual): (Vec<_>, _) = traits.into_iter() - .partition(|i| i.impl_.derived); + let (derived, manual): (Vec<_>, _) = traits.iter().partition(|i| { + i.impl_.derived + }); for i in &manual { let did = i.trait_did().unwrap(); - try!(render_impl(w, i, MethodLink::GotoSource(did))); + try!(render_impl(w, i, MethodLink::GotoSource(did), true)); } if !derived.is_empty() { try!(write!(w, "<h3 id='derived_implementations'>\ @@ -2211,27 +2251,52 @@ fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result { </h3>")); for i in &derived { let did = i.trait_did().unwrap(); - try!(render_impl(w, i, MethodLink::GotoSource(did))); + try!(render_impl(w, i, MethodLink::GotoSource(did), true)); } } } Ok(()) } -fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink) - -> fmt::Result { - try!(write!(w, "<h3 class='impl'><code>impl{} ", - i.impl_.generics)); - if let Some(clean::ImplPolarity::Negative) = i.impl_.polarity { - try!(write!(w, "!")); - } - if let Some(ref ty) = i.impl_.trait_ { - try!(write!(w, "{} for ", *ty)); +fn render_deref_methods(w: &mut fmt::Formatter, impl_: &Impl) -> fmt::Result { + let deref_type = impl_.impl_.trait_.as_ref().unwrap(); + let target = impl_.impl_.items.iter().filter_map(|item| { + match item.inner { + clean::TypedefItem(ref t) => Some(&t.type_), + _ => None, + } + }).next().unwrap(); + let what = MethodRender::DerefFor { trait_: deref_type, type_: target }; + match *target { + clean::ResolvedPath { did, .. } => render_methods(w, did, what), + _ => { + if let Some(prim) = target.primitive_type() { + if let Some(c) = cache().primitive_locations.get(&prim) { + let did = ast::DefId { krate: *c, node: prim.to_node_id() }; + try!(render_methods(w, did, what)); + } + } + Ok(()) + } } - try!(write!(w, "{}{}</code></h3>", i.impl_.for_, - WhereClause(&i.impl_.generics))); - if let Some(ref dox) = i.dox { - try!(write!(w, "<div class='docblock'>{}</div>", Markdown(dox))); +} + +fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink, + render_header: bool) -> fmt::Result { + if render_header { + try!(write!(w, "<h3 class='impl'><code>impl{} ", + i.impl_.generics)); + if let Some(clean::ImplPolarity::Negative) = i.impl_.polarity { + try!(write!(w, "!")); + } + if let Some(ref ty) = i.impl_.trait_ { + try!(write!(w, "{} for ", *ty)); + } + try!(write!(w, "{}{}</code></h3>", i.impl_.for_, + WhereClause(&i.impl_.generics))); + if let Some(ref dox) = i.dox { + try!(write!(w, "<div class='docblock'>{}</div>", Markdown(dox))); + } } fn doctraititem(w: &mut fmt::Formatter, item: &clean::Item, @@ -2393,7 +2458,7 @@ fn item_primitive(w: &mut fmt::Formatter, it: &clean::Item, _p: &clean::PrimitiveType) -> fmt::Result { try!(document(w, it)); - render_methods(w, it) + render_methods(w, it.def_id, MethodRender::All) } fn get_basic_keywords() -> &'static str { diff --git a/src/librustdoc/html/static/main.css b/src/librustdoc/html/static/main.css index 657b2c8fef8..c94dbc15103 100644 --- a/src/librustdoc/html/static/main.css +++ b/src/librustdoc/html/static/main.css @@ -481,9 +481,12 @@ em.stab.unstable { background: #FFF5D6; border-color: #FFC600; } em.stab.deprecated { background: #F3DFFF; border-color: #7F0087; } em.stab { display: inline-block; - border-width: 2px; + border-width: 1px; border-style: solid; - padding: 5px; + padding: 3px; + margin-bottom: 5px; + font-size: 90%; + font-style: normal; } em.stab p { display: inline; @@ -492,6 +495,7 @@ em.stab p { .module-item .stab { border-width: 0; padding: 0; + margin: 0; background: inherit !important; } diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 449f9c79d1d..bbe0a6f2675 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::cell::RefCell; +use std::cell::{RefCell, Cell}; use std::collections::{HashSet, HashMap}; use std::dynamic_lib::DynamicLibrary; use std::env; @@ -92,6 +92,7 @@ pub fn run(input: &str, external_typarams: RefCell::new(None), inlined: RefCell::new(None), populated_crate_impls: RefCell::new(HashSet::new()), + deref_trait_did: Cell::new(None), }; let mut v = RustdocVisitor::new(&ctx, None); |
