rustdoc: Fill in external trait methods

This commit alters rustdoc to crawl the metadata of upstream libraries in order
to fill in default methods for traits implemented in downstream crates. This,
for example, documents the `insert` function on hash maps.

This is a fairly lossy extraction from the metadata. Documentation and
attributes are lost, but they aren't used anyway. Unfortunately, argument names
are also lost because they are not present in the metadata. Source links are
also lost because the spans are not serialized.

While not perfect, it appears that presenting this documentation through rustdoc
is much better than nothing, so I wanted to land this to allow iteration on it
later on.
This commit is contained in:
Alex Crichton 2014-05-03 02:08:58 -07:00
parent 9411cec580
commit c746c503f0
7 changed files with 433 additions and 104 deletions

View File

@ -25,6 +25,7 @@ use rustc::driver::driver;
use rustc::metadata::cstore; use rustc::metadata::cstore;
use rustc::metadata::csearch; use rustc::metadata::csearch;
use rustc::metadata::decoder; use rustc::metadata::decoder;
use rustc::middle::ty;
use std::strbuf::StrBuf; use std::strbuf::StrBuf;
@ -128,7 +129,7 @@ pub struct Item {
pub attrs: Vec<Attribute> , pub attrs: Vec<Attribute> ,
pub inner: ItemEnum, pub inner: ItemEnum,
pub visibility: Option<Visibility>, pub visibility: Option<Visibility>,
pub id: ast::NodeId, pub def_id: ast::DefId,
} }
impl Item { impl Item {
@ -274,7 +275,7 @@ impl Clean<Item> for doctree::Module {
attrs: self.attrs.clean(), attrs: self.attrs.clean(),
source: where.clean(), source: where.clean(),
visibility: self.vis.clean(), visibility: self.vis.clean(),
id: self.id, def_id: ast_util::local_def(self.id),
inner: ModuleItem(Module { inner: ModuleItem(Module {
is_crate: self.is_crate, is_crate: self.is_crate,
items: items.iter() items: items.iter()
@ -339,7 +340,7 @@ impl<'a> attr::AttrMetaMethods for &'a Attribute {
#[deriving(Clone, Encodable, Decodable)] #[deriving(Clone, Encodable, Decodable)]
pub struct TyParam { pub struct TyParam {
pub name: StrBuf, pub name: StrBuf,
pub id: ast::NodeId, pub did: ast::DefId,
pub bounds: Vec<TyParamBound>, pub bounds: Vec<TyParamBound>,
} }
@ -347,12 +348,25 @@ impl Clean<TyParam> for ast::TyParam {
fn clean(&self) -> TyParam { fn clean(&self) -> TyParam {
TyParam { TyParam {
name: self.ident.clean(), name: self.ident.clean(),
id: self.id, did: ast::DefId { krate: ast::LOCAL_CRATE, node: self.id },
bounds: self.bounds.clean().move_iter().collect(), bounds: self.bounds.clean().move_iter().collect(),
} }
} }
} }
impl Clean<TyParam> for ty::TypeParameterDef {
fn clean(&self) -> TyParam {
let cx = super::ctxtkey.get().unwrap();
cx.external_typarams.borrow_mut().get_mut_ref().insert(self.def_id,
self.ident.clean());
TyParam {
name: self.ident.clean(),
did: self.def_id,
bounds: self.bounds.clean(),
}
}
}
#[deriving(Clone, Encodable, Decodable)] #[deriving(Clone, Encodable, Decodable)]
pub enum TyParamBound { pub enum TyParamBound {
RegionBound, RegionBound,
@ -369,6 +383,96 @@ impl Clean<TyParamBound> for ast::TyParamBound {
} }
} }
fn external_path(name: &str) -> Path {
Path {
global: false,
segments: vec![PathSegment {
name: name.to_strbuf(),
lifetimes: Vec::new(),
types: Vec::new(),
}]
}
}
impl Clean<TyParamBound> for ty::BuiltinBound {
fn clean(&self) -> TyParamBound {
let cx = super::ctxtkey.get().unwrap();
let tcx = match cx.maybe_typed {
core::Typed(ref tcx) => tcx,
core::NotTyped(_) => return RegionBound,
};
let (did, path) = match *self {
ty::BoundStatic => return RegionBound,
ty::BoundSend =>
(tcx.lang_items.send_trait().unwrap(), external_path("Send")),
ty::BoundSized =>
(tcx.lang_items.sized_trait().unwrap(), external_path("Sized")),
ty::BoundCopy =>
(tcx.lang_items.copy_trait().unwrap(), external_path("Copy")),
ty::BoundShare =>
(tcx.lang_items.share_trait().unwrap(), external_path("Share")),
};
let fqn = csearch::get_item_path(tcx, did);
let fqn = fqn.move_iter().map(|i| i.to_str().to_strbuf()).collect();
cx.external_paths.borrow_mut().get_mut_ref().insert(did,
(fqn, TypeTrait));
TraitBound(ResolvedPath {
path: path,
typarams: None,
did: did,
})
}
}
impl Clean<TyParamBound> for ty::TraitRef {
fn clean(&self) -> TyParamBound {
let cx = super::ctxtkey.get().unwrap();
let tcx = match cx.maybe_typed {
core::Typed(ref tcx) => tcx,
core::NotTyped(_) => return RegionBound,
};
let fqn = csearch::get_item_path(tcx, self.def_id);
let fqn = fqn.move_iter().map(|i| i.to_str().to_strbuf())
.collect::<Vec<StrBuf>>();
let path = external_path(fqn.last().unwrap().as_slice());
cx.external_paths.borrow_mut().get_mut_ref().insert(self.def_id,
(fqn, TypeTrait));
TraitBound(ResolvedPath {
path: path,
typarams: None,
did: self.def_id,
})
}
}
impl Clean<Vec<TyParamBound>> for ty::ParamBounds {
fn clean(&self) -> Vec<TyParamBound> {
let mut v = Vec::new();
for b in self.builtin_bounds.iter() {
if b != ty::BoundSized {
v.push(b.clean());
}
}
for t in self.trait_bounds.iter() {
v.push(t.clean());
}
return v;
}
}
impl Clean<Option<Vec<TyParamBound>>> for ty::substs {
fn clean(&self) -> Option<Vec<TyParamBound>> {
let mut v = Vec::new();
match self.regions {
ty::NonerasedRegions(..) => v.push(RegionBound),
ty::ErasedRegions => {}
}
v.extend(self.tps.iter().map(|t| TraitBound(t.clean())));
if v.len() > 0 {Some(v)} else {None}
}
}
#[deriving(Clone, Encodable, Decodable)] #[deriving(Clone, Encodable, Decodable)]
pub struct Lifetime(StrBuf); pub struct Lifetime(StrBuf);
@ -386,6 +490,29 @@ impl Clean<Lifetime> for ast::Lifetime {
} }
} }
impl Clean<Lifetime> for ty::RegionParameterDef {
fn clean(&self) -> Lifetime {
Lifetime(token::get_name(self.name).get().to_strbuf())
}
}
impl Clean<Option<Lifetime>> for ty::Region {
fn clean(&self) -> Option<Lifetime> {
match *self {
ty::ReStatic => Some(Lifetime("static".to_strbuf())),
ty::ReLateBound(_, ty::BrNamed(_, name)) =>
Some(Lifetime(token::get_name(name).get().to_strbuf())),
ty::ReLateBound(..) |
ty::ReEarlyBound(..) |
ty::ReFree(..) |
ty::ReScope(..) |
ty::ReInfer(..) |
ty::ReEmpty(..) => None
}
}
}
// maybe use a Generic enum and use ~[Generic]? // maybe use a Generic enum and use ~[Generic]?
#[deriving(Clone, Encodable, Decodable)] #[deriving(Clone, Encodable, Decodable)]
pub struct Generics { pub struct Generics {
@ -402,6 +529,15 @@ impl Clean<Generics> for ast::Generics {
} }
} }
impl Clean<Generics> for ty::Generics {
fn clean(&self) -> Generics {
Generics {
lifetimes: self.region_param_defs.clean(),
type_params: self.type_param_defs.clean(),
}
}
}
#[deriving(Clone, Encodable, Decodable)] #[deriving(Clone, Encodable, Decodable)]
pub struct Method { pub struct Method {
pub generics: Generics, pub generics: Generics,
@ -428,11 +564,11 @@ impl Clean<Item> for ast::Method {
name: Some(self.ident.clean()), name: Some(self.ident.clean()),
attrs: self.attrs.clean().move_iter().collect(), attrs: self.attrs.clean().move_iter().collect(),
source: self.span.clean(), source: self.span.clean(),
id: self.id.clone(), def_id: ast_util::local_def(self.id.clone()),
visibility: self.vis.clean(), visibility: self.vis.clean(),
inner: MethodItem(Method { inner: MethodItem(Method {
generics: self.generics.clean(), generics: self.generics.clean(),
self_: self.explicit_self.clean(), self_: self.explicit_self.node.clean(),
fn_style: self.fn_style.clone(), fn_style: self.fn_style.clone(),
decl: decl, decl: decl,
}), }),
@ -466,12 +602,12 @@ impl Clean<Item> for ast::TypeMethod {
name: Some(self.ident.clean()), name: Some(self.ident.clean()),
attrs: self.attrs.clean().move_iter().collect(), attrs: self.attrs.clean().move_iter().collect(),
source: self.span.clean(), source: self.span.clean(),
id: self.id, def_id: ast_util::local_def(self.id),
visibility: None, visibility: None,
inner: TyMethodItem(TyMethod { inner: TyMethodItem(TyMethod {
fn_style: self.fn_style.clone(), fn_style: self.fn_style.clone(),
decl: decl, decl: decl,
self_: self.explicit_self.clean(), self_: self.explicit_self.node.clean(),
generics: self.generics.clean(), generics: self.generics.clean(),
}), }),
} }
@ -486,9 +622,9 @@ pub enum SelfTy {
SelfOwned, SelfOwned,
} }
impl Clean<SelfTy> for ast::ExplicitSelf { impl Clean<SelfTy> for ast::ExplicitSelf_ {
fn clean(&self) -> SelfTy { fn clean(&self) -> SelfTy {
match self.node { match *self {
ast::SelfStatic => SelfStatic, ast::SelfStatic => SelfStatic,
ast::SelfValue => SelfValue, ast::SelfValue => SelfValue,
ast::SelfUniq => SelfOwned, ast::SelfUniq => SelfOwned,
@ -511,7 +647,7 @@ impl Clean<Item> for doctree::Function {
attrs: self.attrs.clean(), attrs: self.attrs.clean(),
source: self.where.clean(), source: self.where.clean(),
visibility: self.vis.clean(), visibility: self.vis.clean(),
id: self.id, def_id: ast_util::local_def(self.id),
inner: FunctionItem(Function { inner: FunctionItem(Function {
decl: self.decl.clean(), decl: self.decl.clean(),
generics: self.generics.clean(), generics: self.generics.clean(),
@ -533,7 +669,7 @@ pub struct ClosureDecl {
impl Clean<ClosureDecl> for ast::ClosureTy { impl Clean<ClosureDecl> for ast::ClosureTy {
fn clean(&self) -> ClosureDecl { fn clean(&self) -> ClosureDecl {
ClosureDecl { ClosureDecl {
lifetimes: self.lifetimes.clean().move_iter().collect(), lifetimes: self.lifetimes.clean(),
decl: self.decl.clean(), decl: self.decl.clean(),
onceness: self.onceness, onceness: self.onceness,
fn_style: self.fn_style, fn_style: self.fn_style,
@ -571,6 +707,25 @@ impl Clean<FnDecl> for ast::FnDecl {
} }
} }
impl Clean<FnDecl> for ty::FnSig {
fn clean(&self) -> FnDecl {
FnDecl {
output: self.output.clean(),
cf: Return,
attrs: Vec::new(), // FIXME: this is likely wrong
inputs: Arguments {
values: self.inputs.iter().map(|t| {
Argument {
type_: t.clean(),
id: 0,
name: "".to_strbuf(), // FIXME: where are the names?
}
}).collect(),
},
}
}
}
#[deriving(Clone, Encodable, Decodable)] #[deriving(Clone, Encodable, Decodable)]
pub struct Argument { pub struct Argument {
pub type_: Type, pub type_: Type,
@ -616,7 +771,7 @@ impl Clean<Item> for doctree::Trait {
name: Some(self.name.clean()), name: Some(self.name.clean()),
attrs: self.attrs.clean(), attrs: self.attrs.clean(),
source: self.where.clean(), source: self.where.clean(),
id: self.id, def_id: ast_util::local_def(self.id),
visibility: self.vis.clean(), visibility: self.vis.clean(),
inner: TraitItem(Trait { inner: TraitItem(Trait {
methods: self.methods.clean(), methods: self.methods.clean(),
@ -669,6 +824,58 @@ impl Clean<TraitMethod> for ast::TraitMethod {
} }
} }
impl Clean<TraitMethod> for ty::Method {
fn clean(&self) -> TraitMethod {
let m = if self.provided_source.is_some() {Provided} else {Required};
let cx = super::ctxtkey.get().unwrap();
let tcx = match cx.maybe_typed {
core::Typed(ref tcx) => tcx,
core::NotTyped(_) => fail!(),
};
let mut attrs = Vec::new();
csearch::get_item_attrs(&tcx.sess.cstore, self.def_id, |v| {
attrs.extend(v.move_iter().map(|i| i.clean()));
});
let (self_, sig) = match self.explicit_self {
ast::SelfStatic => (ast::SelfStatic.clean(), self.fty.sig.clone()),
s => {
let sig = ty::FnSig {
inputs: Vec::from_slice(self.fty.sig.inputs.slice_from(1)),
..self.fty.sig.clone()
};
let s = match s {
ast::SelfRegion(..) => {
match ty::get(*self.fty.sig.inputs.get(0)).sty {
ty::ty_rptr(r, mt) => {
SelfBorrowed(r.clean(), mt.mutbl.clean())
}
_ => s.clean(),
}
}
s => s.clean(),
};
(s, sig)
}
};
m(Item {
name: Some(self.ident.clean()),
visibility: Some(ast::Inherited),
def_id: self.def_id,
attrs: attrs,
source: Span {
filename: "".to_strbuf(),
loline: 0, locol: 0, hiline: 0, hicol: 0,
},
inner: TyMethodItem(TyMethod {
fn_style: self.fty.fn_style,
generics: self.generics.clean(),
self_: self_,
decl: sig.clean(),
})
})
}
}
/// A representation of a Type suitable for hyperlinking purposes. Ideally one can get the original /// A representation of a Type suitable for hyperlinking purposes. Ideally one can get the original
/// type out of the AST/ty::ctxt given one of these, if more information is needed. Most importantly /// type out of the AST/ty::ctxt given one of these, if more information is needed. Most importantly
/// it does not preserve mutability or boxes. /// it does not preserve mutability or boxes.
@ -684,9 +891,9 @@ pub enum Type {
TyParamBinder(ast::NodeId), TyParamBinder(ast::NodeId),
/// For parameterized types, so the consumer of the JSON don't go looking /// For parameterized types, so the consumer of the JSON don't go looking
/// for types which don't exist anywhere. /// for types which don't exist anywhere.
Generic(ast::NodeId), Generic(ast::DefId),
/// For references to self /// For references to self
Self(ast::NodeId), Self(ast::DefId),
/// Primitives are just the fixed-size numeric types (plus int/uint/float), and char. /// Primitives are just the fixed-size numeric types (plus int/uint/float), and char.
Primitive(ast::PrimTy), Primitive(ast::PrimTy),
Closure(Box<ClosureDecl>, Option<Lifetime>), Closure(Box<ClosureDecl>, Option<Lifetime>),
@ -753,6 +960,93 @@ impl Clean<Type> for ast::Ty {
} }
} }
impl Clean<Type> for ty::t {
fn clean(&self) -> Type {
match ty::get(*self).sty {
ty::ty_nil => Unit,
ty::ty_bot => Bottom,
ty::ty_bool => Bool,
ty::ty_char => Primitive(ast::TyChar),
ty::ty_int(t) => Primitive(ast::TyInt(t)),
ty::ty_uint(u) => Primitive(ast::TyUint(u)),
ty::ty_float(f) => Primitive(ast::TyFloat(f)),
ty::ty_box(t) => Managed(box t.clean()),
ty::ty_uniq(t) => Unique(box t.clean()),
ty::ty_str => String,
ty::ty_vec(mt, None) => Vector(box mt.ty.clean()),
ty::ty_vec(mt, Some(i)) => FixedVector(box mt.ty.clean(),
format_strbuf!("{}", i)),
ty::ty_ptr(mt) => RawPointer(mt.mutbl.clean(), box mt.ty.clean()),
ty::ty_rptr(r, mt) => BorrowedRef {
lifetime: r.clean(),
mutability: mt.mutbl.clean(),
type_: box mt.ty.clean(),
},
ty::ty_bare_fn(ref fty) => BareFunction(box BareFunctionDecl {
fn_style: fty.fn_style,
generics: Generics {
lifetimes: Vec::new(), type_params: Vec::new()
},
decl: fty.sig.clean(),
abi: fty.abi.to_str().to_strbuf(),
}),
ty::ty_closure(ref fty) => {
let decl = box ClosureDecl {
lifetimes: Vec::new(), // FIXME: this looks wrong...
decl: fty.sig.clean(),
onceness: fty.onceness,
fn_style: fty.fn_style,
bounds: fty.bounds.iter().map(|i| i.clean()).collect(),
};
match fty.store {
ty::UniqTraitStore => Proc(decl),
ty::RegionTraitStore(ref r, _) => Closure(decl, r.clean()),
}
}
ty::ty_struct(did, ref substs) |
ty::ty_enum(did, ref substs) |
ty::ty_trait(box ty::TyTrait { def_id: did, ref substs, .. }) => {
let cx = super::ctxtkey.get().unwrap();
let tcx = match cx.maybe_typed {
core::Typed(ref tycx) => tycx,
core::NotTyped(_) => fail!(),
};
let fqn = csearch::get_item_path(tcx, did);
let fqn: Vec<StrBuf> = fqn.move_iter().map(|i| {
i.to_str().to_strbuf()
}).collect();
let mut path = external_path(fqn.last().unwrap().to_str());
let kind = match ty::get(*self).sty {
ty::ty_struct(..) => TypeStruct,
ty::ty_trait(..) => TypeTrait,
_ => TypeEnum,
};
path.segments.get_mut(0).lifetimes = match substs.regions {
ty::ErasedRegions => Vec::new(),
ty::NonerasedRegions(ref v) => {
v.iter().filter_map(|v| v.clean()).collect()
}
};
path.segments.get_mut(0).types = substs.tps.clean();
cx.external_paths.borrow_mut().get_mut_ref().insert(did,
(fqn, kind));
ResolvedPath {
path: path,
typarams: None,
did: did,
}
}
ty::ty_tup(ref t) => Tuple(t.iter().map(|t| t.clean()).collect()),
ty::ty_param(ref p) => Generic(p.def_id),
ty::ty_self(did) => Self(did),
ty::ty_infer(..) => fail!("ty_infer"),
ty::ty_err => fail!("ty_err"),
}
}
}
#[deriving(Clone, Encodable, Decodable)] #[deriving(Clone, Encodable, Decodable)]
pub enum StructField { pub enum StructField {
HiddenStructField, // inserted later by strip passes HiddenStructField, // inserted later by strip passes
@ -770,7 +1064,7 @@ impl Clean<Item> for ast::StructField {
attrs: self.node.attrs.clean().move_iter().collect(), attrs: self.node.attrs.clean().move_iter().collect(),
source: self.span.clean(), source: self.span.clean(),
visibility: Some(vis), visibility: Some(vis),
id: self.node.id, def_id: ast_util::local_def(self.node.id),
inner: StructFieldItem(TypedStructField(self.node.ty.clean())), inner: StructFieldItem(TypedStructField(self.node.ty.clean())),
} }
} }
@ -798,7 +1092,7 @@ impl Clean<Item> for doctree::Struct {
name: Some(self.name.clean()), name: Some(self.name.clean()),
attrs: self.attrs.clean(), attrs: self.attrs.clean(),
source: self.where.clean(), source: self.where.clean(),
id: self.id, def_id: ast_util::local_def(self.id),
visibility: self.vis.clean(), visibility: self.vis.clean(),
inner: StructItem(Struct { inner: StructItem(Struct {
struct_type: self.struct_type, struct_type: self.struct_type,
@ -843,7 +1137,7 @@ impl Clean<Item> for doctree::Enum {
name: Some(self.name.clean()), name: Some(self.name.clean()),
attrs: self.attrs.clean(), attrs: self.attrs.clean(),
source: self.where.clean(), source: self.where.clean(),
id: self.id, def_id: ast_util::local_def(self.id),
visibility: self.vis.clean(), visibility: self.vis.clean(),
inner: EnumItem(Enum { inner: EnumItem(Enum {
variants: self.variants.clean(), variants: self.variants.clean(),
@ -866,7 +1160,7 @@ impl Clean<Item> for doctree::Variant {
attrs: self.attrs.clean(), attrs: self.attrs.clean(),
source: self.where.clean(), source: self.where.clean(),
visibility: self.vis.clean(), visibility: self.vis.clean(),
id: self.id, def_id: ast_util::local_def(self.id),
inner: VariantItem(Variant { inner: VariantItem(Variant {
kind: self.kind.clean(), kind: self.kind.clean(),
}), }),
@ -988,7 +1282,7 @@ impl Clean<Item> for doctree::Typedef {
name: Some(self.name.clean()), name: Some(self.name.clean()),
attrs: self.attrs.clean(), attrs: self.attrs.clean(),
source: self.where.clean(), source: self.where.clean(),
id: self.id.clone(), def_id: ast_util::local_def(self.id.clone()),
visibility: self.vis.clean(), visibility: self.vis.clean(),
inner: TypedefItem(Typedef { inner: TypedefItem(Typedef {
type_: self.ty.clean(), type_: self.ty.clean(),
@ -1037,7 +1331,7 @@ impl Clean<Item> for doctree::Static {
name: Some(self.name.clean()), name: Some(self.name.clean()),
attrs: self.attrs.clean(), attrs: self.attrs.clean(),
source: self.where.clean(), source: self.where.clean(),
id: self.id, def_id: ast_util::local_def(self.id),
visibility: self.vis.clean(), visibility: self.vis.clean(),
inner: StaticItem(Static { inner: StaticItem(Static {
type_: self.type_.clean(), type_: self.type_.clean(),
@ -1089,7 +1383,7 @@ impl Clean<Item> for doctree::Impl {
name: None, name: None,
attrs: self.attrs.clean(), attrs: self.attrs.clean(),
source: self.where.clean(), source: self.where.clean(),
id: self.id, def_id: ast_util::local_def(self.id),
visibility: self.vis.clean(), visibility: self.vis.clean(),
inner: ImplItem(Impl { inner: ImplItem(Impl {
generics: self.generics.clean(), generics: self.generics.clean(),
@ -1113,7 +1407,7 @@ impl Clean<Item> for ast::ViewItem {
name: None, name: None,
attrs: self.attrs.clean().move_iter().collect(), attrs: self.attrs.clean().move_iter().collect(),
source: self.span.clean(), source: self.span.clean(),
id: 0, def_id: ast_util::local_def(0),
visibility: self.vis.clean(), visibility: self.vis.clean(),
inner: ViewItemItem(ViewItem { inner: ViewItemItem(ViewItem {
inner: self.node.clean() inner: self.node.clean()
@ -1219,7 +1513,7 @@ impl Clean<Item> for ast::ForeignItem {
name: Some(self.ident.clean()), name: Some(self.ident.clean()),
attrs: self.attrs.clean().move_iter().collect(), attrs: self.attrs.clean().move_iter().collect(),
source: self.span.clean(), source: self.span.clean(),
id: self.id, def_id: ast_util::local_def(self.id),
visibility: self.vis.clean(), visibility: self.vis.clean(),
inner: inner, inner: inner,
} }
@ -1288,7 +1582,7 @@ fn name_from_pat(p: &ast::Pat) -> StrBuf {
} }
/// Given a Type, resolve it using the def_map /// Given a Type, resolve it using the def_map
fn resolve_type(path: Path, tpbs: Option<Vec<TyParamBound> >, fn resolve_type(path: Path, tpbs: Option<Vec<TyParamBound>>,
id: ast::NodeId) -> Type { id: ast::NodeId) -> Type {
let cx = super::ctxtkey.get().unwrap(); let cx = super::ctxtkey.get().unwrap();
let tycx = match cx.maybe_typed { let tycx = match cx.maybe_typed {
@ -1303,13 +1597,13 @@ fn resolve_type(path: Path, tpbs: Option<Vec<TyParamBound> >,
}; };
match def { match def {
ast::DefSelfTy(i) => return Self(i), ast::DefSelfTy(i) => return Self(ast_util::local_def(i)),
ast::DefPrimTy(p) => match p { ast::DefPrimTy(p) => match p {
ast::TyStr => return String, ast::TyStr => return String,
ast::TyBool => return Bool, ast::TyBool => return Bool,
_ => return Primitive(p) _ => return Primitive(p)
}, },
ast::DefTyParam(i, _) => return Generic(i.node), ast::DefTyParam(i, _) => return Generic(i),
ast::DefTyParamBinder(i) => return TyParamBinder(i), ast::DefTyParamBinder(i) => return TyParamBinder(i),
_ => {} _ => {}
}; };
@ -1337,9 +1631,26 @@ fn register_def(cx: &core::DocContext, def: ast::Def) -> ast::DefId {
let fqn = fqn.move_iter().map(|i| i.to_str().to_strbuf()).collect(); let fqn = fqn.move_iter().map(|i| i.to_str().to_strbuf()).collect();
debug!("recording {} => {}", did, fqn); debug!("recording {} => {}", did, fqn);
cx.external_paths.borrow_mut().get_mut_ref().insert(did, (fqn, kind)); cx.external_paths.borrow_mut().get_mut_ref().insert(did, (fqn, kind));
match kind {
TypeTrait => {
let t = build_external_trait(tcx, did);
cx.external_traits.borrow_mut().get_mut_ref().insert(did, t);
}
_ => {}
}
return did; return did;
} }
fn build_external_trait(tcx: &ty::ctxt, did: ast::DefId) -> Trait {
let def = csearch::get_trait_def(tcx, did);
let methods = ty::trait_methods(tcx, did);
Trait {
generics: def.generics.clean(),
methods: methods.iter().map(|i| i.clean()).collect(),
parents: Vec::new(), // FIXME: this is likely wrong
}
}
fn resolve_use_source(path: Path, id: ast::NodeId) -> ImportSource { fn resolve_use_source(path: Path, id: ast::NodeId) -> ImportSource {
ImportSource { ImportSource {
path: path, path: path,
@ -1369,7 +1680,7 @@ impl Clean<Item> for doctree::Macro {
attrs: self.attrs.clean(), attrs: self.attrs.clean(),
source: self.where.clean(), source: self.where.clean(),
visibility: ast::Public.clean(), visibility: ast::Public.clean(),
id: self.id, def_id: ast_util::local_def(self.id),
inner: MacroItem(Macro { inner: MacroItem(Macro {
source: self.where.to_src(), source: self.where.to_src(),
}), }),

View File

@ -20,7 +20,7 @@ use syntax;
use std::cell::RefCell; use std::cell::RefCell;
use std::os; use std::os;
use collections::{HashSet, HashMap}; use collections::{HashMap, HashSet};
use visit_ast::RustdocVisitor; use visit_ast::RustdocVisitor;
use clean; use clean;
@ -39,6 +39,8 @@ pub struct DocContext {
pub maybe_typed: MaybeTyped, pub maybe_typed: MaybeTyped,
pub src: Path, pub src: Path,
pub external_paths: ExternalPaths, pub external_paths: ExternalPaths,
pub external_traits: RefCell<Option<HashMap<ast::DefId, clean::Trait>>>,
pub external_typarams: RefCell<Option<HashMap<ast::DefId, StrBuf>>>,
} }
impl DocContext { impl DocContext {
@ -54,6 +56,8 @@ pub struct CrateAnalysis {
pub exported_items: privacy::ExportedItems, pub exported_items: privacy::ExportedItems,
pub public_items: privacy::PublicItems, pub public_items: privacy::PublicItems,
pub external_paths: ExternalPaths, pub external_paths: ExternalPaths,
pub external_traits: RefCell<Option<HashMap<ast::DefId, clean::Trait>>>,
pub external_typarams: RefCell<Option<HashMap<ast::DefId, StrBuf>>>,
} }
/// Parses, resolves, and typechecks the given crate /// Parses, resolves, and typechecks the given crate
@ -104,11 +108,15 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<StrBuf>)
krate: krate, krate: krate,
maybe_typed: Typed(ty_cx), maybe_typed: Typed(ty_cx),
src: cpath.clone(), src: cpath.clone(),
external_traits: RefCell::new(Some(HashMap::new())),
external_typarams: RefCell::new(Some(HashMap::new())),
external_paths: RefCell::new(Some(HashMap::new())), external_paths: RefCell::new(Some(HashMap::new())),
}, CrateAnalysis { }, CrateAnalysis {
exported_items: exported_items, exported_items: exported_items,
public_items: public_items, public_items: public_items,
external_paths: RefCell::new(None), external_paths: RefCell::new(None),
external_traits: RefCell::new(None),
external_typarams: RefCell::new(None),
}) })
} }
@ -126,5 +134,9 @@ pub fn run_core(libs: HashSet<Path>, cfgs: Vec<StrBuf>, path: &Path)
let external_paths = ctxt.external_paths.borrow_mut().take(); let external_paths = ctxt.external_paths.borrow_mut().take();
*analysis.external_paths.borrow_mut() = external_paths; *analysis.external_paths.borrow_mut() = external_paths;
let map = ctxt.external_traits.borrow_mut().take();
*analysis.external_traits.borrow_mut() = map;
let map = ctxt.external_typarams.borrow_mut().take();
*analysis.external_typarams.borrow_mut() = map;
(krate, analysis) (krate, analysis)
} }

View File

@ -19,7 +19,7 @@ pub trait DocFolder {
/// don't override! /// don't override!
fn fold_item_recur(&mut self, item: Item) -> Option<Item> { fn fold_item_recur(&mut self, item: Item) -> Option<Item> {
let Item { attrs, name, source, visibility, id, inner } = item; let Item { attrs, name, source, visibility, def_id, inner } = item;
let inner = inner; let inner = inner;
let inner = match inner { let inner = match inner {
StructItem(mut i) => { StructItem(mut i) => {
@ -83,7 +83,7 @@ pub trait DocFolder {
}; };
Some(Item { attrs: attrs, name: name, source: source, inner: inner, Some(Item { attrs: attrs, name: name, source: source, inner: inner,
visibility: visibility, id: id }) visibility: visibility, def_id: def_id })
} }
fn fold_mod(&mut self, m: Module) -> Module { fn fold_mod(&mut self, m: Module) -> Module {

View File

@ -284,11 +284,15 @@ fn tybounds(w: &mut fmt::Formatter,
impl fmt::Show for clean::Type { impl fmt::Show for clean::Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
clean::TyParamBinder(id) | clean::Generic(id) => { clean::TyParamBinder(id) => {
let m = cache_key.get().unwrap(); let m = cache_key.get().unwrap();
f.write(m.typarams.get(&id).as_bytes()) f.write(m.typarams.get(&ast_util::local_def(id)).as_bytes())
} }
clean::ResolvedPath{ did, ref typarams, ref path} => { clean::Generic(did) => {
let m = cache_key.get().unwrap();
f.write(m.typarams.get(&did).as_bytes())
}
clean::ResolvedPath{ did, ref typarams, ref path } => {
try!(resolved_path(f, did, path, false)); try!(resolved_path(f, did, path, false));
tybounds(f, typarams) tybounds(f, typarams)
} }

View File

@ -85,7 +85,7 @@ pub struct Context {
/// functions), and the value is the list of containers belonging to this /// functions), and the value is the list of containers belonging to this
/// header. This map will change depending on the surrounding context of the /// header. This map will change depending on the surrounding context of the
/// page. /// page.
pub sidebar: HashMap<StrBuf, Vec<StrBuf> >, pub sidebar: HashMap<StrBuf, Vec<StrBuf>>,
/// This flag indicates whether [src] links should be generated or not. If /// This flag indicates whether [src] links should be generated or not. If
/// the source files are present in the html rendering, then this will be /// the source files are present in the html rendering, then this will be
/// `true`. /// `true`.
@ -124,7 +124,7 @@ pub struct Cache {
/// Mapping of typaram ids to the name of the type parameter. This is used /// Mapping of typaram ids to the name of the type parameter. This is used
/// when pretty-printing a type (so pretty printing doesn't have to /// when pretty-printing a type (so pretty printing doesn't have to
/// painfully maintain a context like this) /// painfully maintain a context like this)
pub typarams: HashMap<ast::NodeId, StrBuf>, pub typarams: HashMap<ast::DefId, StrBuf>,
/// Maps a type id to all known implementations for that type. This is only /// Maps a type id to all known implementations for that type. This is only
/// recognized for intra-crate `ResolvedPath` types, and is used to print /// recognized for intra-crate `ResolvedPath` types, and is used to print
@ -132,7 +132,7 @@ pub struct Cache {
/// ///
/// The values of the map are a list of implementations and documentation /// The values of the map are a list of implementations and documentation
/// found on that implementation. /// found on that implementation.
pub impls: HashMap<ast::NodeId, Vec<(clean::Impl, Option<StrBuf>)> >, pub impls: HashMap<ast::NodeId, Vec<(clean::Impl, Option<StrBuf>)>>,
/// Maintains a mapping of local crate node ids to the fully qualified name /// Maintains a mapping of local crate node ids to the fully qualified name
/// and "short type description" of that node. This is used when generating /// and "short type description" of that node. This is used when generating
@ -145,15 +145,12 @@ pub struct Cache {
/// Implementations of a crate should inherit the documentation of the /// Implementations of a crate should inherit the documentation of the
/// parent trait if no extra documentation is specified, and default methods /// parent trait if no extra documentation is specified, and default methods
/// should show up in documentation about trait implementations. /// should show up in documentation about trait implementations.
pub traits: HashMap<ast::NodeId, clean::Trait>, pub traits: HashMap<ast::DefId, clean::Trait>,
/// When rendering traits, it's often useful to be able to list all /// When rendering traits, it's often useful to be able to list all
/// implementors of the trait, and this mapping is exactly, that: a mapping /// implementors of the trait, and this mapping is exactly, that: a mapping
/// of trait ids to the list of known implementors of the trait /// of trait ids to the list of known implementors of the trait
pub implementors: HashMap<ast::NodeId, Vec<Implementor>>, pub implementors: HashMap<ast::DefId, Vec<Implementor>>,
/// Implementations of external traits, keyed by the external trait def id.
pub foreign_implementors: HashMap<ast::DefId, Vec<Implementor>>,
/// Cache of where external crate documentation can be found. /// Cache of where external crate documentation can be found.
pub extern_locations: HashMap<ast::CrateNum, ExternalLocation>, pub extern_locations: HashMap<ast::CrateNum, ExternalLocation>,
@ -251,6 +248,7 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
// Crawl the crate to build various caches used for the output // Crawl the crate to build various caches used for the output
let analysis = ::analysiskey.get(); let analysis = ::analysiskey.get();
let public_items = analysis.as_ref().map(|a| a.public_items.clone()); let public_items = analysis.as_ref().map(|a| a.public_items.clone());
let public_items = public_items.unwrap_or(NodeSet::new());
let paths = analysis.as_ref().map(|a| { let paths = analysis.as_ref().map(|a| {
let paths = a.external_paths.borrow_mut().take_unwrap(); let paths = a.external_paths.borrow_mut().take_unwrap();
paths.move_iter().map(|(k, (v, t))| { paths.move_iter().map(|(k, (v, t))| {
@ -267,18 +265,21 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
}).unwrap_or(HashMap::new()); }).unwrap_or(HashMap::new());
let mut cache = Cache { let mut cache = Cache {
impls: HashMap::new(), impls: HashMap::new(),
typarams: HashMap::new(),
paths: paths, paths: paths,
traits: HashMap::new(),
implementors: HashMap::new(), implementors: HashMap::new(),
foreign_implementors: HashMap::new(),
stack: Vec::new(), stack: Vec::new(),
parent_stack: Vec::new(), parent_stack: Vec::new(),
search_index: Vec::new(), search_index: Vec::new(),
extern_locations: HashMap::new(), extern_locations: HashMap::new(),
privmod: false, privmod: false,
public_items: public_items.unwrap_or(NodeSet::new()), public_items: public_items,
orphan_methods: Vec::new(), orphan_methods: Vec::new(),
traits: analysis.as_ref().map(|a| {
a.external_traits.borrow_mut().take_unwrap()
}).unwrap_or(HashMap::new()),
typarams: analysis.as_ref().map(|a| {
a.external_typarams.borrow_mut().take_unwrap()
}).unwrap_or(HashMap::new()),
}; };
cache.stack.push(krate.name.clone()); cache.stack.push(krate.name.clone());
krate = cache.fold_crate(krate); krate = cache.fold_crate(krate);
@ -431,7 +432,8 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
// Update the list of all implementors for traits // Update the list of all implementors for traits
let dst = cx.dst.join("implementors"); let dst = cx.dst.join("implementors");
try!(mkdir(&dst)); try!(mkdir(&dst));
for (&did, imps) in cache.foreign_implementors.iter() { for (&did, imps) in cache.implementors.iter() {
if ast_util::is_local(did) { continue }
let &(ref remote_path, remote_item_type) = cache.paths.get(&did); let &(ref remote_path, remote_item_type) = cache.paths.get(&did);
let mut mydst = dst.clone(); let mut mydst = dst.clone();
@ -686,7 +688,7 @@ impl DocFolder for Cache {
// trait // trait
match item.inner { match item.inner {
clean::TraitItem(ref t) => { clean::TraitItem(ref t) => {
self.traits.insert(item.id, t.clone()); self.traits.insert(item.def_id, t.clone());
} }
_ => {} _ => {}
} }
@ -695,15 +697,10 @@ impl DocFolder for Cache {
match item.inner { match item.inner {
clean::ImplItem(ref i) => { clean::ImplItem(ref i) => {
match i.trait_ { match i.trait_ {
// FIXME: this is_local() check seems to be losing
// information
Some(clean::ResolvedPath{ did, .. }) => { Some(clean::ResolvedPath{ did, .. }) => {
let v = if ast_util::is_local(did) { let v = self.implementors.find_or_insert_with(did, |_| {
self.implementors.find_or_insert(did.node, Vec::new()) Vec::new()
} else { });
self.foreign_implementors.find_or_insert(did,
Vec::new())
};
match i.for_ { match i.for_ {
clean::ResolvedPath{..} => { clean::ResolvedPath{..} => {
v.unshift(PathType(i.for_.clone())); v.unshift(PathType(i.for_.clone()));
@ -789,16 +786,19 @@ impl DocFolder for Cache {
clean::TypedefItem(..) | clean::TraitItem(..) | clean::TypedefItem(..) | clean::TraitItem(..) |
clean::FunctionItem(..) | clean::ModuleItem(..) | clean::FunctionItem(..) | clean::ModuleItem(..) |
clean::ForeignFunctionItem(..) => { clean::ForeignFunctionItem(..) => {
// Reexported items mean that the same id can show up twice in if ast_util::is_local(item.def_id) {
// the rustdoc ast that we're looking at. We know, however, that // Reexported items mean that the same id can show up twice
// a reexported item doesn't show up in the `public_items` map, // in the rustdoc ast that we're looking at. We know,
// so we can skip inserting into the paths map if there was // however, that a reexported item doesn't show up in the
// already an entry present and we're not a public item. // `public_items` map, so we can skip inserting into the
let did = ast_util::local_def(item.id); // paths map if there was already an entry present and we're
if !self.paths.contains_key(&did) || // not a public item.
self.public_items.contains(&item.id) { let id = item.def_id.node;
self.paths.insert(did, (self.stack.clone(), if !self.paths.contains_key(&item.def_id) ||
shortty(&item))); self.public_items.contains(&id) {
self.paths.insert(item.def_id,
(self.stack.clone(), shortty(&item)));
}
} }
} }
// link variants to their parent enum because pages aren't emitted // link variants to their parent enum because pages aren't emitted
@ -806,8 +806,7 @@ impl DocFolder for Cache {
clean::VariantItem(..) => { clean::VariantItem(..) => {
let mut stack = self.stack.clone(); let mut stack = self.stack.clone();
stack.pop(); stack.pop();
self.paths.insert(ast_util::local_def(item.id), self.paths.insert(item.def_id, (stack, item_type::Enum));
(stack, item_type::Enum));
} }
_ => {} _ => {}
} }
@ -815,7 +814,10 @@ impl DocFolder for Cache {
// Maintain the parent stack // Maintain the parent stack
let parent_pushed = match item.inner { let parent_pushed = match item.inner {
clean::TraitItem(..) | clean::EnumItem(..) | clean::StructItem(..) => { clean::TraitItem(..) | clean::EnumItem(..) | clean::StructItem(..) => {
self.parent_stack.push(item.id); true if ast_util::is_local(item.def_id) {
self.parent_stack.push(item.def_id.node);
}
true
} }
clean::ImplItem(ref i) => { clean::ImplItem(ref i) => {
match i.for_ { match i.for_ {
@ -893,7 +895,7 @@ impl DocFolder for Cache {
impl<'a> Cache { impl<'a> Cache {
fn generics(&mut self, generics: &clean::Generics) { fn generics(&mut self, generics: &clean::Generics) {
for typ in generics.type_params.iter() { for typ in generics.type_params.iter() {
self.typarams.insert(typ.id, typ.name.clone()); self.typarams.insert(typ.did, typ.name.clone());
} }
} }
} }
@ -1411,7 +1413,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
try!(write!(w, "</div>")); try!(write!(w, "</div>"));
} }
match cache_key.get().unwrap().implementors.find(&it.id) { match cache_key.get().unwrap().implementors.find(&it.def_id) {
Some(implementors) => { Some(implementors) => {
try!(write!(w, " try!(write!(w, "
<h2 id='implementors'>Implementors</h2> <h2 id='implementors'>Implementors</h2>
@ -1667,7 +1669,7 @@ fn render_struct(w: &mut fmt::Formatter, it: &clean::Item,
} }
fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result { fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
match cache_key.get().unwrap().impls.find(&it.id) { match cache_key.get().unwrap().impls.find(&it.def_id.node) {
Some(v) => { Some(v) => {
let mut non_trait = v.iter().filter(|p| { let mut non_trait = v.iter().filter(|p| {
p.ref0().trait_.is_none() p.ref0().trait_.is_none()
@ -1714,16 +1716,10 @@ fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
fn render_impl(w: &mut fmt::Formatter, i: &clean::Impl, fn render_impl(w: &mut fmt::Formatter, i: &clean::Impl,
dox: &Option<StrBuf>) -> fmt::Result { dox: &Option<StrBuf>) -> fmt::Result {
try!(write!(w, "<h3 class='impl'><code>impl{} ", i.generics)); try!(write!(w, "<h3 class='impl'><code>impl{} ", i.generics));
let trait_id = match i.trait_ { match i.trait_ {
Some(ref ty) => { Some(ref ty) => try!(write!(w, "{} for ", *ty)),
try!(write!(w, "{} for ", *ty)); None => {}
match *ty { }
clean::ResolvedPath { did, .. } => Some(did),
_ => None,
}
}
None => None
};
try!(write!(w, "{}</code></h3>", i.for_)); try!(write!(w, "{}</code></h3>", i.for_));
match *dox { match *dox {
Some(ref dox) => { Some(ref dox) => {
@ -1753,31 +1749,34 @@ fn render_impl(w: &mut fmt::Formatter, i: &clean::Impl,
try!(docmeth(w, meth, true)); try!(docmeth(w, meth, true));
} }
fn render_default_methods(w: &mut fmt::Formatter,
t: &clean::Trait,
i: &clean::Impl) -> fmt::Result {
for method in t.methods.iter() {
let n = method.item().name.clone();
match i.methods.iter().find(|m| { m.name == n }) {
Some(..) => continue,
None => {}
}
try!(docmeth(w, method.item(), false));
}
Ok(())
}
// If we've implemented a trait, then also emit documentation for all // If we've implemented a trait, then also emit documentation for all
// default methods which weren't overridden in the implementation block. // default methods which weren't overridden in the implementation block.
match trait_id { match i.trait_ {
None => {} Some(clean::ResolvedPath { did, .. }) => {
// FIXME: this should work for non-local traits
Some(did) if ast_util::is_local(did) => {
try!({ try!({
match cache_key.get().unwrap().traits.find(&did.node) { match cache_key.get().unwrap().traits.find(&did) {
Some(t) => { Some(t) => try!(render_default_methods(w, t, i)),
for method in t.methods.iter() {
let n = method.item().name.clone();
match i.methods.iter().find(|m| m.name == n) {
Some(..) => continue,
None => {}
}
try!(docmeth(w, method.item(), false));
}
}
None => {} None => {}
} }
Ok(()) Ok(())
}) })
} }
Some(..) => {} Some(..) | None => {}
} }
try!(write!(w, "</div>")); try!(write!(w, "</div>"));
Ok(()) Ok(())
@ -1849,7 +1848,7 @@ impl<'a> fmt::Show for Sidebar<'a> {
} }
} }
fn build_sidebar(m: &clean::Module) -> HashMap<StrBuf, Vec<StrBuf> > { fn build_sidebar(m: &clean::Module) -> HashMap<StrBuf, Vec<StrBuf>> {
let mut map = HashMap::new(); let mut map = HashMap::new();
for item in m.items.iter() { for item in m.items.iter() {
let short = shortty(item).to_static_str(); let short = shortty(item).to_static_str();

View File

@ -35,7 +35,7 @@ pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult {
fn fold_item(&mut self, i: Item) -> Option<Item> { fn fold_item(&mut self, i: Item) -> Option<Item> {
if i.is_hidden_from_doc() { if i.is_hidden_from_doc() {
debug!("found one in strip_hidden; removing"); debug!("found one in strip_hidden; removing");
self.stripped.insert(i.id); self.stripped.insert(i.def_id.node);
// use a dedicated hidden item for given item type if any // use a dedicated hidden item for given item type if any
match i.inner { match i.inner {
@ -124,7 +124,8 @@ impl<'a> fold::DocFolder for Stripper<'a> {
clean::TraitItem(..) | clean::FunctionItem(..) | clean::TraitItem(..) | clean::FunctionItem(..) |
clean::VariantItem(..) | clean::MethodItem(..) | clean::VariantItem(..) | clean::MethodItem(..) |
clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) => { clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) => {
if !self.exported_items.contains(&i.id) { if ast_util::is_local(i.def_id) &&
!self.exported_items.contains(&i.def_id.node) {
return None; return None;
} }
} }
@ -173,7 +174,7 @@ impl<'a> fold::DocFolder for Stripper<'a> {
}; };
let i = if fastreturn { let i = if fastreturn {
self.retained.insert(i.id); self.retained.insert(i.def_id.node);
return Some(i); return Some(i);
} else { } else {
self.fold_item_recur(i) self.fold_item_recur(i)
@ -188,7 +189,7 @@ impl<'a> fold::DocFolder for Stripper<'a> {
i.doc_value().is_none() => None, i.doc_value().is_none() => None,
clean::ImplItem(ref i) if i.methods.len() == 0 => None, clean::ImplItem(ref i) if i.methods.len() == 0 => None,
_ => { _ => {
self.retained.insert(i.id); self.retained.insert(i.def_id.node);
Some(i) Some(i)
} }
} }

View File

@ -76,6 +76,8 @@ pub fn run(input: &str,
maybe_typed: core::NotTyped(sess), maybe_typed: core::NotTyped(sess),
src: input_path, src: input_path,
external_paths: RefCell::new(Some(HashMap::new())), external_paths: RefCell::new(Some(HashMap::new())),
external_traits: RefCell::new(None),
external_typarams: RefCell::new(None),
}; };
super::ctxtkey.replace(Some(ctx)); super::ctxtkey.replace(Some(ctx));