about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-04-16 20:34:55 +0000
committerbors <bors@rust-lang.org>2015-04-16 20:34:55 +0000
commit1014ac44f6271a55249b0488a2ce1cc49deed338 (patch)
treec827a7db48525741f0e91758c1df38082bee30aa
parente9080ec39d9a44eb0773d648b348575b19f655e5 (diff)
parent71c1b5b704ca3955e5ca22d77a0d59b55c9af4f1 (diff)
downloadrust-1014ac44f6271a55249b0488a2ce1cc49deed338.tar.gz
rust-1014ac44f6271a55249b0488a2ce1cc49deed338.zip
Auto merge of #24396 - alexcrichton:rustdoc2, r=aturon
A few final improvements to rustdoc for 1.0:

* Improve how rustdoc handles stability
* Fix cross-crate macro source links
* Improve experience of types inheriting methods through `Deref`

Some more details can be found in the commits.

[Preview](http://people.mozilla.org/~acrichton/doc/std/)
-rw-r--r--src/librustdoc/clean/inline.rs49
-rw-r--r--src/librustdoc/clean/mod.rs101
-rw-r--r--src/librustdoc/core.rs9
-rw-r--r--src/librustdoc/doctree.rs1
-rw-r--r--src/librustdoc/html/format.rs134
-rw-r--r--src/librustdoc/html/render.rs360
-rw-r--r--src/librustdoc/html/static/main.css63
-rw-r--r--src/librustdoc/html/static/main.js3
-rw-r--r--src/librustdoc/lib.rs1
-rw-r--r--src/librustdoc/stability_summary.rs199
-rw-r--r--src/librustdoc/test.rs3
-rw-r--r--src/librustdoc/visit_ast.rs1
-rw-r--r--src/test/auxiliary/issue-19190-3.rs30
-rw-r--r--src/test/rustdoc/issue-19190-2.rs22
-rw-r--r--src/test/rustdoc/issue-19190-3.rs35
-rw-r--r--src/test/rustdoc/issue-19190.rs26
16 files changed, 510 insertions, 527 deletions
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index f14437b71b4..4fc86cf181b 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -218,15 +218,17 @@ fn build_type(cx: &DocContext, tcx: &ty::ctxt, did: ast::DefId) -> clean::ItemEn
     })
 }
 
