about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-05-25 13:56:21 -0700
committerbors <bors@rust-lang.org>2014-05-25 13:56:21 -0700
commit0fca6c6a02d03491ee46ea5c4b656d114a73a53b (patch)
tree0b483c1f24d5fdfdfc5ff3c6c95bc4cde4edb8ae
parent1cf1527b91db3c605e44fe9b90fc46ecf1d04c4f (diff)
parent3100bc5b82257820051774eb4aa0447b12f3616a (diff)
downloadrust-0fca6c6a02d03491ee46ea5c4b656d114a73a53b.tar.gz
rust-0fca6c6a02d03491ee46ea5c4b656d114a73a53b.zip
auto merge of #14391 : alexcrichton/rust/more-rustdoc-inline, r=huonw
As part of the libstd facade (cc #13851), rustdoc is taught to inline documentation across crate boundaries through the usage of a `pub use` statement. This is done to allow libstd to maintain the facade that it is a standalone library with a defined public interface (allowing us to shuffle around what's underneath it).

A preview is available at http://people.mozilla.org/~acrichton/doc/std/index.html
-rw-r--r--src/doc/rustdoc.md25
-rw-r--r--src/librustc/metadata/common.rs3
-rw-r--r--src/librustc/metadata/csearch.rs7
-rw-r--r--src/librustc/metadata/decoder.rs15
-rw-r--r--src/librustc/metadata/encoder.rs26
-rw-r--r--src/librustdoc/clean/inline.rs278
-rw-r--r--src/librustdoc/clean/mod.rs (renamed from src/librustdoc/clean.rs)264
-rw-r--r--src/librustdoc/html/format.rs4
-rw-r--r--src/librustdoc/html/markdown.rs2
-rw-r--r--src/librustdoc/html/render.rs175
-rw-r--r--src/librustdoc/html/static/main.js6
-rw-r--r--src/librustdoc/passes.rs3
-rw-r--r--src/libstd/lib.rs12
-rw-r--r--src/libstd/prelude.rs91
14 files changed, 734 insertions, 177 deletions
diff --git a/src/doc/rustdoc.md b/src/doc/rustdoc.md
index 3880503c8e5..1034c776ea6 100644
--- a/src/doc/rustdoc.md
+++ b/src/doc/rustdoc.md
@@ -41,6 +41,31 @@ pub fn recalibrate() {
 # }
 ~~~
 
+Documentation can also be controlled via the `doc` attribute on items. This is
+implicitly done by the compiler when using the above form of doc comments
+(converting the slash-based comments to `#[doc]` attributes).
+
+~~~
+#[doc = "
+Calculates the factorial of a number.
+
+Given the input integer `n`, this function will calculate `n!` and return it.
+"]
+pub fn factorial(n: int) -> int { if n < 2 {1} else {n * factorial(n)} }
+# fn main() {}
+~~~
+
+The `doc` attribute can also be used to control how rustdoc emits documentation
+in some cases.
+
+```
+// Rustdoc will inline documentation of a `pub use` into this crate when the
+// `pub use` reaches across crates, but this behavior can also be disabled.
+#[doc(no_inline)]
+pub use std::option::Option;
+# fn main() {}
+```
+
 Doc comments are markdown, and are currently parsed with the
 [sundown][sundown] library. rustdoc does not yet do any fanciness such as
 referencing other items inline, like javadoc's `@see`. One exception to this
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index f30d6119339..c798118bbd0 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -206,6 +206,9 @@ pub static tag_crate_triple: uint = 0x66;
 
 pub static tag_dylib_dependency_formats: uint = 0x67;
 
+pub static tag_method_argument_names: uint = 0x8e;
+pub static tag_method_argument_name: uint = 0x8f;
+
 #[deriving(Clone, Show)]
 pub struct LinkMeta {
     pub crateid: CrateId,
diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs
index efe3633195e..d407cc04680 100644
--- a/src/librustc/metadata/csearch.rs
+++ b/src/librustc/metadata/csearch.rs
@@ -306,3 +306,10 @@ pub fn get_missing_lang_items(cstore: &cstore::CStore, cnum: ast::CrateNum)
     let cdata = cstore.get_crate_data(cnum);
     decoder::get_missing_lang_items(&*cdata)
 }
+
+pub fn get_method_arg_names(cstore: &cstore::CStore, did: ast::DefId)
+    -> Vec<String>
+{
+    let cdata = cstore.get_crate_data(did.krate);
+    decoder::get_method_arg_names(&*cdata, did.node)
+}
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index 83b24d882e5..e8be05feae8 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -1309,3 +1309,18 @@ pub fn get_missing_lang_items(cdata: Cmd)
     });
     return result;
 }
+
+pub fn get_method_arg_names(cdata: Cmd, id: ast::NodeId) -> Vec<String> {
+    let mut ret = Vec::new();
+    let method_doc = lookup_item(id, cdata.data());
+    match reader::maybe_get_doc(method_doc, tag_method_argument_names) {
+        Some(args_doc) => {
+            reader::tagged_docs(args_doc, tag_method_argument_name, |name_doc| {
+                ret.push(name_doc.as_str_slice().to_strbuf());
+                true
+            });
+        }
+        None => {}
+    }
+    return ret;
+}
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index bd333a5afe0..443a8acfb0c 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -536,6 +536,7 @@ fn encode_reexports(ecx: &EncodeContext,
 fn encode_info_for_mod(ecx: &EncodeContext,
                        ebml_w: &mut Encoder,
                        md: &Mod,
+                       attrs: &[Attribute],
                        id: NodeId,
                        path: PathElems,
                        name: Ident,
@@ -584,6 +585,7 @@ fn encode_info_for_mod(ecx: &EncodeContext,
         debug!("(encoding info for module) encoding reexports for {}", id);
         encode_reexports(ecx, ebml_w, id, path);
     }
+    encode_attributes(ebml_w, attrs);
 
     ebml_w.end_tag();
 }
@@ -774,11 +776,30 @@ fn encode_info_for_method(ecx: &EncodeContext,
         } else {
             encode_symbol(ecx, ebml_w, m.def_id.node);
         }
+        encode_method_argument_names(ebml_w, &*ast_method.decl);
     }
 
     ebml_w.end_tag();
 }
 
+fn encode_method_argument_names(ebml_w: &mut Encoder,
+                                decl: &ast::FnDecl) {
+    ebml_w.start_tag(tag_method_argument_names);
+    for arg in decl.inputs.iter() {
+        ebml_w.start_tag(tag_method_argument_name);
+        match arg.pat.node {
+            ast::PatIdent(_, ref name, _) => {
+                let name = name.segments.last().unwrap().identifier;
+                let name = token::get_ident(name);
+                ebml_w.writer.write(name.get().as_bytes());
+            }
+            _ => {}
+        }
+        ebml_w.end_tag();
+    }
+    ebml_w.end_tag();
+}
+
 fn encode_inlined_item(ecx: &EncodeContext,
                        ebml_w: &mut Encoder,
                        ii: InlinedItemRef) {
@@ -895,7 +916,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
         encode_visibility(ebml_w, vis);
         ebml_w.end_tag();
       }
