Skip to content

Commit

Permalink
rustdoc: Inline methods inhereted through Deref
Browse files Browse the repository at this point in the history
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
  • Loading branch information
alexcrichton committed Apr 16, 2015
1 parent 8fb31f7 commit 71c1b5b
Show file tree
Hide file tree
Showing 10 changed files with 333 additions and 66 deletions.
49 changes: 29 additions & 20 deletions src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}

Expand All @@ -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,
Expand All @@ -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);
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
84 changes: 77 additions & 7 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)));

Expand Down Expand Up @@ -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),
Expand All @@ -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);
}
}
}
}
Expand Down
9 changes: 7 additions & 2 deletions src/librustdoc/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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>>;
Expand Down Expand Up @@ -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 = {
Expand All @@ -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)
}
Loading

0 comments on commit 71c1b5b

Please sign in to comment.