-fn build_impls(cx: &DocContext, tcx: &ty::ctxt,
-               did: ast::DefId) -> Vec<clean::Item> {
+pub fn build_impls(cx: &DocContext, tcx: &ty::ctxt,
+                   did: ast::DefId) -> Vec<clean::Item> {
     ty::populate_implementations_for_type_if_necessary(tcx, did);
     let mut impls = Vec::new();
 
     match tcx.inherent_impls.borrow().get(&did) {
         None => {}
         Some(i) => {
-            impls.extend(i.iter().map(|&did| { build_impl(cx, tcx, did) }));
+            for &did in i.iter() {
+                build_impl(cx, tcx, did, &mut impls);
+            }
         }
     }
 
@@ -247,9 +249,9 @@ fn build_impls(cx: &DocContext, tcx: &ty::ctxt,
 
         fn populate_impls(cx: &DocContext, tcx: &ty::ctxt,
                           def: decoder::DefLike,
-                          impls: &mut Vec<Option<clean::Item>>) {
+                          impls: &mut Vec<clean::Item>) {
             match def {
-                decoder::DlImpl(did) => impls.push(build_impl(cx, tcx, did)),
+                decoder::DlImpl(did) => build_impl(cx, tcx, did, impls),
                 decoder::DlDef(def::DefMod(did)) => {
                     csearch::each_child_of_item(&tcx.sess.cstore,
                                                 did,
@@ -262,14 +264,15 @@ fn build_impls(cx: &DocContext, tcx: &ty::ctxt,
         }
     }
 
-    impls.into_iter().filter_map(|a| a).collect()
+    return impls;
 }
 
-fn build_impl(cx: &DocContext,
-              tcx: &ty::ctxt,
-              did: ast::DefId) -> Option<clean::Item> {
+pub fn build_impl(cx: &DocContext,
+                  tcx: &ty::ctxt,
+                  did: ast::DefId,
+                  ret: &mut Vec<clean::Item>) {
     if !cx.inlined.borrow_mut().as_mut().unwrap().insert(did) {
-        return None
+        return
     }
 
     let attrs = load_attrs(cx, tcx, did);
@@ -278,13 +281,13 @@ fn build_impl(cx: &DocContext,
         // If this is an impl for a #[doc(hidden)] trait, be sure to not inline
         let trait_attrs = load_attrs(cx, tcx, t.def_id);
         if trait_attrs.iter().any(|a| is_doc_hidden(a)) {
-            return None
+            return
         }
     }
 
     // If this is a defaulted impl, then bail out early here
     if csearch::is_default_impl(&tcx.sess.cstore, did) {
-        return Some(clean::Item {
+        return ret.push(clean::Item {
             inner: clean::DefaultImplItem(clean::DefaultImpl {
                 // FIXME: this should be decoded
                 unsafety: ast::Unsafety::Normal,
@@ -352,19 +355,25 @@ fn build_impl(cx: &DocContext,
                 })
             }
         }
-    }).collect();
+    }).collect::<Vec<_>>();
     let polarity = csearch::get_impl_polarity(tcx, did);
     let ty = ty::lookup_item_type(tcx, did);
-    return Some(clean::Item {
+    let trait_ = associated_trait.clean(cx).map(|bound| {
+        match bound {
+            clean::TraitBound(polyt, _) => polyt.trait_,
+            clean::RegionBound(..) => unreachable!(),
+        }
+    });
+    if let Some(clean::ResolvedPath { did, .. }) = trait_ {
+        if Some(did) == cx.deref_trait_did.get() {
+            super::build_deref_target_impls(cx, &trait_items, ret);
+        }
+    }
+    ret.push(clean::Item {
         inner: clean::ImplItem(clean::Impl {
             unsafety: ast::Unsafety::Normal, // FIXME: this should be decoded
             derived: clean::detect_derived(&attrs),
-            trait_: associated_trait.clean(cx).map(|bound| {
-                match bound {
-                    clean::TraitBound(polyt, _) => polyt.trait_,
-                    clean::RegionBound(..) => unreachable!(),
-                }
-            }),
+            trait_: trait_,
             for_: ty.ty.clean(cx),
             generics: (&ty.generics, &predicates, subst::TypeSpace).clean(cx),
             items: trait_items,
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 72702dc8d94..f7fbb67e08a 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -128,6 +128,10 @@ impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
     fn clean(&self, cx: &DocContext) -> Crate {
         use rustc::session::config::Input;
 
+        if let Some(t) = cx.tcx_opt() {
+            cx.deref_trait_did.set(t.lang_items.deref_trait());
+        }
+
         let mut externs = Vec::new();
         cx.sess().cstore.iter_crate_data(|n, meta| {
             externs.push((n, meta.clean(cx)));
@@ -313,6 +317,22 @@ impl Item {
     pub fn is_fn(&self) -> bool {
         match self.inner { FunctionItem(..) => true, _ => false }
     }
+
+    pub fn stability_class(&self) -> String {
+        match self.stability {
+            Some(ref s) => {
+                let mut base = match s.level {
+                    attr::Unstable => "unstable".to_string(),
+                    attr::Stable => String::new(),
+                };
+                if !s.deprecated_since.is_empty() {
+                    base.push_str(" deprecated");
+                }
+                base
+            }
+            _ => String::new(),
+        }
+    }
 }
 
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
@@ -371,7 +391,7 @@ impl Clean<Item> for doctree::Module {
         items.extend(self.statics.iter().map(|x| x.clean(cx)));
         items.extend(self.constants.iter().map(|x| x.clean(cx)));
         items.extend(self.traits.iter().map(|x| x.clean(cx)));
-        items.extend(self.impls.iter().map(|x| x.clean(cx)));
+        items.extend(self.impls.iter().flat_map(|x| x.clean(cx).into_iter()));
         items.extend(self.macros.iter().map(|x| x.clean(cx)));
         items.extend(self.def_traits.iter().map(|x| x.clean(cx)));
 
@@ -1254,6 +1274,7 @@ impl Clean<Item> for ast::ImplItem {
             ast::MacImplItem(_) => {
                 MacroItem(Macro {
                     source: self.span.to_src(cx),
+                    imported_from: None,
                 })
             }
         };
@@ -2169,9 +2190,21 @@ fn detect_derived<M: AttrMetaMethods>(attrs: &[M]) -> bool {
     attr::contains_name(attrs, "automatically_derived")
 }
 
-impl Clean<Item> for doctree::Impl {
-    fn clean(&self, cx: &DocContext) -> Item {
-        Item {
+impl Clean<Vec<Item>> for doctree::Impl {
+    fn clean(&self, cx: &DocContext) -> Vec<Item> {
+        let mut ret = Vec::new();
+        let trait_ = self.trait_.clean(cx);
+        let items = self.items.clean(cx);
+
+        // If this impl block is an implementation of the Deref trait, then we
+        // need to try inlining the target's inherent impl blocks as well.
+        if let Some(ResolvedPath { did, .. }) = trait_ {
+            if Some(did) == cx.deref_trait_did.get() {
+                build_deref_target_impls(cx, &items, &mut ret);
+            }
+        }
+
+        ret.push(Item {
             name: None,
             attrs: self.attrs.clean(cx),
             source: self.whence.clean(cx),
@@ -2181,12 +2214,66 @@ impl Clean<Item> for doctree::Impl {
             inner: ImplItem(Impl {
                 unsafety: self.unsafety,
                 generics: self.generics.clean(cx),
-                trait_: self.trait_.clean(cx),
+                trait_: trait_,
                 for_: self.for_.clean(cx),
-                items: self.items.clean(cx),
+                items: items,
                 derived: detect_derived(&self.attrs),
                 polarity: Some(self.polarity.clean(cx)),
             }),
+        });
+        return ret;
+    }
+}
+
+fn build_deref_target_impls(cx: &DocContext,
+                            items: &[Item],
+                            ret: &mut Vec<Item>) {
+    let tcx = match cx.tcx_opt() {
+        Some(t) => t,
+        None => return,
+    };
+
+    for item in items {
+        let target = match item.inner {
+            TypedefItem(ref t) => &t.type_,
+            _ => continue,
+        };
+        let primitive = match *target {
+            ResolvedPath { did, .. } if ast_util::is_local(did) => continue,
+            ResolvedPath { did, .. } => {
+                ret.extend(inline::build_impls(cx, tcx, did));
+                continue
+            }
+            _ => match target.primitive_type() {
+                Some(prim) => prim,
+                None => continue,
+            }
+        };
+        let did = match primitive {
+            Isize => tcx.lang_items.isize_impl(),
+            I8 => tcx.lang_items.i8_impl(),
+            I16 => tcx.lang_items.i16_impl(),
+            I32 => tcx.lang_items.i32_impl(),
+            I64 => tcx.lang_items.i64_impl(),
+            Usize => tcx.lang_items.usize_impl(),
+            U8 => tcx.lang_items.u8_impl(),
+            U16 => tcx.lang_items.u16_impl(),
+            U32 => tcx.lang_items.u32_impl(),
+            U64 => tcx.lang_items.u64_impl(),
+            F32 => tcx.lang_items.f32_impl(),
+            F64 => tcx.lang_items.f64_impl(),
+            Char => tcx.lang_items.char_impl(),
+            Bool => None,
+            Str => tcx.lang_items.str_impl(),
+            Slice => tcx.lang_items.slice_impl(),
+            Array => tcx.lang_items.slice_impl(),
+            PrimitiveTuple => None,
+            PrimitiveRawPointer => tcx.lang_items.const_ptr_impl(),
+        };
+        if let Some(did) = did {
+            if !ast_util::is_local(did) {
+                inline::build_impl(cx, tcx, did, ret);
+            }
         }
     }
 }
@@ -2541,6 +2628,7 @@ fn resolve_def(cx: &DocContext, id: ast::NodeId) -> Option<ast::DefId> {
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct Macro {
     pub source: String,
+    pub imported_from: Option<String>,
 }
 
 impl Clean<Item> for doctree::Macro {
@@ -2554,6 +2642,7 @@ impl Clean<Item> for doctree::Macro {
             def_id: ast_util::local_def(self.id),
             inner: MacroItem(Macro {
                 source: self.whence.to_src(cx),
+                imported_from: self.imported_from.clean(cx),
             }),
         }
     }
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index a637ba9f297..1b74123c4ad 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -20,7 +20,7 @@ use rustc_resolve as resolve;
 
 use syntax::{ast, ast_map, codemap, diagnostic};
 
-use std::cell::RefCell;
+use std::cell::{RefCell, Cell};
 use std::collections::{HashMap, HashSet};
 
 use visit_ast::RustdocVisitor;
@@ -48,6 +48,7 @@ pub struct DocContext<'tcx> {
     pub external_typarams: RefCell<Option<HashMap<ast::DefId, String>>>,
     pub inlined: RefCell<Option<HashSet<ast::DefId>>>,
     pub populated_crate_impls: RefCell<HashSet<ast::CrateNum>>,
+    pub deref_trait_did: Cell<Option<ast::DefId>>,
 }
 
 impl<'tcx> DocContext<'tcx> {
@@ -77,6 +78,7 @@ pub struct CrateAnalysis {
     pub external_paths: ExternalPaths,
     pub external_typarams: RefCell<Option<HashMap<ast::DefId, String>>>,
     pub inlined: RefCell<Option<HashSet<ast::DefId>>>,
+    pub deref_trait_did: Option<ast::DefId>,
 }
 
 pub type Externs = HashMap<String, Vec<String>>;
@@ -147,15 +149,17 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
         external_paths: RefCell::new(Some(HashMap::new())),
         inlined: RefCell::new(Some(HashSet::new())),
         populated_crate_impls: RefCell::new(HashSet::new()),
+        deref_trait_did: Cell::new(None),
     };
     debug!("crate: {:?}", ctxt.krate);
 
-    let analysis = CrateAnalysis {
+    let mut analysis = CrateAnalysis {
         exported_items: exported_items,
         public_items: public_items,
         external_paths: RefCell::new(None),
         external_typarams: RefCell::new(None),
         inlined: RefCell::new(None),
+        deref_trait_did: None,
     };
 
     let krate = {
@@ -170,5 +174,6 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
     *analysis.external_typarams.borrow_mut() = map;
     let map = ctxt.inlined.borrow_mut().take();
     *analysis.inlined.borrow_mut() = map;
+    analysis.deref_trait_did = ctxt.deref_trait_did.get();
     (krate, analysis)
 }
diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs
index 862bca1b813..8fa92304d24 100644
--- a/src/librustdoc/doctree.rs
+++ b/src/librustdoc/doctree.rs
@@ -214,6 +214,7 @@ pub struct Macro {
     pub attrs: Vec<ast::Attribute>,
     pub whence: Span,
     pub stab: Option<attr::Stability>,
+    pub imported_from: Option<Ident>,
 }
 
 pub struct ExternCrate {
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index d2dccca362e..bb53d532f52 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -23,10 +23,8 @@ use syntax::ast;
 use syntax::ast_util;
 
 use clean;
-use stability_summary::ModuleSummary;
 use html::item_type::ItemType;
 use html::render;
-use html::escape::Escape;
 use html::render::{cache, CURRENT_LOCATION_KEY};
 
 /// Helper to render an optional visibility with a space after it (if the
@@ -45,10 +43,6 @@ pub struct MutableSpace(pub clean::Mutability);
 /// Similar to VisSpace, but used for mutability
 #[derive(Copy, Clone)]
 pub struct RawMutableSpace(pub clean::Mutability);
-/// Wrapper struct for properly emitting the stability level.
-pub struct Stability<'a>(pub &'a Option<clean::Stability>);
-/// Wrapper struct for emitting the stability level concisely.
-pub struct ConciseStability<'a>(pub &'a Option<clean::Stability>);
 /// Wrapper struct for emitting a where clause from Generics.
 pub struct WhereClause<'a>(pub &'a clean::Generics);
 /// Wrapper struct for emitting type parameter bounds.
@@ -294,9 +288,9 @@ pub fn href(did: ast::DefId) -> Option<(String, ItemType, Vec<String>)> {
         repeat("../").take(loc.len()).collect::<String>()
     } else {
         match cache.extern_locations[&did.krate] {
-            render::Remote(ref s) => s.to_string(),
-            render::Local => repeat("../").take(loc.len()).collect::<String>(),
-            render::Unknown => return None,
+            (_, render::Remote(ref s)) => s.to_string(),
+            (_, render::Local) => repeat("../").take(loc.len()).collect(),
+            (_, render::Unknown) => return None,
         }
     };
     for component in &fqp[..fqp.len() - 1] {
@@ -385,12 +379,12 @@ fn primitive_link(f: &mut fmt::Formatter,
                 node: ast::CRATE_NODE_ID,
             }];
             let loc = match m.extern_locations[&cnum] {
-                render::Remote(ref s) => Some(s.to_string()),
-                render::Local => {
+                (_, render::Remote(ref s)) => Some(s.to_string()),
+                (_, render::Local) => {
                     let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
                     Some(repeat("../").take(len).collect::<String>())
                 }
-                render::Unknown => None,
+                (_, render::Unknown) => None,
             };
             match loc {
                 Some(root) => {
@@ -702,119 +696,3 @@ impl fmt::Display for AbiSpace {
         }
     }
 }
-
-impl<'a> fmt::Display for Stability<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let Stability(stab) = *self;
-        match *stab {
-            Some(ref stability) => {
-                let lvl = if stability.deprecated_since.is_empty() {
-                    format!("{}", stability.level)
-                } else {
-                    "Deprecated".to_string()
-                };
-                write!(f, "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
-                       lvl = Escape(&*lvl),
-                       reason = Escape(&*stability.reason))
-            }
-            None => Ok(())
-        }
-    }
-}
-
-impl<'a> fmt::Display for ConciseStability<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let ConciseStability(stab) = *self;
-        match *stab {
-            Some(ref stability) => {
-                let lvl = if stability.deprecated_since.is_empty() {
-                    format!("{}", stability.level)
-                } else {
-                    "Deprecated".to_string()
-                };
-                write!(f, "<a class='stability {lvl}' title='{lvl}{colon}{reason}'></a>",
-                       lvl = Escape(&*lvl),
-                       colon = if !stability.reason.is_empty() { ": " } else { "" },
-                       reason = Escape(&*stability.reason))
-            }
-            None => {
-                write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
-            }
-        }
-    }
-}
-
-impl fmt::Display for ModuleSummary {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fn fmt_inner<'a>(f: &mut fmt::Formatter,
-                         context: &mut Vec<&'a str>,
-                         m: &'a ModuleSummary)
-                     -> fmt::Result {
-            let cnt = m.counts;
-            let tot = cnt.total();
-            if tot == 0 { return Ok(()) }
-
-            context.push(&m.name);
-            let path = context.connect("::");
-
-            try!(write!(f, "<tr>"));
-            try!(write!(f, "<td><a href='{}'>{}</a></td>", {
-                            let mut url = context[1..].to_vec();
-                            url.push("index.html");
-                            url.connect("/")
-                        },
-                        path));
-            try!(write!(f, "<td class='summary-column'>"));
-            try!(write!(f, "<span class='summary Stable' \
-                            style='width: {:.4}%; display: inline-block'>&nbsp</span>",
-                        (100 * cnt.stable) as f64/tot as f64));
-            try!(write!(f, "<span class='summary Unstable' \
-                            style='width: {:.4}%; display: inline-block'>&nbsp</span>",
-                        (100 * cnt.unstable) as f64/tot as f64));
-            try!(write!(f, "<span class='summary Deprecated' \
-                            style='width: {:.4}%; display: inline-block'>&nbsp</span>",
-                        (100 * cnt.deprecated) as f64/tot as f64));
-            try!(write!(f, "<span class='summary Unmarked' \
-                            style='width: {:.4}%; display: inline-block'>&nbsp</span>",
-                        (100 * cnt.unmarked) as f64/tot as f64));
-            try!(write!(f, "</td></tr>"));
-
-            for submodule in &m.submodules {
-                try!(fmt_inner(f, context, submodule));
-            }
-            context.pop();
-            Ok(())
-        }
-
-        let mut context = Vec::new();
-
-        let tot = self.counts.total();
-        let (stable, unstable, deprecated, unmarked) = if tot == 0 {
-            (0, 0, 0, 0)
-        } else {
-            ((100 * self.counts.stable)/tot,
-             (100 * self.counts.unstable)/tot,
-             (100 * self.counts.deprecated)/tot,
-             (100 * self.counts.unmarked)/tot)
-        };
-
-        try!(write!(f,
-r"<h1 class='fqn'>Stability dashboard: crate <a class='mod' href='index.html'>{name}</a></h1>
-This dashboard summarizes the stability levels for all of the public modules of
-the crate, according to the total number of items at each level in the module and
-its children (percentages total for {name}):
-<blockquote>
-<a class='stability Stable'></a> stable ({}%),<br/>
-<a class='stability Unstable'></a> unstable ({}%),<br/>
-<a class='stability Deprecated'></a> deprecated ({}%),<br/>
-<a class='stability Unmarked'></a> unmarked ({}%)
-</blockquote>
-The counts do not include methods or trait
-implementations that are visible only through a re-exported type.",
-stable, unstable, deprecated, unmarked,
-name=self.name));
-        try!(write!(f, "<table>"));
-        try!(fmt_inner(f, &mut context, self));
-        write!(f, "</table>")
-    }
-}
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 5f4a3e74b65..f87a86eb3a6 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -56,19 +56,20 @@ use serialize::json::ToJson;
 use syntax::abi;
 use syntax::ast;
 use syntax::ast_util;
+use syntax::attr;
 use rustc::util::nodemap::NodeSet;
 
 use clean;
 use doctree;
 use fold::DocFolder;
-use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace, Stability};
-use html::format::{ConciseStability, TyParamBounds, WhereClause, href, AbiSpace};
+use html::escape::Escape;
+use html::format::{TyParamBounds, WhereClause, href, AbiSpace};
+use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace};
 use html::highlight;
 use html::item_type::ItemType;
 use html::layout;
 use html::markdown::Markdown;
 use html::markdown;
-use stability_summary;
 
 /// A pair of name and its optional document.
 pub type NameDoc = (String, Option<String>);
@@ -192,7 +193,7 @@ pub struct Cache {
     pub implementors: HashMap<ast::DefId, Vec<Implementor>>,
 
     /// Cache of where external crate documentation can be found.
-    pub extern_locations: HashMap<ast::CrateNum, ExternalLocation>,
+    pub extern_locations: HashMap<ast::CrateNum, (String, ExternalLocation)>,
 
     /// Cache of where documentation for primitives can be found.
     pub primitive_locations: HashMap<clean::PrimitiveType, ast::CrateNum>,
@@ -208,6 +209,7 @@ pub struct Cache {
     privmod: bool,
     remove_priv: bool,
     public_items: NodeSet,
+    deref_trait_did: Option<ast::DefId>,
 
     // In rare case where a structure is defined in one module but implemented
     // in another, if the implementing module is parsed before defining module,
@@ -395,6 +397,7 @@ pub fn run(mut krate: clean::Crate,
         public_items: public_items,
         orphan_methods: Vec::new(),
         traits: mem::replace(&mut krate.external_traits, HashMap::new()),
+        deref_trait_did: analysis.as_ref().and_then(|a| a.deref_trait_did),
         typarams: analysis.as_ref().map(|a| {
             a.external_typarams.borrow_mut().take().unwrap()
         }).unwrap_or(HashMap::new()),
@@ -402,12 +405,11 @@ pub fn run(mut krate: clean::Crate,
             a.inlined.borrow_mut().take().unwrap()
         }).unwrap_or(HashSet::new()),
     };
-    cache.stack.push(krate.name.clone());
-    krate = cache.fold_crate(krate);
 
     // Cache where all our extern crates are located
     for &(n, ref e) in &krate.externs {
-        cache.extern_locations.insert(n, extern_location(e, &cx.dst));
+        cache.extern_locations.insert(n, (e.name.clone(),
+                                          extern_location(e, &cx.dst)));
         let did = ast::DefId { krate: n, node: ast::CRATE_NODE_ID };
         cache.paths.insert(did, (vec![e.name.to_string()], ItemType::Module));
     }
@@ -425,6 +427,9 @@ pub fn run(mut krate: clean::Crate,
         cache.primitive_locations.insert(prim, ast::LOCAL_CRATE);
     }
 
+    cache.stack.push(krate.name.clone());
+    krate = cache.fold_crate(krate);
+
     // Build our search index
     let index = try!(build_index(&krate, &mut cache));
 
@@ -437,11 +442,8 @@ pub fn run(mut krate: clean::Crate,
     try!(write_shared(&cx, &krate, &*cache, index));
     let krate = try!(render_sources(&mut cx, krate));
 
-    // Crawl the crate, building a summary of the stability levels.
-    let summary = stability_summary::build(&krate);
-
     // And finally render the whole crate's documentation
-    cx.krate(krate, summary)
+    cx.krate(krate)
 }
 
 fn build_index(krate: &clean::Crate, cache: &mut Cache) -> io::Result<String> {
@@ -645,8 +647,7 @@ fn write_shared(cx: &Context,
             // going on). If they're in different crates then the crate defining
             // the trait will be interested in our implementation.
             if imp.def_id.krate == did.krate { continue }
-            try!(write!(&mut f, r#""{}impl{} {}{} for {}","#,
-                        ConciseStability(&imp.stability),
+            try!(write!(&mut f, r#""impl{} {}{} for {}","#,
                         imp.generics,
                         if imp.polarity == Some(clean::ImplPolarity::Negative) { "!" } else { "" },
                         imp.trait_, imp.for_));
@@ -1071,8 +1072,11 @@ impl DocFolder for Cache {
                             }
 
                             ref t => {
-                                t.primitive_type().map(|p| {
-                                    ast_util::local_def(p.to_node_id())
+                                t.primitive_type().and_then(|t| {
+                                    self.primitive_locations.get(&t).map(|n| {
+                                        let id = t.to_node_id();
+                                        ast::DefId { krate: *n, node: id }
+                                    })
                                 })
                             }
                         };
@@ -1143,38 +1147,13 @@ impl Context {
     ///
     /// This currently isn't parallelized, but it'd be pretty easy to add
     /// parallelization to this function.
-    fn krate(mut self, mut krate: clean::Crate,
-             stability: stability_summary::ModuleSummary) -> io::Result<()> {
+    fn krate(self, mut krate: clean::Crate) -> io::Result<()> {
         let mut item = match krate.module.take() {
             Some(i) => i,
             None => return Ok(())
         };
         item.name = Some(krate.name);
 
-        // render stability dashboard
-        try!(self.recurse(stability.name.clone(), |this| {
-            let json_dst = &this.dst.join("stability.json");
-            let mut json_out = BufWriter::new(try!(File::create(json_dst)));
-            try!(write!(&mut json_out, "{}", json::as_json(&stability)));
-
-            let mut title = stability.name.clone();
-            title.push_str(" - Stability dashboard");
-            let desc = format!("API stability overview for the Rust `{}` crate.",
-                               this.layout.krate);
-            let page = layout::Page {
-                ty: "mod",
-                root_path: &this.root_path,
-                title: &title,
-                description: &desc,
-                keywords: get_basic_keywords(),
-            };
-            let html_dst = &this.dst.join("stability.html");
-            let mut html_out = BufWriter::new(try!(File::create(html_dst)));
-            layout::render(&mut html_out, &this.layout, &page,
-                           &Sidebar{ cx: this, item: &item },
-                           &stability)
-        }));
-
         // render the crate documentation
         let mut work = vec!((self, item));
         loop {
@@ -1371,22 +1350,43 @@ impl<'a> Item<'a> {
     /// may happen, for example, with externally inlined items where the source
     /// of their crate documentation isn't known.
     fn href(&self, cx: &Context) -> Option<String> {
+        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)
+        };
+
+        // First check to see if this is an imported macro source. In this case
+        // we need to handle it specially as cross-crate inlined macros have...
+        // odd locations!
+        let imported_macro_from = match self.item.inner {
+            clean::MacroItem(ref m) => m.imported_from.as_ref(),
+            _ => None,
+        };
+        if let Some(krate) = imported_macro_from {
+            let cache = cache();
+            let root = cache.extern_locations.values().find(|&&(ref n, _)| {
+                *krate == *n
+            }).map(|l| &l.1);
+            let root = match root {
+                Some(&Remote(ref s)) => s.to_string(),
+                Some(&Local) => self.cx.root_path.clone(),
+                None | Some(&Unknown) => return None,
+            };
+            Some(format!("{root}/{krate}/macro.{name}.html?gotomacrosrc=1",
+                         root = root,
+                         krate = krate,
+                         name = self.item.name.as_ref().unwrap()))
+
         // 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) {
+        } else if ast_util::is_local(self.item.def_id) {
             let mut path = Vec::new();
             clean_srcpath(&cx.src_root, Path::new(&self.item.source.filename),
                           true, |component| {
                 path.push(component.to_string());
             });
-            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,
@@ -1408,9 +1408,9 @@ impl<'a> Item<'a> {
             let cache = cache();
             let path = &cache.external_paths[&self.item.def_id];
             let root = match cache.extern_locations[&self.item.def_id.krate] {
-                Remote(ref s) => s.to_string(),
-                Local => self.cx.root_path.clone(),
-                Unknown => return None,
+                (_, Remote(ref s)) => s.to_string(),
+                (_, Local) => self.cx.root_path.clone(),
+                (_, Unknown) => return None,
             };
             Some(format!("{root}{path}/{file}?gotosrc={goto}",
                          root = root,
@@ -1456,21 +1456,8 @@ impl<'a> fmt::Display for Item<'a> {
         try!(write!(fmt, "<a class='{}' href=''>{}</a>",
                     shortty(self.item), self.item.name.as_ref().unwrap()));
 
-        // Write stability level
-        try!(write!(fmt, "<wbr>{}", Stability(&self.item.stability)));
-
         try!(write!(fmt, "</span>")); // in-band
-        // Links to out-of-band information, i.e. src and stability dashboard
         try!(write!(fmt, "<span class='out-of-band'>"));
-
-        // Write stability dashboard link
-        match self.item.inner {
-            clean::ModuleItem(ref m) if m.is_crate => {
-                try!(write!(fmt, "<a href='stability.html'>[stability]</a> "));
-            }
-            _ => {}
-        };
-
         try!(write!(fmt,
         r##"<span id='render-detail'>
             <a id="collapse-all" href="#">[-]</a>&nbsp;<a id="expand-all" href="#">[+]</a>
@@ -1485,7 +1472,8 @@ impl<'a> fmt::Display for Item<'a> {
         if self.cx.include_sources && !is_primitive {
             match self.href(self.cx) {
                 Some(l) => {
-                    try!(write!(fmt, "<a id='src-{}' href='{}'>[src]</a>",
+                    try!(write!(fmt, "<a id='src-{}' class='srclink' \
+                                       href='{}'>[src]</a>",
                                 self.item.def_id.node, l));
                 }
                 None => {}
@@ -1554,11 +1542,11 @@ fn plain_summary_line(s: Option<&str>) -> String {
 }
 
 fn document(w: &mut fmt::Formatter, item: &clean::Item) -> fmt::Result {
-    match item.doc_value() {
-        Some(s) => {
-            try!(write!(w, "<div class='docblock'>{}</div>", Markdown(s)));
-        }
-        None => {}
+    if let Some(s) = short_stability(item, true) {
+        try!(write!(w, "<div class='stability'>{}</div>", s));
+    }
+    if let Some(s) = item.doc_value() {
+        try!(write!(w, "<div class='docblock'>{}</div>", Markdown(s)));
     }
     Ok(())
 }
@@ -1593,10 +1581,17 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
     fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: usize, idx2: usize) -> Ordering {
         let ty1 = shortty(i1);
         let ty2 = shortty(i2);
-        if ty1 == ty2 {
-            return i1.name.cmp(&i2.name);
+        if ty1 != ty2 {
+            return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2))
+        }
+        let s1 = i1.stability.as_ref().map(|s| s.level);
+        let s2 = i2.stability.as_ref().map(|s| s.level);
+        match (s1, s2) {
+            (Some(attr::Unstable), Some(attr::Stable)) => return Ordering::Greater,
+            (Some(attr::Stable), Some(attr::Unstable)) => return Ordering::Less,
+            _ => {}
         }
-        (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2))
+        i1.name.cmp(&i2.name)
     }
 
     indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2));
@@ -1665,19 +1660,27 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
 
             _ => {
                 if myitem.name.is_none() { continue }
+                let stab_docs = if let Some(s) = short_stability(myitem, false) {
+                    format!("[{}]", s)
+                } else {
+                    String::new()
+                };
                 try!(write!(w, "
-                    <tr>
-                        <td>{stab}<a class='{class}' href='{href}'
-                               title='{title}'>{}</a></td>
-                        <td class='docblock short'>{}</td>
+                    <tr class='{stab} module-item'>
+                        <td><a class='{class}' href='{href}'
+                               title='{title}'>{name}</a></td>
+                        <td class='docblock short'>
+                            {stab_docs} {docs}
+                        </td>
                     </tr>
                 ",
-                *myitem.name.as_ref().unwrap(),
-                Markdown(&shorter(myitem.doc_value())[..]),
+                name = *myitem.name.as_ref().unwrap(),
+                stab_docs = stab_docs,
+                docs = Markdown(&shorter(myitem.doc_value())),
                 class = shortty(myitem),
+                stab = myitem.stability_class(),
                 href = item_path(myitem),
-                title = full_path(cx, myitem),
-                stab = ConciseStability(&myitem.stability)));
+                title = full_path(cx, myitem)));
             }
         }
     }
@@ -1685,6 +1688,30 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
     write!(w, "</table>")
 }
 
+fn short_stability(item: &clean::Item, show_reason: bool) -> Option<String> {
+    item.stability.as_ref().and_then(|stab| {
+        let reason = if show_reason && !stab.reason.is_empty() {
+            format!(": {}", stab.reason)
+        } else {
+            String::new()
+        };
+        let text = if !stab.deprecated_since.is_empty() {
+            let since = if show_reason {
+                format!(" since {}", Escape(&stab.deprecated_since))
+            } else {
+                String::new()
+            };
+            format!("Deprecated{}{}", since, Markdown(&reason))
+        } else if stab.level == attr::Unstable {
+            format!("Unstable{}", Markdown(&reason))
+        } else {
+            return None
+        };
+        Some(format!("<em class='stab {}'>{}</em>",
+                     item.stability_class(), text))
+    })
+}
+
 struct Initializer<'a>(&'a str);
 
 impl<'a> fmt::Display for Initializer<'a> {
@@ -1800,10 +1827,10 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
 
     fn trait_item(w: &mut fmt::Formatter, m: &clean::Item)
                   -> fmt::Result {
-        try!(write!(w, "<h3 id='{}.{}' class='method'>{}<code>",
-                    shortty(m),
-                    *m.name.as_ref().unwrap(),
-                    ConciseStability(&m.stability)));
+        try!(write!(w, "<h3 id='{ty}.{name}' class='method stab {stab}'><code>",
+                    ty = shortty(m),
+                    name = *m.name.as_ref().unwrap(),
+                    stab = m.stability_class()));
         try!(render_method(w, m, MethodLink::Anchor));
         try!(write!(w, "</code></h3>"));
         try!(document(w, m));
@@ -1844,7 +1871,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
     }
 
     // If there are methods directly on this trait object, render them here.
-    try!(render_methods(w, it));
+    try!(render_methods(w, it.def_id, MethodRender::All));
 
     let cache = cache();
     try!(write!(w, "
@@ -1854,8 +1881,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
     match cache.implementors.get(&it.def_id) {
         Some(implementors) => {
             for i in implementors {
-                try!(writeln!(w, "<li>{}<code>impl{} {} for {}{}</code></li>",
-                              ConciseStability(&i.stability),
+                try!(writeln!(w, "<li><code>impl{} {} for {}{}</code></li>",
                               i.generics, i.trait_, i.for_, WhereClause(&i.generics)));
             }
         }
@@ -1964,9 +1990,10 @@ fn item_struct(w: &mut fmt::Formatter, it: &clean::Item,
         if fields.peek().is_some() {
             try!(write!(w, "<h2 class='fields'>Fields</h2>\n<table>"));
             for field in fields {
-                try!(write!(w, "<tr><td id='structfield.{name}'>\
-                                  {stab}<code>{name}</code></td><td>",
-                            stab = ConciseStability(&field.stability),
+                try!(write!(w, "<tr class='stab {stab}'>
+                                  <td id='structfield.{name}'>\
+                                    <code>{name}</code></td><td>",
+                            stab = field.stability_class(),
                             name = field.name.as_ref().unwrap()));
                 try!(document(w, field));
                 try!(write!(w, "</td></tr>"));
@@ -1974,7 +2001,7 @@ fn item_struct(w: &mut fmt::Formatter, it: &clean::Item,
             try!(write!(w, "</table>"));
         }
     }
-    render_methods(w, it)
+    render_methods(w, it.def_id, MethodRender::All)
 }
 
 fn item_enum(w: &mut fmt::Formatter, it: &clean::Item,
@@ -2034,8 +2061,7 @@ fn item_enum(w: &mut fmt::Formatter, it: &clean::Item,
     if !e.variants.is_empty() {
         try!(write!(w, "<h2 class='variants'>Variants</h2>\n<table>"));
         for variant in &e.variants {
-            try!(write!(w, "<tr><td id='variant.{name}'>{stab}<code>{name}</code></td><td>",
-                          stab = ConciseStability(&variant.stability),
+            try!(write!(w, "<tr><td id='variant.{name}'><code>{name}</code></td><td>",
                           name = variant.name.as_ref().unwrap()));
             try!(document(w, variant));
             match variant.inner {
@@ -2074,7 +2100,7 @@ fn item_enum(w: &mut fmt::Formatter, it: &clean::Item,
         try!(write!(w, "</table>"));
 
     }
-    try!(render_methods(w, it));
+    try!(render_methods(w, it.def_id, MethodRender::All));
     Ok(())
 }
 
@@ -2163,27 +2189,61 @@ enum MethodLink {
     GotoSource(ast::DefId),
 }
 
-fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
-    let v = match cache().impls.get(&it.def_id) {
-        Some(v) => v.clone(),
+enum MethodRender<'a> {
+    All,
+    DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type },
+}
+
+fn render_methods(w: &mut fmt::Formatter,
+                  it: ast::DefId,
+                  what: MethodRender) -> fmt::Result {
+    let c = cache();
+    let v = match c.impls.get(&it) {
+        Some(v) => v,
         None => return Ok(()),
     };
-    let (non_trait, traits): (Vec<_>, _) = v.into_iter()
-        .partition(|i| i.impl_.trait_.is_none());
+    let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| {
+        i.impl_.trait_.is_none()
+    });
     if !non_trait.is_empty() {
-        try!(write!(w, "<h2 id='methods'>Methods</h2>"));
+        let render_header = match what {
+            MethodRender::All => {
+                try!(write!(w, "<h2 id='methods'>Methods</h2>"));
+                true
+            }
+            MethodRender::DerefFor { trait_, type_ } => {
+                try!(write!(w, "<h2 id='deref-methods'>Methods from \
+                                    {}&lt;Target={}&gt;</h2>", trait_, type_));
+                false
+            }
+        };
         for i in &non_trait {
-            try!(render_impl(w, i, MethodLink::Anchor));
+            try!(render_impl(w, i, MethodLink::Anchor, render_header));
         }
     }
+    if let MethodRender::DerefFor { .. } = what {
+        return Ok(())
+    }
     if !traits.is_empty() {
+        let deref_impl = traits.iter().find(|t| {
+            match *t.impl_.trait_.as_ref().unwrap() {
+                clean::ResolvedPath { did, .. } => {
+                    Some(did) == c.deref_trait_did
+                }
+                _ => false
+            }
+        });
+        if let Some(impl_) = deref_impl {
+            try!(render_deref_methods(w, impl_));
+        }
         try!(write!(w, "<h2 id='implementations'>Trait \
                           Implementations</h2>"));
-        let (derived, manual): (Vec<_>, _) = traits.into_iter()
-            .partition(|i| i.impl_.derived);
+        let (derived, manual): (Vec<_>, _) = traits.iter().partition(|i| {
+            i.impl_.derived
+        });
         for i in &manual {
             let did = i.trait_did().unwrap();
-            try!(render_impl(w, i, MethodLink::GotoSource(did)));
+            try!(render_impl(w, i, MethodLink::GotoSource(did), true));
         }
         if !derived.is_empty() {
             try!(write!(w, "<h3 id='derived_implementations'>\
@@ -2191,73 +2251,92 @@ fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
             </h3>"));
             for i in &derived {
                 let did = i.trait_did().unwrap();
-                try!(render_impl(w, i, MethodLink::GotoSource(did)));
+                try!(render_impl(w, i, MethodLink::GotoSource(did), true));
             }
         }
     }
     Ok(())
 }
 
-fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink)
-               -> fmt::Result {
-    try!(write!(w, "<h3 class='impl'>{}<code>impl{} ",
-                ConciseStability(&i.stability),
-                i.impl_.generics));
-    if let Some(clean::ImplPolarity::Negative) = i.impl_.polarity {
-        try!(write!(w, "!"));
-    }
-    if let Some(ref ty) = i.impl_.trait_ {
-        try!(write!(w, "{} for ", *ty));
+fn render_deref_methods(w: &mut fmt::Formatter, impl_: &Impl) -> fmt::Result {
+    let deref_type = impl_.impl_.trait_.as_ref().unwrap();
+    let target = impl_.impl_.items.iter().filter_map(|item| {
+        match item.inner {
+            clean::TypedefItem(ref t) => Some(&t.type_),
+            _ => None,
+        }
+    }).next().unwrap();
+    let what = MethodRender::DerefFor { trait_: deref_type, type_: target };
+    match *target {
+        clean::ResolvedPath { did, .. } => render_methods(w, did, what),
+        _ => {
+            if let Some(prim) = target.primitive_type() {
+                if let Some(c) = cache().primitive_locations.get(&prim) {
+                    let did = ast::DefId { krate: *c, node: prim.to_node_id() };
+                    try!(render_methods(w, did, what));
+                }
+            }
+            Ok(())
+        }
     }
-    try!(write!(w, "{}{}</code></h3>", i.impl_.for_,
-                WhereClause(&i.impl_.generics)));
-    if let Some(ref dox) = i.dox {
-        try!(write!(w, "<div class='docblock'>{}</div>", Markdown(dox)));
+}
+
+fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink,
+               render_header: bool) -> fmt::Result {
+    if render_header {
+        try!(write!(w, "<h3 class='impl'><code>impl{} ",
+                    i.impl_.generics));
+        if let Some(clean::ImplPolarity::Negative) = i.impl_.polarity {
+            try!(write!(w, "!"));
+        }
+        if let Some(ref ty) = i.impl_.trait_ {
+            try!(write!(w, "{} for ", *ty));
+        }
+        try!(write!(w, "{}{}</code></h3>", i.impl_.for_,
+                    WhereClause(&i.impl_.generics)));
+        if let Some(ref dox) = i.dox {
+            try!(write!(w, "<div class='docblock'>{}</div>", Markdown(dox)));
+        }
     }
 
     fn doctraititem(w: &mut fmt::Formatter, item: &clean::Item,
-                    dox: bool, link: MethodLink) -> fmt::Result {
+                    link: MethodLink) -> fmt::Result {
         match item.inner {
             clean::MethodItem(..) | clean::TyMethodItem(..) => {
-                try!(write!(w, "<h4 id='method.{}' class='{}'>{}<code>",
+                try!(write!(w, "<h4 id='method.{}' class='{}'><code>",
                             *item.name.as_ref().unwrap(),
-                            shortty(item),
-                            ConciseStability(&item.stability)));
+                            shortty(item)));
                 try!(render_method(w, item, link));
                 try!(write!(w, "</code></h4>\n"));
             }
             clean::TypedefItem(ref tydef) => {
                 let name = item.name.as_ref().unwrap();
-                try!(write!(w, "<h4 id='assoc_type.{}' class='{}'>{}<code>",
+                try!(write!(w, "<h4 id='assoc_type.{}' class='{}'><code>",
                             *name,
-                            shortty(item),
-                            ConciseStability(&item.stability)));
+                            shortty(item)));
                 try!(write!(w, "type {} = {}", name, tydef.type_));
                 try!(write!(w, "</code></h4>\n"));
             }
             clean::AssociatedTypeItem(ref bounds, ref default) => {
                 let name = item.name.as_ref().unwrap();
-                try!(write!(w, "<h4 id='assoc_type.{}' class='{}'>{}<code>",
+                try!(write!(w, "<h4 id='assoc_type.{}' class='{}'><code>",
                             *name,
-                            shortty(item),
-                            ConciseStability(&item.stability)));
+                            shortty(item)));
                 try!(assoc_type(w, item, bounds, default));
                 try!(write!(w, "</code></h4>\n"));
             }
             _ => panic!("can't make docs for trait item with name {:?}", item.name)
         }
-        match item.doc_value() {
-            Some(s) if dox => {
-                try!(write!(w, "<div class='docblock'>{}</div>", Markdown(s)));
-                Ok(())
-            }
-            Some(..) | None => Ok(())
+        if let MethodLink::Anchor = link {
+            document(w, item)
+        } else {
+            Ok(())
         }
     }
 
     try!(write!(w, "<div class='impl-items'>"));
     for trait_item in i.impl_.items.iter() {
-        try!(doctraititem(w, trait_item, true, link));
+        try!(doctraititem(w, trait_item, link));
     }
 
     fn render_default_methods(w: &mut fmt::Formatter,
@@ -2271,8 +2350,7 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink)
                 None => {}
             }
 
-            try!(doctraititem(w, trait_item, false,
-                              MethodLink::GotoSource(did)));
+            try!(doctraititem(w, trait_item, MethodLink::GotoSource(did)));
         }
         Ok(())
     }
@@ -2380,7 +2458,7 @@ fn item_primitive(w: &mut fmt::Formatter,
                   it: &clean::Item,
                   _p: &clean::PrimitiveType) -> fmt::Result {
     try!(document(w, it));
-    render_methods(w, it)
+    render_methods(w, it.def_id, MethodRender::All)
 }
 
 fn get_basic_keywords() -> &'static str {
diff --git a/src/librustdoc/html/static/main.css b/src/librustdoc/html/static/main.css
index 2af20ce59da..c94dbc15103 100644
--- a/src/librustdoc/html/static/main.css
+++ b/src/librustdoc/html/static/main.css
@@ -245,6 +245,10 @@ nav.sub {
 .content .highlighted.tymethod { background-color: #c6afb3; }
 .content .highlighted.type { background-color: #c6afb3; }
 
+.docblock.short p {
+    display: inline;
+}
+
 .docblock.short.nowrap {
     display: block;
     overflow: hidden;
@@ -337,11 +341,16 @@ nav.sub {
 /* Shift "where ..." part of method definition down a line */
 .content .method .where { display: block; }
 /* Bit of whitespace to indent it */
-.content .method .where::before { content: '      '; }
+.content .method .where::before { content: '  '; }
 
-.content .methods .docblock { margin-left: 40px; }
+.content .methods > div { margin-left: 40px; }
 
-.content .impl-items .docblock { margin-left: 40px; }
+.content .impl-items .docblock, .content .impl-items .stability {
+    margin-left: 40px;
+}
+.content .impl-items .method, .content .impl-items .type {
+    margin-left: 20px;
+}
 
 nav {
     border-bottom: 1px solid #e0e0e0;
@@ -468,30 +477,31 @@ a {
     padding: 20px;
 }
 
-.stability {
-    border-left: 6px solid;
-    padding: 3px 6px;
-    border-radius: 3px;
+em.stab.unstable { background: #FFF5D6; border-color: #FFC600; }
+em.stab.deprecated { background: #F3DFFF; border-color: #7F0087; }
+em.stab {
+    display: inline-block;
+    border-width: 1px;
+    border-style: solid;
+    padding: 3px;
+    margin-bottom: 5px;
+    font-size: 90%;
+    font-style: normal;
 }
-
-h1 .stability {
-    text-transform: lowercase;
-    font-weight: 400;
-    margin-left: 14px;
-    padding: 4px 10px;
+em.stab p {
+    display: inline;
 }
 
-.impl-items .stability, .methods .stability {
-    margin-right: 20px;
+.module-item .stab {
+    border-width: 0;
+    padding: 0;
+    margin: 0;
+    background: inherit !important;
 }
 
-.stability.Deprecated { border-color: #A071A8; color: #82478C; }
-.stability.Experimental { border-color: #D46D6A; color: #AA3C39; }
-.stability.Unstable { border-color: #D4B16A; color: #AA8439; }
-.stability.Stable { border-color: #54A759; color: #2D8632; }
-.stability.Frozen { border-color: #009431; color: #007726; }
-.stability.Locked { border-color: #0084B6; color: #00668c; }
-.stability.Unmarked { border-color: #BBBBBB; }
+.module-item.unstable {
+    opacity: 0.65;
+}
 
 td.summary-column {
     width: 100%;
@@ -500,11 +510,6 @@ td.summary-column {
 .summary {
     padding-right: 0px;
 }
-.summary.Deprecated { background-color: #A071A8; }
-.summary.Experimental { background-color: #D46D6A; }
-.summary.Unstable { background-color: #D4B16A; }
-.summary.Stable { background-color: #54A759; }
-.summary.Unmarked { background-color: #BBBBBB; }
 
 :target { background: #FDFFD3; }
 .line-numbers :target { background-color: transparent; }
@@ -555,9 +560,9 @@ pre.rust { position: relative; }
 .collapse-toggle {
     font-weight: 300;
     position: absolute;
-    left: 13px;
+    left: -23px;
     color: #999;
-    margin-top: 2px;
+    top: 0;
 }
 
 .toggle-wrapper > .collapse-toggle {
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index c3ab375a9e2..0379c04be4d 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -802,6 +802,9 @@
     if (query['gotosrc']) {
         window.location = $('#src-' + query['gotosrc']).attr('href');
     }
+    if (query['gotomacrosrc']) {
+        window.location = $('.srclink').attr('href');
+    }
 
     $("#expand-all").on("click", function() {
         $(".docblock").show();
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 008da466db0..2682bbf4662 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -91,7 +91,6 @@ pub mod html {
 pub mod markdown;
 pub mod passes;
 pub mod plugins;
-pub mod stability_summary;
 pub mod visit_ast;
 pub mod test;
 mod flock;
diff --git a/src/librustdoc/stability_summary.rs b/src/librustdoc/stability_summary.rs
deleted file mode 100644
index 3e4f6896ee6..00000000000
--- a/src/librustdoc/stability_summary.rs
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright 2014 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.
-
-//! This module crawls a `clean::Crate` and produces a summarization of the
-//! stability levels within the crate. The summary contains the module
-//! hierarchy, with item counts for every stability level per module. A parent
-//! module's count includes its children's.
-
-use std::cmp::Ordering;
-use std::ops::Add;
-
-use syntax::attr::{Unstable, Stable};
-use syntax::ast::Public;
-
-use clean::{Crate, Item, ModuleItem, Module, EnumItem, Enum};
-use clean::{ImplItem, Impl, Trait, TraitItem};
-use clean::{ExternCrateItem, ImportItem, PrimitiveItem, Stability};
-
-use html::render::cache;
-
-#[derive(RustcEncodable, RustcDecodable, PartialEq, Eq)]
-/// The counts for each stability level.
-#[derive(Copy, Clone)]
-pub struct Counts {
-    pub deprecated: u64,
-    pub unstable: u64,
-    pub stable: u64,
-
-    /// No stability level, inherited or otherwise.
-    pub unmarked: u64,
-}
-
-impl Add for Counts {
-    type Output = Counts;
-
-    fn add(self, other: Counts) -> Counts {
-        Counts {
-            deprecated:   self.deprecated   + other.deprecated,
-            unstable:     self.unstable     + other.unstable,
-            stable:       self.stable       + other.stable,
-            unmarked:     self.unmarked     + other.unmarked,
-        }
-    }
-}
-
-impl Counts {
-    fn zero() -> Counts {
-        Counts {
-            deprecated:   0,
-            unstable:     0,
-            stable:       0,
-            unmarked:     0,
-        }
-    }
-
-    pub fn total(&self) -> u64 {
-        self.deprecated + self.unstable + self.stable + self.unmarked
-    }
-}
-
-#[derive(RustcEncodable, RustcDecodable, PartialEq, Eq)]
-/// A summarized module, which includes total counts and summarized children
-/// modules.
-pub struct ModuleSummary {
-    pub name: String,
-    pub counts: Counts,
-    pub submodules: Vec<ModuleSummary>,
-}
-
-impl PartialOrd for ModuleSummary {
-    fn partial_cmp(&self, other: &ModuleSummary) -> Option<Ordering> {
-        self.name.partial_cmp(&other.name)
-    }
-}
-
-impl Ord for ModuleSummary {
-    fn cmp(&self, other: &ModuleSummary) -> Ordering {
-        self.name.cmp(&other.name)
-    }
-}
-
-// is the item considered publicly visible?
-fn visible(item: &Item) -> bool {
-    match item.inner {
-        ImplItem(_) => true,
-        _ => item.visibility == Some(Public)
-    }
-}
-
-fn count_stability(stab: Option<&Stability>) -> Counts {
-    match stab {
-        None            => Counts { unmarked: 1,     .. Counts::zero() },
-        Some(ref stab) => {
-            if !stab.deprecated_since.is_empty() {
-                return Counts { deprecated: 1, .. Counts::zero() };
-            }
-            match stab.level {
-                Unstable    => Counts { unstable: 1,     .. Counts::zero() },
-                Stable      => Counts { stable: 1,       .. Counts::zero() },
-            }
-        }
-    }
-}
-
-fn summarize_methods(item: &Item) -> Counts {
-    match cache().impls.get(&item.def_id) {
-        Some(v) => {
-            v.iter().map(|i| {
-                let count = count_stability(i.stability.as_ref());
-                if i.impl_.trait_.is_none() {
-                    count + i.impl_.items.iter()
-                        .map(|ti| summarize_item(ti).0)
-                        .fold(Counts::zero(), |acc, c| acc + c)
-                } else {
-                    count
-                }
-            }).fold(Counts::zero(), |acc, c| acc + c)
-        },
-        None => {
-            Counts::zero()
-        },
-    }
-}
-
-
-// Produce the summary for an arbitrary item. If the item is a module, include a
-// module summary. The counts for items with nested items (e.g. modules, traits,
-// impls) include all children counts.
-fn summarize_item(item: &Item) -> (Counts, Option<ModuleSummary>) {
-    let item_counts = count_stability(item.stability.as_ref()) + summarize_methods(item);
-
-    // Count this item's children, if any. Note that a trait impl is
-    // considered to have no children.
-    match item.inner {
-        // Require explicit `pub` to be visible
-        ImplItem(Impl { ref items, trait_: None, .. }) => {
-            let subcounts = items.iter().filter(|i| visible(*i))
-                                        .map(summarize_item)
-                                        .map(|s| s.0)
-                                        .fold(Counts::zero(), |acc, x| acc + x);
-            (subcounts, None)
-        }
-        // `pub` automatically
-        EnumItem(Enum { variants: ref subitems, .. }) => {
-            let subcounts = subitems.iter().map(summarize_item)
-                                           .map(|s| s.0)
-                                           .fold(Counts::zero(), |acc, x| acc + x);
-            (item_counts + subcounts, None)
-        }
-        TraitItem(Trait { ref items, .. }) => {
-            let subcounts = items.iter().map(summarize_item)
-                                        .map(|s| s.0)
-                                        .fold(Counts::zero(), |acc, x| acc + x);
-            (item_counts + subcounts, None)
-        }
-        ModuleItem(Module { ref items, .. }) => {
-            let mut counts = item_counts;
-            let mut submodules = Vec::new();
-
-            for (subcounts, submodule) in items.iter().filter(|i| visible(*i))
-                                                      .map(summarize_item) {
-                counts = counts + subcounts;
-                submodule.map(|m| submodules.push(m));
-            }
-            submodules.sort();
-
-            (counts, Some(ModuleSummary {
-                name: item.name.as_ref().map_or("".to_string(), |n| n.clone()),
-                counts: counts,
-                submodules: submodules,
-            }))
-        }
-        // no stability information for the following items:
-        ExternCrateItem(..) | ImportItem(_) |
-        PrimitiveItem(_) => (Counts::zero(), None),
-        _ => (item_counts, None)
-    }
-}
-
-/// Summarizes the stability levels in a crate.
-pub fn build(krate: &Crate) -> ModuleSummary {
-    match krate.module {
-        None => ModuleSummary {
-            name: krate.name.clone(),
-            counts: Counts::zero(),
-            submodules: Vec::new(),
-        },
-        Some(ref item) => ModuleSummary {
-            name: krate.name.clone(), .. summarize_item(item).1.unwrap()
-        }
-    }
-}
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index 449f9c79d1d..bbe0a6f2675 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use std::cell::RefCell;
+use std::cell::{RefCell, Cell};
 use std::collections::{HashSet, HashMap};
 use std::dynamic_lib::DynamicLibrary;
 use std::env;
@@ -92,6 +92,7 @@ pub fn run(input: &str,
         external_typarams: RefCell::new(None),
         inlined: RefCell::new(None),
         populated_crate_impls: RefCell::new(HashSet::new()),
+        deref_trait_did: Cell::new(None),
     };
 
     let mut v = RustdocVisitor::new(&ctx, None);
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index e4f4dbaafbe..4ad693578cc 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -398,6 +398,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             name: def.ident,
             whence: def.span,
             stab: self.stability(def.id),
+            imported_from: def.imported_from,
         }
     }
 }
diff --git a/src/test/auxiliary/issue-19190-3.rs b/src/test/auxiliary/issue-19190-3.rs
new file mode 100644
index 00000000000..435f0176162
--- /dev/null
+++ b/src/test/auxiliary/issue-19190-3.rs
@@ -0,0 +1,30 @@
+// Copyright 2015 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.
+
+use std::ops::Deref;
+
+pub struct Foo;
+
+impl Deref for Foo {
+    type Target = i32;
+    fn deref(&self) -> &i32 { loop {} }
+}
+
+pub struct Bar;
+pub struct Baz;
+
+impl Baz {
+    pub fn baz(&self) {}
+}
+
+impl Deref for Bar {
+    type Target = Baz;
+    fn deref(&self) -> &Baz { loop {} }
+}
diff --git a/src/test/rustdoc/issue-19190-2.rs b/src/test/rustdoc/issue-19190-2.rs
new file mode 100644
index 00000000000..b84ec6d6166
--- /dev/null
+++ b/src/test/rustdoc/issue-19190-2.rs
@@ -0,0 +1,22 @@
+// Copyright 2015 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.
+
+use std::ops::Deref;
+
+pub struct Bar;
+
+impl Deref for Bar {
+    type Target = i32;
+    fn deref(&self) -> &i32 { loop {} }
+}
+
+// @has issue_19190_2/struct.Bar.html
+// @has - '//*[@id="method.count_ones"]' 'fn count_ones(self) -> u32'
+
diff --git a/src/test/rustdoc/issue-19190-3.rs b/src/test/rustdoc/issue-19190-3.rs
new file mode 100644
index 00000000000..c315ea26d26
--- /dev/null
+++ b/src/test/rustdoc/issue-19190-3.rs
@@ -0,0 +1,35 @@
+// Copyright 2015 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.
+
+// aux-build:issue-19190-3.rs
+// ignore-android
+
+extern crate issue_19190_3;
+
+use std::ops::Deref;
+use issue_19190_3::Baz;
+
+// @has issue_19190_3/struct.Foo.html
+// @has - '//*[@id="method.count_ones"]' 'fn count_ones(self) -> u32'
+pub use issue_19190_3::Foo;
+
+// @has issue_19190_3/struct.Bar.html
+// @has - '//*[@id="method.baz"]' 'fn baz(&self)'
+pub use issue_19190_3::Bar;
+
+// @has issue_19190_3/struct.MyBar.html
+// @has - '//*[@id="method.baz"]' 'fn baz(&self)'
+pub struct MyBar;
+
+impl Deref for MyBar {
+    type Target = Baz;
+    fn deref(&self) -> &Baz { loop {} }
+}
+
diff --git a/src/test/rustdoc/issue-19190.rs b/src/test/rustdoc/issue-19190.rs
new file mode 100644
index 00000000000..f011a3e64a6
--- /dev/null
+++ b/src/test/rustdoc/issue-19190.rs
@@ -0,0 +1,26 @@
+// Copyright 2015 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.
+
+use std::ops::Deref;
+
+pub struct Foo;
+pub struct Bar;
+
+impl Foo {
+    pub fn foo(&self) {}
+}
+
+impl Deref for Bar {
+    type Target = Foo;
+    fn deref(&self) -> &Foo { loop {} }
+}
+
+// @has issue_19190/struct.Bar.html
+// @has - '//*[@id="method.foo"]' 'fn foo(&self)'