diff --git a/src/librustdoc/clean.rs b/src/librustdoc/clean.rs
index bd911f42db0..236c98b72e6 100644
--- a/src/librustdoc/clean.rs
+++ b/src/librustdoc/clean.rs
@@ -25,6 +25,7 @@ use rustc::driver::driver;
 use rustc::metadata::cstore;
 use rustc::metadata::csearch;
 use rustc::metadata::decoder;
+use rustc::middle::ty;
 
 use std::strbuf::StrBuf;
 
@@ -128,7 +129,7 @@ pub struct Item {
     pub attrs: Vec<Attribute> ,
     pub inner: ItemEnum,
     pub visibility: Option<Visibility>,
-    pub id: ast::NodeId,
+    pub def_id: ast::DefId,
 }
 
 impl Item {
@@ -274,7 +275,7 @@ impl Clean<Item> for doctree::Module {
             attrs: self.attrs.clean(),
             source: where.clean(),
             visibility: self.vis.clean(),
-            id: self.id,
+            def_id: ast_util::local_def(self.id),
             inner: ModuleItem(Module {
                is_crate: self.is_crate,
                items: items.iter()
@@ -339,7 +340,7 @@ impl<'a> attr::AttrMetaMethods for &'a Attribute {
 #[deriving(Clone, Encodable, Decodable)]
 pub struct TyParam {
     pub name: StrBuf,
-    pub id: ast::NodeId,
+    pub did: ast::DefId,
     pub bounds: Vec<TyParamBound>,
 }
 
@@ -347,12 +348,25 @@ impl Clean<TyParam> for ast::TyParam {
     fn clean(&self) -> TyParam {
         TyParam {
             name: self.ident.clean(),
-            id: self.id,
+            did: ast::DefId { krate: ast::LOCAL_CRATE, node: self.id },
             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)]
 pub enum TyParamBound {
     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)]
 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]?
 #[deriving(Clone, Encodable, Decodable)]
 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)]
 pub struct Method {
     pub generics: Generics,
@@ -428,11 +564,11 @@ impl Clean<Item> for ast::Method {
             name: Some(self.ident.clean()),
             attrs: self.attrs.clean().move_iter().collect(),
             source: self.span.clean(),
-            id: self.id.clone(),
+            def_id: ast_util::local_def(self.id.clone()),
             visibility: self.vis.clean(),
             inner: MethodItem(Method {
                 generics: self.generics.clean(),
-                self_: self.explicit_self.clean(),
+                self_: self.explicit_self.node.clean(),
                 fn_style: self.fn_style.clone(),
                 decl: decl,
             }),
@@ -466,12 +602,12 @@ impl Clean<Item> for ast::TypeMethod {
             name: Some(self.ident.clean()),
             attrs: self.attrs.clean().move_iter().collect(),
             source: self.span.clean(),
-            id: self.id,
+            def_id: ast_util::local_def(self.id),
             visibility: None,
             inner: TyMethodItem(TyMethod {
                 fn_style: self.fn_style.clone(),
                 decl: decl,
-                self_: self.explicit_self.clean(),
+                self_: self.explicit_self.node.clean(),
                 generics: self.generics.clean(),
             }),
         }
@@ -486,9 +622,9 @@ pub enum SelfTy {
     SelfOwned,
 }
 
-impl Clean<SelfTy> for ast::ExplicitSelf {
+impl Clean<SelfTy> for ast::ExplicitSelf_ {
     fn clean(&self) -> SelfTy {
-        match self.node {
+        match *self {
             ast::SelfStatic => SelfStatic,
             ast::SelfValue => SelfValue,
             ast::SelfUniq => SelfOwned,
@@ -511,7 +647,7 @@ impl Clean<Item> for doctree::Function {
             attrs: self.attrs.clean(),
             source: self.where.clean(),
             visibility: self.vis.clean(),
-            id: self.id,
+            def_id: ast_util::local_def(self.id),
             inner: FunctionItem(Function {
                 decl: self.decl.clean(),
                 generics: self.generics.clean(),
@@ -533,7 +669,7 @@ pub struct ClosureDecl {
 impl Clean<ClosureDecl> for ast::ClosureTy {
     fn clean(&self) -> ClosureDecl {
         ClosureDecl {
-            lifetimes: self.lifetimes.clean().move_iter().collect(),
+            lifetimes: self.lifetimes.clean(),
             decl: self.decl.clean(),
             onceness: self.onceness,
             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)]
 pub struct Argument {
     pub type_: Type,
@@ -616,7 +771,7 @@ impl Clean<Item> for doctree::Trait {
             name: Some(self.name.clean()),
             attrs: self.attrs.clean(),
             source: self.where.clean(),
-            id: self.id,
+            def_id: ast_util::local_def(self.id),
             visibility: self.vis.clean(),
             inner: TraitItem(Trait {
                 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
 /// 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.
@@ -684,9 +891,9 @@ pub enum Type {
     TyParamBinder(ast::NodeId),
     /// For parameterized types, so the consumer of the JSON don't go looking
     /// for types which don't exist anywhere.
-    Generic(ast::NodeId),
+    Generic(ast::DefId),
     /// For references to self
-    Self(ast::NodeId),
+    Self(ast::DefId),
     /// Primitives are just the fixed-size numeric types (plus int/uint/float), and char.
     Primitive(ast::PrimTy),
     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)]
 pub enum StructField {
     HiddenStructField, // inserted later by strip passes
@@ -770,7 +1064,7 @@ impl Clean<Item> for ast::StructField {
             attrs: self.node.attrs.clean().move_iter().collect(),
             source: self.span.clean(),
             visibility: Some(vis),
-            id: self.node.id,
+            def_id: ast_util::local_def(self.node.id),
             inner: StructFieldItem(TypedStructField(self.node.ty.clean())),
         }
     }
@@ -798,7 +1092,7 @@ impl Clean<Item> for doctree::Struct {
             name: Some(self.name.clean()),
             attrs: self.attrs.clean(),
             source: self.where.clean(),
-            id: self.id,
+            def_id: ast_util::local_def(self.id),
             visibility: self.vis.clean(),
             inner: StructItem(Struct {
                 struct_type: self.struct_type,
@@ -843,7 +1137,7 @@ impl Clean<Item> for doctree::Enum {
             name: Some(self.name.clean()),
             attrs: self.attrs.clean(),
             source: self.where.clean(),
-            id: self.id,
+            def_id: ast_util::local_def(self.id),
             visibility: self.vis.clean(),
             inner: EnumItem(Enum {
                 variants: self.variants.clean(),
@@ -866,7 +1160,7 @@ impl Clean<Item> for doctree::Variant {
             attrs: self.attrs.clean(),
             source: self.where.clean(),
             visibility: self.vis.clean(),
-            id: self.id,
+            def_id: ast_util::local_def(self.id),
             inner: VariantItem(Variant {
                 kind: self.kind.clean(),
             }),
@@ -988,7 +1282,7 @@ impl Clean<Item> for doctree::Typedef {
             name: Some(self.name.clean()),
             attrs: self.attrs.clean(),
             source: self.where.clean(),
-            id: self.id.clone(),
+            def_id: ast_util::local_def(self.id.clone()),
             visibility: self.vis.clean(),
             inner: TypedefItem(Typedef {
                 type_: self.ty.clean(),
@@ -1037,7 +1331,7 @@ impl Clean<Item> for doctree::Static {
             name: Some(self.name.clean()),
             attrs: self.attrs.clean(),
             source: self.where.clean(),
-            id: self.id,
+            def_id: ast_util::local_def(self.id),
             visibility: self.vis.clean(),
             inner: StaticItem(Static {
                 type_: self.type_.clean(),
@@ -1089,7 +1383,7 @@ impl Clean<Item> for doctree::Impl {
             name: None,
             attrs: self.attrs.clean(),
             source: self.where.clean(),
-            id: self.id,
+            def_id: ast_util::local_def(self.id),
             visibility: self.vis.clean(),
             inner: ImplItem(Impl {
                 generics: self.generics.clean(),
@@ -1113,7 +1407,7 @@ impl Clean<Item> for ast::ViewItem {
             name: None,
             attrs: self.attrs.clean().move_iter().collect(),
             source: self.span.clean(),
-            id: 0,
+            def_id: ast_util::local_def(0),
             visibility: self.vis.clean(),
             inner: ViewItemItem(ViewItem {
                 inner: self.node.clean()
@@ -1219,7 +1513,7 @@ impl Clean<Item> for ast::ForeignItem {
             name: Some(self.ident.clean()),
             attrs: self.attrs.clean().move_iter().collect(),
             source: self.span.clean(),
-            id: self.id,
+            def_id: ast_util::local_def(self.id),
             visibility: self.vis.clean(),
             inner: inner,
         }
@@ -1288,7 +1582,7 @@ fn name_from_pat(p: &ast::Pat) -> StrBuf {
 }
 
 /// 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 {
     let cx = super::ctxtkey.get().unwrap();
     let tycx = match cx.maybe_typed {
@@ -1303,13 +1597,13 @@ fn resolve_type(path: Path, tpbs: Option<Vec<TyParamBound> >,
     };
 
     match def {
-        ast::DefSelfTy(i) => return Self(i),
+        ast::DefSelfTy(i) => return Self(ast_util::local_def(i)),
         ast::DefPrimTy(p) => match p {
             ast::TyStr => return String,
             ast::TyBool => return Bool,
             _ => return Primitive(p)
         },
-        ast::DefTyParam(i, _) => return Generic(i.node),
+        ast::DefTyParam(i, _) => return Generic(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();
     debug!("recording {} => {}", did, fqn);
     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;
 }
 
+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 {
     ImportSource {
         path: path,
@@ -1369,7 +1680,7 @@ impl Clean<Item> for doctree::Macro {
             attrs: self.attrs.clean(),
             source: self.where.clean(),
             visibility: ast::Public.clean(),
-            id: self.id,
+            def_id: ast_util::local_def(self.id),
             inner: MacroItem(Macro {
                 source: self.where.to_src(),
             }),
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index f105e14e6e4..470f706e81e 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -20,7 +20,7 @@ use syntax;
 
 use std::cell::RefCell;
 use std::os;
-use collections::{HashSet, HashMap};
+use collections::{HashMap, HashSet};
 
 use visit_ast::RustdocVisitor;
 use clean;
@@ -39,6 +39,8 @@ pub struct DocContext {
     pub maybe_typed: MaybeTyped,
     pub src: Path,
     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 {
@@ -54,6 +56,8 @@ pub struct CrateAnalysis {
     pub exported_items: privacy::ExportedItems,
     pub public_items: privacy::PublicItems,
     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
@@ -104,11 +108,15 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<StrBuf>)
         krate: krate,
         maybe_typed: Typed(ty_cx),
         src: cpath.clone(),
+        external_traits: RefCell::new(Some(HashMap::new())),
+        external_typarams: RefCell::new(Some(HashMap::new())),
         external_paths: RefCell::new(Some(HashMap::new())),
     }, CrateAnalysis {
         exported_items: exported_items,
         public_items: public_items,
         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();
     *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)
 }
diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs
index 0098bcfb4b7..77b12aec97b 100644
--- a/src/librustdoc/fold.rs
+++ b/src/librustdoc/fold.rs
@@ -19,7 +19,7 @@ pub trait DocFolder {
 
     /// don't override!
     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 = match inner {
             StructItem(mut i) => {
@@ -83,7 +83,7 @@ pub trait DocFolder {
         };
 
         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 {
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 563da5318f7..948d47b2eaf 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -284,11 +284,15 @@ fn tybounds(w: &mut fmt::Formatter,
 impl fmt::Show for clean::Type {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
-            clean::TyParamBinder(id) | clean::Generic(id) => {
+            clean::TyParamBinder(id) => {
                 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));
                 tybounds(f, typarams)
             }
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index ed6c25c7c80..752f193fa3f 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -85,7 +85,7 @@ pub struct Context {
     /// functions), and the value is the list of containers belonging to this
     /// header. This map will change depending on the surrounding context of the
     /// 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
     /// the source files are present in the html rendering, then this will be
     /// `true`.
@@ -124,7 +124,7 @@ pub struct Cache {
     /// 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
     /// 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
     /// 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
     /// 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
     /// 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
     /// parent trait if no extra documentation is specified, and default methods
     /// 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
     /// implementors of the trait, and this mapping is exactly, that: a mapping
     /// of trait ids to the list of known implementors of the trait
-    pub implementors: HashMap<ast::NodeId, Vec<Implementor>>,
-
-    /// Implementations of external traits, keyed by the external trait def id.
-    pub foreign_implementors: HashMap<ast::DefId, Vec<Implementor>>,
+    pub implementors: HashMap<ast::DefId, Vec<Implementor>>,
 
     /// Cache of where external crate documentation can be found.
     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
     let analysis = ::analysiskey.get();
     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 = a.external_paths.borrow_mut().take_unwrap();
         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());
     let mut cache = Cache {
         impls: HashMap::new(),
-        typarams: HashMap::new(),
         paths: paths,
-        traits: HashMap::new(),
         implementors: HashMap::new(),
-        foreign_implementors: HashMap::new(),
         stack: Vec::new(),
         parent_stack: Vec::new(),
         search_index: Vec::new(),
         extern_locations: HashMap::new(),
         privmod: false,
-        public_items: public_items.unwrap_or(NodeSet::new()),
+        public_items: public_items,
         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());
     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
         let dst = cx.dst.join("implementors");
         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 mut mydst = dst.clone();
@@ -686,7 +688,7 @@ impl DocFolder for Cache {
         // trait
         match item.inner {
             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 {
             clean::ImplItem(ref i) => {
                 match i.trait_ {
-                    // FIXME: this is_local() check seems to be losing
-                    // information
                     Some(clean::ResolvedPath{ did, .. }) => {
-                        let v = if ast_util::is_local(did) {
-                            self.implementors.find_or_insert(did.node, Vec::new())
-                        } else {
-                            self.foreign_implementors.find_or_insert(did,
-                                                                     Vec::new())
-                        };
+                        let v = self.implementors.find_or_insert_with(did, |_| {
+                            Vec::new()
+                        });
                         match i.for_ {
                             clean::ResolvedPath{..} => {
                                 v.unshift(PathType(i.for_.clone()));
@@ -789,16 +786,19 @@ impl DocFolder for Cache {
             clean::TypedefItem(..) | clean::TraitItem(..) |
             clean::FunctionItem(..) | clean::ModuleItem(..) |
             clean::ForeignFunctionItem(..) => {
-                // Reexported items mean that the same id can show up twice in
-                // the rustdoc ast that we're looking at. We know, however, that
-                // a reexported item doesn't show up in the `public_items` map,
-                // so we can skip inserting into the paths map if there was
-                // already an entry present and we're not a public item.
-                let did = ast_util::local_def(item.id);
-                if !self.paths.contains_key(&did) ||
-                   self.public_items.contains(&item.id) {
-                    self.paths.insert(did, (self.stack.clone(),
-                                            shortty(&item)));
+                if ast_util::is_local(item.def_id) {
+                    // Reexported items mean that the same id can show up twice
+                    // in the rustdoc ast that we're looking at. We know,
+                    // however, that a reexported item doesn't show up in the
+                    // `public_items` map, so we can skip inserting into the
+                    // paths map if there was already an entry present and we're
+                    // not a public item.
+                    let id = item.def_id.node;
+                    if !self.paths.contains_key(&item.def_id) ||
+                       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
@@ -806,8 +806,7 @@ impl DocFolder for Cache {
             clean::VariantItem(..) => {
                 let mut stack = self.stack.clone();
                 stack.pop();
-                self.paths.insert(ast_util::local_def(item.id),
-                                  (stack, item_type::Enum));
+                self.paths.insert(item.def_id, (stack, item_type::Enum));
             }
             _ => {}
         }
@@ -815,7 +814,10 @@ impl DocFolder for Cache {
         // Maintain the parent stack
         let parent_pushed = match item.inner {
             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) => {
                 match i.for_ {
@@ -893,7 +895,7 @@ impl DocFolder for Cache {
 impl<'a> Cache {
     fn generics(&mut self, generics: &clean::Generics) {
         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>"));
     }
 
-    match cache_key.get().unwrap().implementors.find(&it.id) {
+    match cache_key.get().unwrap().implementors.find(&it.def_id) {
         Some(implementors) => {
             try!(write!(w, "
                 <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 {
-    match cache_key.get().unwrap().impls.find(&it.id) {
+    match cache_key.get().unwrap().impls.find(&it.def_id.node) {
         Some(v) => {
             let mut non_trait = v.iter().filter(|p| {
                 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,
                dox: &Option<StrBuf>) -> fmt::Result {
     try!(write!(w, "<h3 class='impl'><code>impl{} ", i.generics));
-    let trait_id = match i.trait_ {
-        Some(ref ty) => {
-            try!(write!(w, "{} for ", *ty));
-            match *ty {
-                clean::ResolvedPath { did, .. } => Some(did),
-                _ => None,
-            }
-        }
-        None => None
-    };
+    match i.trait_ {
+        Some(ref ty) => try!(write!(w, "{} for ", *ty)),
+        None => {}
+    }
     try!(write!(w, "{}</code></h3>", i.for_));
     match *dox {
         Some(ref dox) => {
@@ -1753,31 +1749,34 @@ fn render_impl(w: &mut fmt::Formatter, i: &clean::Impl,
         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
     // default methods which weren't overridden in the implementation block.
-    match trait_id {
-        None => {}
-        // FIXME: this should work for non-local traits
-        Some(did) if ast_util::is_local(did) => {
+    match i.trait_ {
+        Some(clean::ResolvedPath { did, .. }) => {
             try!({
-                match cache_key.get().unwrap().traits.find(&did.node) {
-                    Some(t) => {
-                        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));
-                        }
-                    }
+                match cache_key.get().unwrap().traits.find(&did) {
+                    Some(t) => try!(render_default_methods(w, t, i)),
                     None => {}
                 }
                 Ok(())
             })
         }
-        Some(..) => {}
+        Some(..) | None => {}
     }
     try!(write!(w, "</div>"));
     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();
     for item in m.items.iter() {
         let short = shortty(item).to_static_str();
diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs
index a4491817479..a0d993dfe7d 100644
--- a/src/librustdoc/passes.rs
+++ b/src/librustdoc/passes.rs
@@ -35,7 +35,7 @@ pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult {
             fn fold_item(&mut self, i: Item) -> Option<Item> {
                 if i.is_hidden_from_doc() {
                     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
                     match i.inner {
@@ -124,7 +124,8 @@ impl<'a> fold::DocFolder for Stripper<'a> {
             clean::TraitItem(..) | clean::FunctionItem(..) |
             clean::VariantItem(..) | clean::MethodItem(..) |
             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;
                 }
             }
@@ -173,7 +174,7 @@ impl<'a> fold::DocFolder for Stripper<'a> {
         };
 
         let i = if fastreturn {
-            self.retained.insert(i.id);
+            self.retained.insert(i.def_id.node);
             return Some(i);
         } else {
             self.fold_item_recur(i)
@@ -188,7 +189,7 @@ impl<'a> fold::DocFolder for Stripper<'a> {
                            i.doc_value().is_none() => 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)
                     }
                 }
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index 6a07b7a906e..5b4a9bd27b7 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -76,6 +76,8 @@ pub fn run(input: &str,
         maybe_typed: core::NotTyped(sess),
         src: input_path,
         external_paths: RefCell::new(Some(HashMap::new())),
+        external_traits: RefCell::new(None),
+        external_typarams: RefCell::new(None),
     };
     super::ctxtkey.replace(Some(ctx));