-      ItemFn(_, fn_style, _, ref generics, _) => {
+      ItemFn(ref decl, fn_style, _, ref generics, _) => {
         add_to_index(item, ebml_w, index);
         ebml_w.start_tag(tag_items_data_item);
         encode_def_id(ebml_w, def_id);
@@ -911,6 +932,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
             encode_symbol(ecx, ebml_w, item.id);
         }
         encode_visibility(ebml_w, vis);
+        encode_method_argument_names(ebml_w, &**decl);
         ebml_w.end_tag();
       }
       ItemMod(ref m) => {
@@ -918,6 +940,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
         encode_info_for_mod(ecx,
                             ebml_w,
                             m,
+                            item.attrs.as_slice(),
                             item.id,
                             path,
                             item.ident,
@@ -1317,6 +1340,7 @@ fn encode_info_for_items(ecx: &EncodeContext,
     encode_info_for_mod(ecx,
                         ebml_w,
                         &krate.module,
+                        &[],
                         CRATE_NODE_ID,
                         ast_map::Values([].iter()).chain(None),
                         syntax::parse::token::special_idents::invalid,
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
new file mode 100644
index 00000000000..dd5fddca0db
--- /dev/null
+++ b/src/librustdoc/clean/inline.rs
@@ -0,0 +1,278 @@
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Support for inlining external documentation into the current AST.
+
+use syntax::ast;
+use syntax::ast_util;
+use syntax::attr::AttrMetaMethods;
+
+use rustc::metadata::csearch;
+use rustc::metadata::decoder;
+use rustc::middle::ty;
+
+use core;
+use doctree;
+use clean;
+
+use super::Clean;
+
+/// Attempt to inline the definition of a local node id into this AST.
+///
+/// This function will fetch the definition of the id specified, and if it is
+/// from another crate it will attempt to inline the documentation from the
+/// other crate into this crate.
+///
+/// This is primarily used for `pub use` statements which are, in general,
+/// implementation details. Inlining the documentation should help provide a
+/// better experience when reading the documentation in this use case.
+///
+/// The returned value is `None` if the `id` could not be inlined, and `Some`
+/// of a vector of items if it was successfully expanded.
+pub fn try_inline(id: ast::NodeId) -> Option<Vec<clean::Item>> {
+    let cx = ::ctxtkey.get().unwrap();
+    let tcx = match cx.maybe_typed {
+        core::Typed(ref tycx) => tycx,
+        core::NotTyped(_) => return None,
+    };
+    let def = match tcx.def_map.borrow().find(&id) {
+        Some(def) => *def,
+        None => return None,
+    };
+    let did = ast_util::def_id_of_def(def);
+    if ast_util::is_local(did) { return None }
+    try_inline_def(&**cx, tcx, def)
+}
+
+fn try_inline_def(cx: &core::DocContext,
+                  tcx: &ty::ctxt,
+                  def: ast::Def) -> Option<Vec<clean::Item>> {
+    let mut ret = Vec::new();
+    let did = ast_util::def_id_of_def(def);
+    let inner = match def {
+        ast::DefTrait(did) => {
+            record_extern_fqn(cx, did, clean::TypeTrait);
+            clean::TraitItem(build_external_trait(tcx, did))
+        }
+        ast::DefFn(did, style) => {
+            record_extern_fqn(cx, did, clean::TypeFunction);
+            clean::FunctionItem(build_external_function(tcx, did, style))
+        }
+        ast::DefStruct(did) => {
+            record_extern_fqn(cx, did, clean::TypeStruct);
+            ret.extend(build_impls(tcx, did).move_iter());
+            clean::StructItem(build_struct(tcx, did))
+        }
+        ast::DefTy(did) => {
+            record_extern_fqn(cx, did, clean::TypeEnum);
+            ret.extend(build_impls(tcx, did).move_iter());
+            build_type(tcx, did)
+        }
+        // Assume that the enum type is reexported next to the variant, and
+        // variants don't show up in documentation specially.
+        ast::DefVariant(..) => return Some(Vec::new()),
+        ast::DefMod(did) => {
+            record_extern_fqn(cx, did, clean::TypeModule);
+            clean::ModuleItem(build_module(cx, tcx, did))
+        }
+        _ => return None,
+    };
+    let fqn = csearch::get_item_path(tcx, did);
+    ret.push(clean::Item {
+        source: clean::Span::empty(),
+        name: Some(fqn.last().unwrap().to_str().to_strbuf()),
+        attrs: load_attrs(tcx, did),
+        inner: inner,
+        visibility: Some(ast::Public),
+        def_id: did,
+    });
+    Some(ret)
+}
+
+pub fn load_attrs(tcx: &ty::ctxt, did: ast::DefId) -> Vec<clean::Attribute> {
+    let mut attrs = Vec::new();
+    csearch::get_item_attrs(&tcx.sess.cstore, did, |v| {
+        attrs.extend(v.move_iter().map(|mut a| {
+            // FIXME this isn't quite always true, it's just true about 99% of
+            //       the time when dealing with documentation. For example,
+            //       this would treat doc comments of the form `#[doc = "foo"]`
+            //       incorrectly.
+            if a.name().get() == "doc" && a.value_str().is_some() {
+                a.node.is_sugared_doc = true;
+            }
+            a.clean()
+        }));
+    });
+    attrs
+}
+
+/// Record an external fully qualified name in the external_paths cache.
+///
+/// These names are used later on by HTML rendering to generate things like
+/// source links back to the original item.
+pub fn record_extern_fqn(cx: &core::DocContext,
+                         did: ast::DefId,
+                         kind: clean::TypeKind) {
+    match cx.maybe_typed {
+        core::Typed(ref tcx) => {
+            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, kind));
+        }
+        core::NotTyped(..) => {}
+    }
+}
+
+pub fn build_external_trait(tcx: &ty::ctxt, did: ast::DefId) -> clean::Trait {
+    let def = ty::lookup_trait_def(tcx, did);
+    let methods = ty::trait_methods(tcx, did);
+    clean::Trait {
+        generics: def.generics.clean(),
+        methods: methods.iter().map(|i| i.clean()).collect(),
+        parents: Vec::new(), // FIXME: this is likely wrong
+    }
+}
+
+fn build_external_function(tcx: &ty::ctxt,
+                           did: ast::DefId,
+                           style: ast::FnStyle) -> clean::Function {
+    let t = ty::lookup_item_type(tcx, did);
+    clean::Function {
+        decl: match ty::get(t.ty).sty {
+            ty::ty_bare_fn(ref f) => (did, &f.sig).clean(),
+            _ => fail!("bad function"),
+        },
+        generics: t.generics.clean(),
+        fn_style: style,
+    }
+}
+
+fn build_struct(tcx: &ty::ctxt, did: ast::DefId) -> clean::Struct {
+    use syntax::parse::token::special_idents::unnamed_field;
+
+    let t = ty::lookup_item_type(tcx, did);
+    let fields = ty::lookup_struct_fields(tcx, did);
+
+    clean::Struct {
+        struct_type: match fields.as_slice() {
+            [] => doctree::Unit,
+            [ref f] if f.name == unnamed_field.name => doctree::Newtype,
+            [ref f, ..] if f.name == unnamed_field.name => doctree::Tuple,
+            _ => doctree::Plain,
+        },
+        generics: t.generics.clean(),
+        fields: fields.iter().map(|f| f.clean()).collect(),
+        fields_stripped: false,
+    }
+}
+
+fn build_type(tcx: &ty::ctxt, did: ast::DefId) -> clean::ItemEnum {
+    let t = ty::lookup_item_type(tcx, did);
+    match ty::get(t.ty).sty {
+        ty::ty_enum(edid, _) => {
+            return clean::EnumItem(clean::Enum {
+                generics: t.generics.clean(),
+                variants_stripped: false,
+                variants: ty::enum_variants(tcx, edid).clean(),
+            })
+        }
+        _ => {}
+    }
+
+    clean::TypedefItem(clean::Typedef {
+        type_: t.ty.clean(),
+        generics: t.generics.clean(),
+    })
+}
+
+fn build_impls(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().find(&did) {
+        None => {}
+        Some(i) => {
+            impls.extend(i.borrow().iter().map(|&did| { build_impl(tcx, did) }));
+        }
+    }
+
+    impls
+}
+
+fn build_impl(tcx: &ty::ctxt, did: ast::DefId) -> clean::Item {
+    let associated_trait = csearch::get_impl_trait(tcx, did);
+    let attrs = load_attrs(tcx, did);
+    let ty = ty::lookup_item_type(tcx, did);
+    let methods = csearch::get_impl_methods(&tcx.sess.cstore, did).iter().map(|did| {
+        let mut item = match ty::method(tcx, *did).clean() {
+            clean::Provided(item) => item,
+            clean::Required(item) => item,
+        };
+        item.inner = match item.inner.clone() {
+            clean::TyMethodItem(clean::TyMethod {
+                fn_style, decl, self_, generics
+            }) => {
+                clean::MethodItem(clean::Method {
+                    fn_style: fn_style,
+                    decl: decl,
+                    self_: self_,
+                    generics: generics,
+                })
+            }
+            _ => fail!("not a tymethod"),
+        };
+        item
+    }).collect();
+    clean::Item {
+        inner: clean::ImplItem(clean::Impl {
+            derived: clean::detect_derived(attrs.as_slice()),
+            trait_: associated_trait.clean().map(|bound| {
+                match bound {
+                    clean::TraitBound(ty) => ty,
+                    clean::RegionBound => unreachable!(),
+                }
+            }),
+            for_: ty.ty.clean(),
+            generics: ty.generics.clean(),
+            methods: methods,
+        }),
+        source: clean::Span::empty(),
+        name: None,
+        attrs: attrs,
+        visibility: Some(ast::Inherited),
+        def_id: did,
+    }
+}
+
+fn build_module(cx: &core::DocContext, tcx: &ty::ctxt,
+                did: ast::DefId) -> clean::Module {
+    let mut items = Vec::new();
+
+    // FIXME: this doesn't handle reexports inside the module itself.
+    //        Should they be handled?
+    csearch::each_child_of_item(&tcx.sess.cstore, did, |def, _, _| {
+        match def {
+            decoder::DlDef(def) => {
+                match try_inline_def(cx, tcx, def) {
+                    Some(i) => items.extend(i.move_iter()),
+                    None => {}
+                }
+            }
+            decoder::DlImpl(did) => items.push(build_impl(tcx, did)),
+            decoder::DlField => fail!("unimplemented field"),
+        }
+    });
+
+    clean::Module {
+        items: items,
+        is_crate: false,
+    }
+}
diff --git a/src/librustdoc/clean.rs b/src/librustdoc/clean/mod.rs
index 68af35acdbe..f0f68426425 100644
--- a/src/librustdoc/clean.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -15,7 +15,7 @@ use syntax;
 use syntax::ast;
 use syntax::ast_util;
 use syntax::attr;
-use syntax::attr::AttributeMethods;
+use syntax::attr::{AttributeMethods, AttrMetaMethods};
 use syntax::codemap::Pos;
 use syntax::parse::token::InternedString;
 use syntax::parse::token;
@@ -27,7 +27,7 @@ use rustc::metadata::csearch;
 use rustc::metadata::decoder;
 use rustc::middle::ty;
 
-use std::string::String;
+use std::rc::Rc;
 
 use core;
 use doctree;
@@ -37,6 +37,8 @@ use visit_ast;
 /// Increment this when the `Crate` and related structures change.
 pub static SCHEMA_VERSION: &'static str = "0.8.2";
 
+mod inline;
+
 pub trait Clean<T> {
     fn clean(&self) -> T;
 }
@@ -53,6 +55,12 @@ impl<T: Clean<U>, U> Clean<U> for @T {
     }
 }
 
+impl<T: Clean<U>, U> Clean<U> for Rc<T> {
+    fn clean(&self) -> U {
+        (**self).clean()
+    }
+}
+
 impl<T: Clean<U>, U> Clean<Option<U>> for Option<T> {
     fn clean(&self) -> Option<U> {
         match self {
@@ -250,7 +258,8 @@ impl Clean<Item> for doctree::Module {
             self.statics.clean().move_iter().collect(),
             self.traits.clean().move_iter().collect(),
             self.impls.clean().move_iter().collect(),
-            self.view_items.clean().move_iter().collect(),
+            self.view_items.clean().move_iter()
+                           .flat_map(|s| s.move_iter()).collect(),
             self.macros.clean().move_iter().collect()
         );
 
@@ -336,6 +345,14 @@ impl attr::AttrMetaMethods for Attribute {
         None
     }
 }
+impl<'a> attr::AttrMetaMethods for &'a Attribute {
+    fn name(&self) -> InternedString { (**self).name() }
+    fn value_str(&self) -> Option<InternedString> { (**self).value_str() }
+    fn meta_item_list<'a>(&'a self) -> Option<&'a [@ast::MetaItem]> { None }
+    fn name_str_pair(&self) -> Option<(InternedString, InternedString)> {
+        None
+    }
+}
 
 #[deriving(Clone, Encodable, Decodable)]
 pub struct TyParam {
@@ -473,7 +490,7 @@ impl Clean<Option<Vec<TyParamBound>>> for ty::substs {
     }
 }
 
-#[deriving(Clone, Encodable, Decodable)]
+#[deriving(Clone, Encodable, Decodable, Eq)]
 pub struct Lifetime(String);
 
 impl Lifetime {
@@ -614,7 +631,7 @@ impl Clean<Item> for ast::TypeMethod {
     }
 }
 
-#[deriving(Clone, Encodable, Decodable)]
+#[deriving(Clone, Encodable, Decodable, Eq)]
 pub enum SelfTy {
     SelfStatic,
     SelfValue,
@@ -707,18 +724,32 @@ impl Clean<FnDecl> for ast::FnDecl {
     }
 }
 
-impl Clean<FnDecl> for ty::FnSig {
+impl<'a> Clean<FnDecl> for (ast::DefId, &'a ty::FnSig) {
     fn clean(&self) -> FnDecl {
+        let cx = super::ctxtkey.get().unwrap();
+        let tcx = match cx.maybe_typed {
+            core::Typed(ref tcx) => tcx,
+            core::NotTyped(_) => unreachable!(),
+        };
+        let (did, sig) = *self;
+        let mut names = if did.node != 0 {
+            csearch::get_method_arg_names(&tcx.sess.cstore, did).move_iter()
+        } else {
+            Vec::new().move_iter()
+        }.peekable();
+        if names.peek().map(|s| s.as_slice()) == Some("self") {
+            let _ = names.next();
+        }
         FnDecl {
-            output: self.output.clean(),
+            output: sig.output.clean(),
             cf: Return,
-            attrs: Vec::new(), // FIXME: this is likely wrong
+            attrs: Vec::new(),
             inputs: Arguments {
-                values: self.inputs.iter().map(|t| {
+                values: sig.inputs.iter().map(|t| {
                     Argument {
                         type_: t.clean(),
                         id: 0,
-                        name: "".to_strbuf(), // FIXME: where are the names?
+                        name: names.next().unwrap_or("".to_strbuf()),
                     }
                 }).collect(),
             },
@@ -830,12 +861,8 @@ impl Clean<TraitMethod> for ty::Method {
         let cx = super::ctxtkey.get().unwrap();
         let tcx = match cx.maybe_typed {
             core::Typed(ref tcx) => tcx,
-            core::NotTyped(_) => fail!(),
+            core::NotTyped(_) => unreachable!(),
         };
-        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 => {
@@ -857,20 +884,18 @@ impl Clean<TraitMethod> for ty::Method {
                 (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,
-            },
+            attrs: inline::load_attrs(tcx, self.def_id),
+            source: Span::empty(),
             inner: TyMethodItem(TyMethod {
                 fn_style: self.fty.fn_style,
                 generics: self.generics.clean(),
                 self_: self_,
-                decl: sig.clean(),
+                decl: (self.def_id, &sig).clean(),
             })
         })
     }
@@ -987,13 +1012,13 @@ impl Clean<Type> for ty::t {
                 generics: Generics {
                     lifetimes: Vec::new(), type_params: Vec::new()
                 },
-                decl: fty.sig.clean(),
+                decl: (ast_util::local_def(0), &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(),
+                    decl: (ast_util::local_def(0), &fty.sig).clean(),
                     onceness: fty.onceness,
                     fn_style: fty.fn_style,
                     bounds: fty.bounds.iter().map(|i| i.clean()).collect(),
@@ -1009,7 +1034,7 @@ impl Clean<Type> for ty::t {
                 let cx = super::ctxtkey.get().unwrap();
                 let tcx = match cx.maybe_typed {
                     core::Typed(ref tycx) => tycx,
-                    core::NotTyped(_) => fail!(),
+                    core::NotTyped(_) => unreachable!(),
                 };
                 let fqn = csearch::get_item_path(tcx, did);
                 let fqn: Vec<String> = fqn.move_iter().map(|i| {
@@ -1073,6 +1098,31 @@ impl Clean<Item> for ast::StructField {
     }
 }
 
+impl Clean<Item> for ty::field_ty {
+    fn clean(&self) -> Item {
+        use syntax::parse::token::special_idents::unnamed_field;
+        let name = if self.name == unnamed_field.name {
+            None
+        } else {
+            Some(self.name)
+        };
+        let cx = super::ctxtkey.get().unwrap();
+        let tcx = match cx.maybe_typed {
+            core::Typed(ref tycx) => tycx,
+            core::NotTyped(_) => unreachable!(),
+        };
+        let ty = ty::lookup_item_type(tcx, self.id);
+        Item {
+            name: name.clean(),
+            attrs: inline::load_attrs(tcx, self.id),
+            source: Span::empty(),
+            visibility: Some(self.vis),
+            def_id: self.id,
+            inner: StructFieldItem(TypedStructField(ty.ty.clean())),
+        }
+    }
+}
+
 pub type Visibility = ast::Visibility;
 
 impl Clean<Option<Visibility>> for ast::Visibility {
@@ -1171,6 +1221,53 @@ impl Clean<Item> for doctree::Variant {
     }
 }
 
+impl Clean<Item> for ty::VariantInfo {
+    fn clean(&self) -> Item {
+        // use syntax::parse::token::special_idents::unnamed_field;
+        let cx = super::ctxtkey.get().unwrap();
+        let tcx = match cx.maybe_typed {
+            core::Typed(ref tycx) => tycx,
+            core::NotTyped(_) => fail!("tcx not present"),
+        };
+        let kind = match self.arg_names.as_ref().map(|s| s.as_slice()) {
+            None | Some([]) if self.args.len() == 0 => CLikeVariant,
+            None | Some([]) => {
+                TupleVariant(self.args.iter().map(|t| t.clean()).collect())
+            }
+            Some(s) => {
+                StructVariant(VariantStruct {
+                    struct_type: doctree::Plain,
+                    fields_stripped: false,
+                    fields: s.iter().zip(self.args.iter()).map(|(name, ty)| {
+                        Item {
+                            source: Span::empty(),
+                            name: Some(name.clean()),
+                            attrs: Vec::new(),
+                            visibility: Some(ast::Public),
+                            // FIXME: this is not accurate, we need an id for
+                            //        the specific field but we're using the id
+                            //        for the whole variant. Nothing currently
+                            //        uses this so we should be good for now.
+                            def_id: self.id,
+                            inner: StructFieldItem(
+                                TypedStructField(ty.clean())
+                            )
+                        }
+                    }).collect()
+                })
+            }
+        };
+        Item {
+            name: Some(self.name.clean()),
+            attrs: inline::load_attrs(tcx, self.id),
+            source: Span::empty(),
+            visibility: Some(ast::Public),
+            def_id: self.id,
+            inner: VariantItem(Variant { kind: kind }),
+        }
+    }
+}
+
 #[deriving(Clone, Encodable, Decodable)]
 pub enum VariantKind {
     CLikeVariant,
@@ -1202,6 +1299,16 @@ pub struct Span {
     pub hicol: uint,
 }
 
+impl Span {
+    fn empty() -> Span {
+        Span {
+            filename: "".to_strbuf(),
+            loline: 0, locol: 0,
+            hiline: 0, hicol: 0,
+        }
+    }
+}
+
 impl Clean<Span> for syntax::codemap::Span {
     fn clean(&self) -> Span {
         let ctxt = super::ctxtkey.get().unwrap();
@@ -1273,6 +1380,12 @@ impl Clean<String> for ast::Ident {
     }
 }
 
+impl Clean<String> for ast::Name {
+    fn clean(&self) -> String {
+        token::get_name(*self).get().to_strbuf()
+    }
+}
+
 #[deriving(Clone, Encodable, Decodable)]
 pub struct Typedef {
     pub type_: Type,
@@ -1345,7 +1458,7 @@ impl Clean<Item> for doctree::Static {
     }
 }
 
-#[deriving(Show, Clone, Encodable, Decodable)]
+#[deriving(Show, Clone, Encodable, Decodable, Eq)]
 pub enum Mutability {
     Mutable,
     Immutable,
@@ -1369,19 +1482,12 @@ pub struct Impl {
     pub derived: bool,
 }
 
+fn detect_derived<M: AttrMetaMethods>(attrs: &[M]) -> bool {
+    attr::contains_name(attrs, "automatically_derived")
+}
+
 impl Clean<Item> for doctree::Impl {
     fn clean(&self) -> Item {
-        let mut derived = false;
-        for attr in self.attrs.iter() {
-            match attr.node.value.node {
-                ast::MetaWord(ref s) => {
-                    if s.get() == "automatically_derived" {
-                        derived = true;
-                    }
-                }
-                _ => {}
-            }
-        }
         Item {
             name: None,
             attrs: self.attrs.clean(),
@@ -1393,7 +1499,7 @@ impl Clean<Item> for doctree::Impl {
                 trait_: self.trait_.clean(),
                 for_: self.for_.clean(),
                 methods: self.methods.clean(),
-                derived: derived,
+                derived: detect_derived(self.attrs.as_slice()),
             }),
         }
     }
@@ -1404,18 +1510,63 @@ pub struct ViewItem {
     pub inner: ViewItemInner,
 }
 
-impl Clean<Item> for ast::ViewItem {
-    fn clean(&self) -> Item {
-        Item {
-            name: None,
-            attrs: self.attrs.clean().move_iter().collect(),
-            source: self.span.clean(),
-            def_id: ast_util::local_def(0),
-            visibility: self.vis.clean(),
-            inner: ViewItemItem(ViewItem {
-                inner: self.node.clean()
-            }),
+impl Clean<Vec<Item>> for ast::ViewItem {
+    fn clean(&self) -> Vec<Item> {
+        // We consider inlining the documentation of `pub use` statments, but we
+        // forcefully don't inline if this is not public or if the
+        // #[doc(no_inline)] attribute is present.
+        let denied = self.vis != ast::Public || self.attrs.iter().any(|a| {
+            a.name().get() == "doc" && match a.meta_item_list() {
+                Some(l) => attr::contains_name(l, "no_inline"),
+                None => false,
+            }
+        });
+        let convert = |node: &ast::ViewItem_| {
+            Item {
+                name: None,
+                attrs: self.attrs.clean().move_iter().collect(),
+                source: self.span.clean(),
+                def_id: ast_util::local_def(0),
+                visibility: self.vis.clean(),
+                inner: ViewItemItem(ViewItem { inner: node.clean() }),
+            }
+        };
+        let mut ret = Vec::new();
+        match self.node {
+            ast::ViewItemUse(ref path) if !denied => {
+                match path.node {
+                    ast::ViewPathGlob(..) => ret.push(convert(&self.node)),
+                    ast::ViewPathList(ref a, ref list, ref b) => {
+                        // Attempt to inline all reexported items, but be sure
+                        // to keep any non-inlineable reexports so they can be
+                        // listed in the documentation.
+                        let remaining = list.iter().filter(|path| {
+                            match inline::try_inline(path.node.id) {
+                                Some(items) => {
+                                    ret.extend(items.move_iter()); false
+                                }
+                                None => true,
+                            }
+                        }).map(|a| a.clone()).collect::<Vec<ast::PathListIdent>>();
+                        if remaining.len() > 0 {
+                            let path = ast::ViewPathList(a.clone(),
+                                                         remaining,
+                                                         b.clone());
+                            let path = syntax::codemap::dummy_spanned(path);
+                            ret.push(convert(&ast::ViewItemUse(@path)));
+                        }
+                    }
+                    ast::ViewPathSimple(_, _, id) => {
+                        match inline::try_inline(id) {
+                            Some(items) => ret.extend(items.move_iter()),
+                            None => ret.push(convert(&self.node)),
+                        }
+                    }
+                }
+            }
+            ref n => ret.push(convert(n)),
         }
+        return ret;
     }
 }
 
@@ -1630,13 +1781,10 @@ fn register_def(cx: &core::DocContext, def: ast::Def) -> ast::DefId {
         core::Typed(ref t) => t,
         core::NotTyped(_) => return did
     };
-    let fqn = csearch::get_item_path(tcx, did);
-    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));
+    inline::record_extern_fqn(cx, did, kind);
     match kind {
         TypeTrait => {
-            let t = build_external_trait(tcx, did);
+            let t = inline::build_external_trait(tcx, did);
             cx.external_traits.borrow_mut().get_mut_ref().insert(did, t);
         }
         _ => {}
@@ -1644,16 +1792,6 @@ fn register_def(cx: &core::DocContext, def: ast::Def) -> ast::DefId {
     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,
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index e14be8ac6fd..9043ffd10ba 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -99,7 +99,7 @@ impl fmt::Show for clean::TyParamBound {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
             clean::RegionBound => {
-                f.write("::".as_bytes())
+                f.write("'static".as_bytes())
             }
             clean::TraitBound(ref ty) => {
                 write!(f, "{}", *ty)
@@ -150,7 +150,7 @@ fn resolved_path(w: &mut fmt::Formatter, did: ast::DefId, p: &clean::Path,
                  print_all: bool) -> fmt::Result {
     path(w, p, print_all,
         |cache, loc| {
-            if ast_util::is_local(did) {
+            if ast_util::is_local(did) || cache.paths.contains_key(&did) {
                 Some(("../".repeat(loc.len())).to_strbuf())
             } else {
                 match *cache.extern_locations.get(&did.krate) {
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 7b8514ab38f..f49b7f3e989 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -144,6 +144,8 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
     extern fn block(ob: *mut hoedown_buffer, text: *hoedown_buffer,
                     lang: *hoedown_buffer, opaque: *mut libc::c_void) {
         unsafe {
+            if text.is_null() { return }
+
             let opaque = opaque as *mut hoedown_html_renderer_state;
             let my_opaque: &MyOpaque = &*((*opaque).opaque as *MyOpaque);
             slice::raw::buf_as_slice((*text).data, (*text).size as uint, |text| {
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 53e271dafa2..d601d2ae957 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -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<String>)>>,
+    pub impls: HashMap<ast::DefId, Vec<(clean::Impl, Option<String>)>>,
 
     /// 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
@@ -141,6 +141,10 @@ pub struct Cache {
     /// necessary.
     pub paths: HashMap<ast::DefId, (Vec<String>, ItemType)>,
 
+    /// Similar to `paths`, but only holds external paths. This is only used for
+    /// generating explicit hyperlinks to other crates.
+    pub external_paths: HashMap<ast::DefId, Vec<String>>,
+
     /// This map contains information about all known traits of this crate.
     /// Implementations of a crate should inherit the documentation of the
     /// parent trait if no extra documentation is specified, and default methods
@@ -157,9 +161,9 @@ pub struct Cache {
 
     // Private fields only used when initially crawling a crate to build a cache
 
-    stack: Vec<String> ,
-    parent_stack: Vec<ast::NodeId> ,
-    search_index: Vec<IndexItem> ,
+    stack: Vec<String>,
+    parent_stack: Vec<ast::DefId>,
+    search_index: Vec<IndexItem>,
     privmod: bool,
     public_items: NodeSet,
 
@@ -198,7 +202,7 @@ struct IndexItem {
     name: String,
     path: String,
     desc: String,
-    parent: Option<ast::NodeId>,
+    parent: Option<ast::DefId>,
 }
 
 // TLS keys used to carry information around during rendering.
@@ -249,7 +253,8 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
     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: HashMap<ast::DefId, (Vec<String>, ItemType)> =
+      analysis.as_ref().map(|a| {
         let paths = a.external_paths.borrow_mut().take_unwrap();
         paths.move_iter().map(|(k, (v, t))| {
             (k, (v, match t {
@@ -265,6 +270,8 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
     }).unwrap_or(HashMap::new());
     let mut cache = Cache {
         impls: HashMap::new(),
+        external_paths: paths.iter().map(|(&k, &(ref v, _))| (k, v.clone()))
+                             .collect(),
         paths: paths,
         implementors: HashMap::new(),
         stack: Vec::new(),
@@ -302,7 +309,7 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
                         path: fqp.slice_to(fqp.len() - 1).connect("::")
                                                          .to_strbuf(),
                         desc: shorter(item.doc_value()).to_strbuf(),
-                        parent: Some(pid),
+                        parent: Some(did),
                     });
                 },
                 None => {}
@@ -360,9 +367,8 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
 
         try!(write!(&mut w, r#"],"paths":["#));
 
-        for (i, &nodeid) in pathid_to_nodeid.iter().enumerate() {
-            let def = ast_util::local_def(nodeid);
-            let &(ref fqp, short) = cache.paths.find(&def).unwrap();
+        for (i, &did) in pathid_to_nodeid.iter().enumerate() {
+            let &(ref fqp, short) = cache.paths.find(&did).unwrap();
             if i > 0 {
                 try!(write!(&mut w, ","));
             }
@@ -497,13 +503,15 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
             seen: HashSet::new(),
             cx: &mut cx,
         };
+        // skip all invalid spans
+        folder.seen.insert("".to_strbuf());
         krate = folder.fold_crate(krate);
     }
 
     for &(n, ref e) in krate.externs.iter() {
         cache.extern_locations.insert(n, extern_location(e, &cx.dst));
         let did = ast::DefId { krate: n, node: ast::CRATE_NODE_ID };
-        cache.paths.insert(did, (Vec::new(), item_type::Module));
+        cache.paths.insert(did, (vec![e.name.to_strbuf()], item_type::Module));
     }
 
     // And finally render the whole crate's documentation
@@ -730,14 +738,13 @@ impl DocFolder for Cache {
                     clean::VariantItem(..) => {
                         (Some(*self.parent_stack.last().unwrap()),
                          Some(self.stack.slice_to(self.stack.len() - 1)))
-
                     }
                     clean::MethodItem(..) => {
                         if self.parent_stack.len() == 0 {
                             (None, None)
                         } else {
                             let last = self.parent_stack.last().unwrap();
-                            let did = ast_util::local_def(*last);
+                            let did = *last;
                             let path = match self.paths.find(&did) {
                                 Some(&(_, item_type::Trait)) =>
                                     Some(self.stack.slice_to(self.stack.len() - 1)),
@@ -766,9 +773,11 @@ impl DocFolder for Cache {
                         });
                     }
                     (Some(parent), None) if !self.privmod => {
-                        // We have a parent, but we don't know where they're
-                        // defined yet. Wait for later to index this item.
-                        self.orphan_methods.push((parent, item.clone()))
+                        if ast_util::is_local(parent) {
+                            // We have a parent, but we don't know where they're
+                            // defined yet. Wait for later to index this item.
+                            self.orphan_methods.push((parent.node, item.clone()))
+                        }
                     }
                     _ => {}
                 }
@@ -789,19 +798,18 @@ impl DocFolder for Cache {
             clean::TypedefItem(..) | clean::TraitItem(..) |
             clean::FunctionItem(..) | clean::ModuleItem(..) |
             clean::ForeignFunctionItem(..) => {
-                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)));
-                    }
+                // 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) ||
+                   !ast_util::is_local(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
@@ -817,20 +825,14 @@ impl DocFolder for Cache {
         // Maintain the parent stack
         let parent_pushed = match item.inner {
             clean::TraitItem(..) | clean::EnumItem(..) | clean::StructItem(..) => {
-                if ast_util::is_local(item.def_id) {
-                    self.parent_stack.push(item.def_id.node);
-                }
+                self.parent_stack.push(item.def_id);
                 true
             }
             clean::ImplItem(ref i) => {
                 match i.for_ {
                     clean::ResolvedPath{ did, .. } => {
-                        if ast_util::is_local(did) {
-                            self.parent_stack.push(did.node);
-                            true
-                        } else {
-                            false
-                        }
+                        self.parent_stack.push(did);
+                        true
                     }
                     _ => false
                 }
@@ -845,10 +847,8 @@ impl DocFolder for Cache {
                 match item {
                     clean::Item{ attrs, inner: clean::ImplItem(i), .. } => {
                         match i.for_ {
-                            clean::ResolvedPath { did, .. }
-                                if ast_util::is_local(did) =>
-                            {
-                                let v = self.impls.find_or_insert_with(did.node, |_| {
+                            clean::ResolvedPath { did, .. } => {
+                                let v = self.impls.find_or_insert_with(did, |_| {
                                     Vec::new()
                                 });
                                 // extract relevant documentation for this impl
@@ -1041,23 +1041,62 @@ impl<'a> Item<'a> {
         }
     }
 
-    fn link(&self) -> String {
-        let mut path = Vec::new();
-        clean_srcpath(self.item.source.filename.as_bytes(), |component| {
-            path.push(component.to_owned());
-        });
-        let href = if self.item.source.loline == self.item.source.hiline {
-            format_strbuf!("{}", self.item.source.loline)
+    /// Generate a url appropriate for an `href` attribute back to the source of
+    /// this item.
+    ///
+    /// The url generated, when clicked, will redirect the browser back to the
+    /// original source code.
+    ///
+    /// If `None` is returned, then a source link couldn't be generated. This
+    /// may happen, for example, with externally inlined items where the source
+    /// of their crate documentation isn't known.
+    fn href(&self) -> Option<String> {
+        // If this item is part of the local crate, then we're guaranteed to
+        // know the span, so we plow forward and generate a proper url. The url
+        // has anchors for the line numbers that we're linking to.
+        if ast_util::is_local(self.item.def_id) {
+            let mut path = Vec::new();
+            clean_srcpath(self.item.source.filename.as_bytes(), |component| {
+                path.push(component.to_owned());
+            });
+            let href = if self.item.source.loline == self.item.source.hiline {
+                format!("{}", self.item.source.loline)
+            } else {
+                format!("{}-{}",
+                        self.item.source.loline,
+                        self.item.source.hiline)
+            };
+            Some(format!("{root}src/{krate}/{path}.html\\#{href}",
+                         root = self.cx.root_path,
+                         krate = self.cx.layout.krate,
+                         path = path.connect("/"),
+                         href = href))
+
+        // If this item is not part of the local crate, then things get a little
+        // trickier. We don't actually know the span of the external item, but
+        // we know that the documentation on the other end knows the span!
+        //
+        // In this case, we generate a link to the *documentation* for this type
+        // in the original crate. There's an extra URL parameter which says that
+        // we want to go somewhere else, and the JS on the destination page will
+        // pick it up and instantly redirect the browser to the source code.
+        //
+        // If we don't know where the external documentation for this crate is
+        // located, then we return `None`.
         } else {
-            format_strbuf!("{}-{}",
-                           self.item.source.loline,
-                           self.item.source.hiline)
-        };
-        format_strbuf!("{root}src/{krate}/{path}.html\\#{href}",
-                       root = self.cx.root_path,
-                       krate = self.cx.layout.krate,
-                       path = path.connect("/"),
-                       href = href)
+            let cache = cache_key.get().unwrap();
+            let path = cache.external_paths.get(&self.item.def_id);
+            let root = match *cache.extern_locations.get(&self.item.def_id.krate) {
+                Remote(ref s) => s.to_strbuf(),
+                Local => format!("{}/..", self.cx.root_path),
+                Unknown => return None,
+            };
+            Some(format!("{root}/{path}/{file}?gotosrc={goto}",
+                         root = root,
+                         path = path.slice_to(path.len() - 1).connect("/"),
+                         file = item_path(self.item),
+                         goto = self.item.def_id.node))
+        }
     }
 }
 
@@ -1105,9 +1144,21 @@ impl<'a> fmt::Show for Item<'a> {
         }
 
         // Write `src` tag
+        //
+        // When this item is part of a `pub use` in a downstream crate, the
+        // [src] link in the downstream documentation will actually come back to
+        // this page, and this link will be auto-clicked. The `id` attribute is
+        // used to find the link to auto-click.
         if self.cx.include_sources {
-            try!(write!(fmt, "<a class='source' href='{}'>[src]</a>",
-                        self.link()));
+            match self.href() {
+                Some(l) => {
+                    try!(write!(fmt,
+                                "<a class='source' id='src-{}' \
+                                    href='{}'>[src]</a>",
+                                self.item.def_id.node, l));
+                }
+                None => {}
+            }
         }
         try!(write!(fmt, "</h1>\n"));
 
@@ -1266,7 +1317,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
                         try!(write!(f, "<code> = </code>"));
                         if s.contains("\n") {
                             write!(f, "<a href='{}'>[definition]</a>",
-                                   item.link())
+                                   item.href())
                         } else {
                             write!(f, "<code>{}</code>", s.as_slice())
                         }
@@ -1672,7 +1723,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.def_id.node) {
+    match cache_key.get().unwrap().impls.find(&it.def_id) {
         Some(v) => {
             let mut non_trait = v.iter().filter(|p| {
                 p.ref0().trait_.is_none()
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index c88e6aa5868..2fb824653d3 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -675,4 +675,10 @@
     if (window.pending_implementors) {
         window.register_implementors(window.pending_implementors);
     }
+
+    // See documentaiton in html/render.rs for what this is doing.
+    var query = getQueryStringParams();
+    if (query['gotosrc']) {
+        window.location = $('#src-' + query['gotosrc']).attr('href');
+    }
 }());
diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs
index 16c319d6363..390f81642e6 100644
--- a/src/librustdoc/passes.rs
+++ b/src/librustdoc/passes.rs
@@ -152,7 +152,8 @@ impl<'a> fold::DocFolder for Stripper<'a> {
             clean::ImplItem(clean::Impl{
                 for_: clean::ResolvedPath{ did, .. }, ..
             }) => {
-                if !self.exported_items.contains(&did.node) {
+                if ast_util::is_local(did) &&
+                   !self.exported_items.contains(&did.node) {
                     return None;
                 }
             }
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index 0d42e1743f5..1786cc8062e 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -16,10 +16,10 @@
 //!
 //! ## Intrinsic types and operations
 //!
-//! The [`ptr`](../core/ptr/index.html) and [`mem`](../core/mem/index.html)
+//! The [`ptr`](ptr/index.html) and [`mem`](mem/index.html)
 //! modules deal with unsafe pointers and memory manipulation.
-//! [`kinds`](../core/kinds/index.html) defines the special built-in traits,
-//! and [`raw`](../core/raw/index.html) the runtime representation of Rust types.
+//! [`kinds`](kinds/index.html) defines the special built-in traits,
+//! and [`raw`](raw/index.html) the runtime representation of Rust types.
 //! These are some of the lowest-level building blocks in Rust.
 //!
 //! ## Math on primitive types and math traits
@@ -31,11 +31,11 @@
 //!
 //! ## Pervasive types
 //!
-//! The [`option`](option/index.html) and [`result`](../core/result/index.html)
+//! The [`option`](option/index.html) and [`result`](result/index.html)
 //! modules define optional and error-handling types, `Option` and `Result`.
-//! [`iter`](../core/iter/index.html) defines Rust's iterator protocol
+//! [`iter`](iter/index.html) defines Rust's iterator protocol
 //! along with a wide variety of iterators.
-//! [`Cell` and `RefCell`](../core/cell/index.html) are for creating types that
+//! [`Cell` and `RefCell`](cell/index.html) are for creating types that
 //! manage their own mutability.
 //!
 //! ## Vectors, slices and strings
diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs
index 8d028a7a96a..07aaeac64be 100644
--- a/src/libstd/prelude.rs
+++ b/src/libstd/prelude.rs
@@ -38,55 +38,62 @@
 //! `drop`, `spawn`, and `channel`.
 
 // Reexported core operators
-pub use kinds::{Copy, Send, Sized, Share};
-pub use ops::{Add, Sub, Mul, Div, Rem, Neg, Not};
-pub use ops::{BitAnd, BitOr, BitXor};
-pub use ops::{Drop, Deref, DerefMut};
-pub use ops::{Shl, Shr, Index};
-pub use option::{Option, Some, None};
-pub use result::{Result, Ok, Err};
+#[doc(no_inline)] pub use kinds::{Copy, Send, Sized, Share};
+#[doc(no_inline)] pub use ops::{Add, Sub, Mul, Div, Rem, Neg, Not};
+#[doc(no_inline)] pub use ops::{BitAnd, BitOr, BitXor};
+#[doc(no_inline)] pub use ops::{Drop, Deref, DerefMut};
+#[doc(no_inline)] pub use ops::{Shl, Shr, Index};
+#[doc(no_inline)] pub use option::{Option, Some, None};
+#[doc(no_inline)] pub use result::{Result, Ok, Err};
 
 // Reexported functions
-pub use from_str::from_str;
-pub use iter::range;
-pub use mem::drop;
+#[doc(no_inline)] pub use from_str::from_str;
+#[doc(no_inline)] pub use iter::range;
+#[doc(no_inline)] pub use mem::drop;
 
 // Reexported types and traits
 
-pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr, IntoBytes};
-pub use c_str::ToCStr;
-pub use char::Char;
-pub use clone::Clone;
-pub use cmp::{Eq, Ord, TotalEq, TotalOrd, Ordering, Less, Equal, Greater, Equiv};
-pub use container::{Container, Mutable, Map, MutableMap, Set, MutableSet};
-pub use iter::{FromIterator, Extendable};
-pub use iter::{Iterator, DoubleEndedIterator, RandomAccessIterator, CloneableIterator};
-pub use iter::{OrdIterator, MutableDoubleEndedIterator, ExactSize};
-pub use num::{Num, NumCast, CheckedAdd, CheckedSub, CheckedMul};
-pub use num::{Signed, Unsigned};
-pub use num::{Primitive, Int, Float, FloatMath, ToPrimitive, FromPrimitive};
-pub use option::Expect;
-pub use owned::Box;
-pub use path::{GenericPath, Path, PosixPath, WindowsPath};
-pub use ptr::RawPtr;
-pub use io::{Buffer, Writer, Reader, Seek};
-pub use str::{Str, StrVector, StrSlice, OwnedStr, IntoMaybeOwned};
-pub use str::{StrAllocating};
-pub use to_str::{ToStr, IntoStr};
-pub use tuple::{Tuple1, Tuple2, Tuple3, Tuple4};
-pub use tuple::{Tuple5, Tuple6, Tuple7, Tuple8};
-pub use tuple::{Tuple9, Tuple10, Tuple11, Tuple12};
-pub use slice::{CloneableVector, ImmutableCloneableVector, MutableCloneableVector};
-pub use slice::{ImmutableVector, MutableVector};
-pub use slice::{ImmutableEqVector, ImmutableTotalOrdVector, MutableTotalOrdVector};
-pub use slice::{Vector, VectorVector, OwnedVector, MutableVectorAllocating};
-pub use string::String;
-pub use vec::Vec;
+#[doc(no_inline)] pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr};
+#[doc(no_inline)] pub use ascii::IntoBytes;
+#[doc(no_inline)] pub use c_str::ToCStr;
+#[doc(no_inline)] pub use char::Char;
+#[doc(no_inline)] pub use clone::Clone;
+#[doc(no_inline)] pub use cmp::{Eq, Ord, TotalEq, TotalOrd};
+#[doc(no_inline)] pub use cmp::{Ordering, Less, Equal, Greater, Equiv};
+#[doc(no_inline)] pub use container::{Container, Mutable, Map, MutableMap};
+#[doc(no_inline)] pub use container::{Set, MutableSet};
+#[doc(no_inline)] pub use iter::{FromIterator, Extendable, ExactSize};
+#[doc(no_inline)] pub use iter::{Iterator, DoubleEndedIterator};
+#[doc(no_inline)] pub use iter::{RandomAccessIterator, CloneableIterator};
+#[doc(no_inline)] pub use iter::{OrdIterator, MutableDoubleEndedIterator};
+#[doc(no_inline)] pub use num::{Num, NumCast, CheckedAdd, CheckedSub, CheckedMul};
+#[doc(no_inline)] pub use num::{Signed, Unsigned, Primitive, Int, Float};
+#[doc(no_inline)] pub use num::{FloatMath, ToPrimitive, FromPrimitive};
+#[doc(no_inline)] pub use option::Expect;
+#[doc(no_inline)] pub use owned::Box;
+#[doc(no_inline)] pub use path::{GenericPath, Path, PosixPath, WindowsPath};
+#[doc(no_inline)] pub use ptr::RawPtr;
+#[doc(no_inline)] pub use io::{Buffer, Writer, Reader, Seek};
+#[doc(no_inline)] pub use str::{Str, StrVector, StrSlice, OwnedStr};
+#[doc(no_inline)] pub use str::{IntoMaybeOwned, StrAllocating};
+#[doc(no_inline)] pub use to_str::{ToStr, IntoStr};
+#[doc(no_inline)] pub use tuple::{Tuple1, Tuple2, Tuple3, Tuple4};
+#[doc(no_inline)] pub use tuple::{Tuple5, Tuple6, Tuple7, Tuple8};
+#[doc(no_inline)] pub use tuple::{Tuple9, Tuple10, Tuple11, Tuple12};
+#[doc(no_inline)] pub use slice::{CloneableVector, ImmutableCloneableVector};
+#[doc(no_inline)] pub use slice::{MutableCloneableVector, MutableTotalOrdVector};
+#[doc(no_inline)] pub use slice::{ImmutableVector, MutableVector};
+#[doc(no_inline)] pub use slice::{ImmutableEqVector, ImmutableTotalOrdVector};
+#[doc(no_inline)] pub use slice::{Vector, VectorVector, OwnedVector};
+#[doc(no_inline)] pub use slice::MutableVectorAllocating;
+#[doc(no_inline)] pub use string::String;
+#[doc(no_inline)] pub use vec::Vec;
 
 // Reexported runtime types
-pub use comm::{sync_channel, channel, SyncSender, Sender, Receiver};
-pub use task::spawn;
+#[doc(no_inline)] pub use comm::{sync_channel, channel};
+#[doc(no_inline)] pub use comm::{SyncSender, Sender, Receiver};
+#[doc(no_inline)] pub use task::spawn;
 
 // Reexported statics
 #[cfg(not(test))]
-pub use gc::GC;
+#[doc(no_inline)] pub use gc::GC;