about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAaron Turon <aturon@mozilla.com>2014-06-26 11:37:39 -0700
committerAaron Turon <aturon@mozilla.com>2014-06-30 22:36:24 -0700
commit256df5e3df5fecbd8e9a8ca1b7a370b0ce22efee (patch)
tree8d920ea2f265cc1f82b300048d508ade82cdc17c
parent5cef1243a21c162cb9968f9bc14d1a23ba88426e (diff)
downloadrust-256df5e3df5fecbd8e9a8ca1b7a370b0ce22efee.tar.gz
rust-256df5e3df5fecbd8e9a8ca1b7a370b0ce22efee.zip
rustdoc: incorporate stability index throughout
This commit hooks rustdoc into the stability index infrastructure in two
ways:

1. It looks up stability levels via the index, rather than by manual
attributes.

2. It adds stability level information throughout rustdoc output, rather
than just at the top header. In particular, a stability color (with
mouseover text) appears next to essentially every item that appears
in rustdoc's HTML output.

Along the way, the stability index code has been lightly refactored.
-rw-r--r--src/librustc/lint/builtin.rs11
-rw-r--r--src/librustc/metadata/encoder.rs18
-rw-r--r--src/librustc/middle/stability.rs32
-rw-r--r--src/librustdoc/clean/inline.rs3
-rw-r--r--src/librustdoc/clean/mod.rs152
-rw-r--r--src/librustdoc/core.rs15
-rw-r--r--src/librustdoc/doctree.rs12
-rw-r--r--src/librustdoc/fold.rs4
-rw-r--r--src/librustdoc/html/format.rs35
-rw-r--r--src/librustdoc/html/render.rs123
-rw-r--r--src/librustdoc/html/static/main.css28
-rw-r--r--src/librustdoc/visit_ast.rs21
12 files changed, 282 insertions, 172 deletions
diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index b562d48f49d..30296cb3186 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -30,7 +30,7 @@ use middle::def::*;
 use middle::trans::adt; // for `adt::is_ffi_safe`
 use middle::typeck::astconv::ast_ty_to_ty;
 use middle::typeck::infer;
-use middle::{typeck, ty, def, pat_util};
+use middle::{typeck, ty, def, pat_util, stability};
 use util::ppaux::{ty_to_str};
 use util::nodemap::NodeSet;
 use lint::{Context, LintPass, LintArray};
@@ -1426,11 +1426,7 @@ impl LintPass for Stability {
                     Some(method) => {
                         match method.origin {
                             typeck::MethodStatic(def_id) => {
-                                // If this implements a trait method, get def_id
-                                // of the method inside trait definition.
-                                // Otherwise, use the current def_id (which refers
-                                // to the method inside impl).
-                                ty::trait_method_of_method(cx.tcx, def_id).unwrap_or(def_id)
+                                def_id
                             }
                             typeck::MethodParam(typeck::MethodParam {
                                 trait_id: trait_id,
@@ -1454,8 +1450,7 @@ impl LintPass for Stability {
         // check anything for crate-local usage.
         if ast_util::is_local(id) { return }
 
-        let stability = cx.tcx.stability.borrow_mut().lookup(&cx.tcx.sess.cstore, id);
-
+        let stability = stability::lookup(cx.tcx, id);
         let (lint, label) = match stability {
             // no stability attributes == Unstable
             None => (UNSTABLE, "unmarked"),
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index c5789e4442a..96284f8de26 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -24,6 +24,7 @@ use middle::ty::{node_id_to_type, lookup_item_type};
 use middle::astencode;
 use middle::ty;
 use middle::typeck;
+use middle::stability;
 use middle;
 use util::nodemap::{NodeMap, NodeSet};
 
@@ -328,7 +329,7 @@ fn encode_enum_variant_info(ecx: &EncodeContext,
         encode_visibility(ebml_w, variant.node.vis);
         encode_attributes(ebml_w, variant.node.attrs.as_slice());
 
-        let stab = ecx.tcx.stability.borrow().lookup_local(variant.node.id);
+        let stab = stability::lookup(ecx.tcx, ast_util::local_def(variant.node.id));
         encode_stability(ebml_w, stab);
 
         match variant.node.kind {
@@ -592,7 +593,9 @@ fn encode_info_for_mod(ecx: &EncodeContext,
 
     encode_path(ebml_w, path.clone());
     encode_visibility(ebml_w, vis);
-    encode_stability(ebml_w, ecx.tcx.stability.borrow().lookup_local(id));
+
+    let stab = stability::lookup(ecx.tcx, ast_util::local_def(id));
+    encode_stability(ebml_w, stab);
 
     // Encode the reexports of this module, if this module is public.
     if vis == Public {
@@ -722,7 +725,8 @@ fn encode_info_for_struct_ctor(ecx: &EncodeContext,
         encode_symbol(ecx, ebml_w, ctor_id);
     }
 
-    encode_stability(ebml_w, ecx.tcx.stability.borrow().lookup_local(ctor_id));
+    let stab = stability::lookup(ecx.tcx, ast_util::local_def(ctor_id));
+    encode_stability(ebml_w, stab);
 
     // indicate that this is a tuple struct ctor, because downstream users will normally want
     // the tuple struct definition, but without this there is no way for them to tell that
@@ -768,7 +772,7 @@ fn encode_info_for_method(ecx: &EncodeContext,
     encode_method_ty_fields(ecx, ebml_w, m);
     encode_parent_item(ebml_w, local_def(parent_id));
 
-    let stab = ecx.tcx.stability.borrow().lookup_local(m.def_id.node);
+    let stab = stability::lookup(ecx.tcx, m.def_id);
     encode_stability(ebml_w, stab);
 
     // The type for methods gets encoded twice, which is unfortunate.
@@ -915,10 +919,10 @@ fn encode_info_for_item(ecx: &EncodeContext,
     }
 
     debug!("encoding info for item at {}",
-           ecx.tcx.sess.codemap().span_to_str(item.span));
+           tcx.sess.codemap().span_to_str(item.span));
 
     let def_id = local_def(item.id);
-    let stab = tcx.stability.borrow().lookup_local(item.id);
+    let stab = stability::lookup(tcx, ast_util::local_def(item.id));
 
     match item.node {
       ItemStatic(_, m, _) => {
@@ -1206,7 +1210,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
             encode_method_ty_fields(ecx, ebml_w, &*method_ty);
             encode_parent_item(ebml_w, def_id);
 
-            let stab = tcx.stability.borrow().lookup_local(method_def_id.node);
+            let stab = stability::lookup(tcx, method_def_id);
             encode_stability(ebml_w, stab);
 
             let elem = ast_map::PathName(method_ty.ident.name);
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index fc76648ec55..ac17bd07503 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -20,7 +20,8 @@ use syntax::ast::{Generics, StructDef, Ident};
 use syntax::ast_util::is_local;
 use syntax::attr::Stability;
 use syntax::visit::{FnKind, FkMethod, Visitor};
-use metadata::{cstore, csearch};
+use middle::ty;
+use metadata::csearch;
 
 /// A stability index, giving the stability level for items and methods.
 pub struct Index {
@@ -105,21 +106,24 @@ impl Index {
                           attr::find_stability(krate.attrs.as_slice()));
         annotator.index
     }
+}
 
-    /// Lookup the stability for a node, loading external crate
-    /// metadata as necessary.
-    pub fn lookup(&mut self, cstore: &cstore::CStore, id: DefId) -> Option<Stability> {
-        if is_local(id) {
-            self.lookup_local(id.node)
-        } else {
-            let stab = csearch::get_stability(cstore, id);
-            self.extern_cache.insert(id, stab.clone());
+/// Lookup the stability for a node, loading external crate
+/// metadata as necessary.
+pub fn lookup(tcx: &ty::ctxt,  id: DefId) -> Option<Stability> {
+    // is this definition the implementation of a trait method?
+    match ty::trait_method_of_method(tcx, id) {
+        Some(trait_method_id) if trait_method_id != id => {
+            lookup(tcx, trait_method_id)
+        }
+        _ if is_local(id) => {
+            tcx.stability.borrow().local.find_copy(&id.node)
+        }
+        _ => {
+            let stab = csearch::get_stability(&tcx.sess.cstore, id);
+            let mut index = tcx.stability.borrow_mut();
+            (*index).extern_cache.insert(id, stab.clone());
             stab
         }
     }
-
-    /// Lookup the stability for a local node without loading any external crates
-    pub fn lookup_local(&self, id: NodeId) -> Option<Stability> {
-        self.local.find_copy(&id)
-    }
 }
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index d243c61ddaf..2d498e7f302 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -18,6 +18,7 @@ use rustc::metadata::csearch;
 use rustc::metadata::decoder;
 use rustc::middle::def;
 use rustc::middle::ty;
+use rustc::middle::stability;
 
 use core;
 use doctree;
@@ -102,6 +103,7 @@ fn try_inline_def(cx: &core::DocContext,
         attrs: load_attrs(tcx, did),
         inner: inner,
         visibility: Some(ast::Public),
+        stability: stability::lookup(tcx, did).clean(),
         def_id: did,
     });
     Some(ret)
@@ -317,6 +319,7 @@ fn build_impl(cx: &core::DocContext,
         name: None,
         attrs: attrs,
         visibility: Some(ast::Inherited),
+        stability: stability::lookup(tcx, did).clean(),
         def_id: did,
     })
 }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index d7bbb439dbb..87151708812 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -29,6 +29,7 @@ use rustc::middle::def;
 use rustc::middle::subst;
 use rustc::middle::subst::VecPerParamSpace;
 use rustc::middle::ty;
+use rustc::middle::stability;
 
 use std::rc::Rc;
 use std::u32;
@@ -44,6 +45,17 @@ pub static SCHEMA_VERSION: &'static str = "0.8.3";
 
 mod inline;
 
+// load the current DocContext from TLD
+fn get_cx() -> Gc<core::DocContext> {
+    *super::ctxtkey.get().unwrap()
+}
+
+// extract the stability index for a node from TLD, if possible
+fn get_stability(def_id: ast::DefId) -> Option<Stability> {
+    get_cx().tcx_opt().and_then(|tcx| stability::lookup(tcx, def_id))
+            .map(|stab| stab.clean())
+}
+
 pub trait Clean<T> {
     fn clean(&self) -> T;
 }
@@ -97,7 +109,7 @@ pub struct Crate {
 
 impl<'a> Clean<Crate> for visit_ast::RustdocVisitor<'a> {
     fn clean(&self) -> Crate {
-        let cx = super::ctxtkey.get().unwrap();
+        let cx = get_cx();
 
         let mut externs = Vec::new();
         cx.sess().cstore.iter_crate_data(|n, meta| {
@@ -158,6 +170,7 @@ impl<'a> Clean<Crate> for visit_ast::RustdocVisitor<'a> {
                     name: Some(prim.to_url_str().to_string()),
                     attrs: Vec::new(),
                     visibility: None,
+                    stability: None,
                     def_id: ast_util::local_def(prim.to_node_id()),
                     inner: PrimitiveItem(prim),
                 };
@@ -193,25 +206,18 @@ pub struct ExternalCrate {
 impl Clean<ExternalCrate> for cstore::crate_metadata {
     fn clean(&self) -> ExternalCrate {
         let mut primitives = Vec::new();
-        let cx = super::ctxtkey.get().unwrap();
-        match cx.maybe_typed {
-            core::Typed(ref tcx) => {
-                csearch::each_top_level_item_of_crate(&tcx.sess.cstore,
-                                                      self.cnum,
-                                                      |def, _, _| {
-                    let did = match def {
-                        decoder::DlDef(def::DefMod(did)) => did,
-                        _ => return
-                    };
-                    let attrs = inline::load_attrs(tcx, did);
-                    match Primitive::find(attrs.as_slice()) {
-                        Some(prim) => primitives.push(prim),
-                        None => {}
-                    }
-                });
-            }
-            core::NotTyped(..) => {}
-        }
+        get_cx().tcx_opt().map(|tcx| {
+            csearch::each_top_level_item_of_crate(&tcx.sess.cstore,
+                                                  self.cnum,
+                                                  |def, _, _| {
+                let did = match def {
+                    decoder::DlDef(def::DefMod(did)) => did,
+                    _ => return
+                };
+                let attrs = inline::load_attrs(tcx, did);
+                Primitive::find(attrs.as_slice()).map(|prim| primitives.push(prim));
+            })
+        });
         ExternalCrate {
             name: self.name.to_string(),
             attrs: decoder::get_crate_attributes(self.data()).clean(),
@@ -233,6 +239,7 @@ pub struct Item {
     pub inner: ItemEnum,
     pub visibility: Option<Visibility>,
     pub def_id: ast::DefId,
+    pub stability: Option<Stability>,
 }
 
 impl Item {
@@ -380,6 +387,7 @@ impl Clean<Item> for doctree::Module {
             attrs: self.attrs.clean(),
             source: where.clean(),
             visibility: self.vis.clean(),
+            stability: self.stab.clean(),
             def_id: ast_util::local_def(self.id),
             inner: ModuleItem(Module {
                is_crate: self.is_crate,
@@ -465,9 +473,8 @@ impl Clean<TyParam> for ast::TyParam {
 
 impl Clean<TyParam> for ty::TypeParameterDef {
     fn clean(&self) -> TyParam {
-        let cx = super::ctxtkey.get().unwrap();
-        cx.external_typarams.borrow_mut().get_mut_ref().insert(self.def_id,
-                                                               self.ident.clean());
+        get_cx().external_typarams.borrow_mut().get_mut_ref()
+                .insert(self.def_id, self.ident.clean());
         TyParam {
             name: self.ident.clean(),
             did: self.def_id,
@@ -515,7 +522,7 @@ fn external_path(name: &str, substs: &subst::Substs) -> Path {
 
 impl Clean<TyParamBound> for ty::BuiltinBound {
     fn clean(&self) -> TyParamBound {
-        let cx = super::ctxtkey.get().unwrap();
+        let cx = get_cx();
         let tcx = match cx.maybe_typed {
             core::Typed(ref tcx) => tcx,
             core::NotTyped(_) => return RegionBound,
@@ -550,7 +557,7 @@ impl Clean<TyParamBound> for ty::BuiltinBound {
 
 impl Clean<TyParamBound> for ty::TraitRef {
     fn clean(&self) -> TyParamBound {
-        let cx = super::ctxtkey.get().unwrap();
+        let cx = get_cx();
         let tcx = match cx.maybe_typed {
             core::Typed(ref tcx) => tcx,
             core::NotTyped(_) => return RegionBound,
@@ -709,8 +716,9 @@ impl Clean<Item> for ast::Method {
             name: Some(self.ident.clean()),
             attrs: self.attrs.clean().move_iter().collect(),
             source: self.span.clean(),
-            def_id: ast_util::local_def(self.id.clone()),
+            def_id: ast_util::local_def(self.id),
             visibility: self.vis.clean(),
+            stability: get_stability(ast_util::local_def(self.id)),
             inner: MethodItem(Method {
                 generics: self.generics.clean(),
                 self_: self.explicit_self.node.clean(),
@@ -749,6 +757,7 @@ impl Clean<Item> for ast::TypeMethod {
             source: self.span.clean(),
             def_id: ast_util::local_def(self.id),
             visibility: None,
+            stability: get_stability(ast_util::local_def(self.id)),
             inner: TyMethodItem(TyMethod {
                 fn_style: self.fn_style.clone(),
                 decl: decl,
@@ -792,6 +801,7 @@ impl Clean<Item> for doctree::Function {
             attrs: self.attrs.clean(),
             source: self.where.clean(),
             visibility: self.vis.clean(),
+            stability: self.stab.clean(),
             def_id: ast_util::local_def(self.id),
             inner: FunctionItem(Function {
                 decl: self.decl.clean(),
@@ -854,14 +864,10 @@ impl Clean<FnDecl> for ast::FnDecl {
 
 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 cx = get_cx();
         let (did, sig) = *self;
         let mut names = if did.node != 0 {
-            csearch::get_method_arg_names(&tcx.sess.cstore, did).move_iter()
+            csearch::get_method_arg_names(&cx.tcx().sess.cstore, did).move_iter()
         } else {
             Vec::new().move_iter()
         }.peekable();
@@ -932,6 +938,7 @@ impl Clean<Item> for doctree::Trait {
             source: self.where.clean(),
             def_id: ast_util::local_def(self.id),
             visibility: self.vis.clean(),
+            stability: self.stab.clean(),
             inner: TraitItem(Trait {
                 methods: self.methods.clean(),
                 generics: self.generics.clean(),
@@ -985,11 +992,7 @@ impl Clean<TraitMethod> for ast::TraitMethod {
 
 impl Clean<Item> for ty::Method {
     fn clean(&self) -> Item {
-        let cx = super::ctxtkey.get().unwrap();
-        let tcx = match cx.maybe_typed {
-            core::Typed(ref tcx) => tcx,
-            core::NotTyped(_) => unreachable!(),
-        };
+        let cx = get_cx();
         let (self_, sig) = match self.explicit_self {
             ast::SelfStatic => (ast::SelfStatic.clean(), self.fty.sig.clone()),
             s => {
@@ -1015,8 +1018,9 @@ impl Clean<Item> for ty::Method {
         Item {
             name: Some(self.ident.clean()),
             visibility: Some(ast::Inherited),
+            stability: get_stability(self.def_id),
             def_id: self.def_id,
-            attrs: inline::load_attrs(tcx, self.def_id),
+            attrs: inline::load_attrs(cx.tcx(), self.def_id),
             source: Span::empty(),
             inner: TyMethodItem(TyMethod {
                 fn_style: self.fty.fn_style,
@@ -1261,12 +1265,7 @@ impl Clean<Type> for ty::t {
             ty::ty_struct(did, ref substs) |
             ty::ty_enum(did, ref substs) |
             ty::ty_trait(box ty::TyTrait { def_id: did, ref substs, .. }) => {
-                let cx = super::ctxtkey.get().unwrap();
-                let tcx = match cx.maybe_typed {
-                    core::Typed(ref tycx) => tycx,
-                    core::NotTyped(_) => unreachable!(),
-                };
-                let fqn = csearch::get_item_path(tcx, did);
+                let fqn = csearch::get_item_path(get_cx().tcx(), did);
                 let fqn: Vec<String> = fqn.move_iter().map(|i| {
                     i.to_str()
                 }).collect();
@@ -1277,8 +1276,8 @@ impl Clean<Type> for ty::t {
                 };
                 let path = external_path(fqn.last().unwrap().to_str().as_slice(),
                                          substs);
-                cx.external_paths.borrow_mut().get_mut_ref().insert(did,
-                                                                    (fqn, kind));
+                get_cx().external_paths.borrow_mut().get_mut_ref()
+                                       .insert(did, (fqn, kind));
                 ResolvedPath {
                     path: path,
                     typarams: None,
@@ -1318,6 +1317,7 @@ impl Clean<Item> for ast::StructField {
             attrs: self.node.attrs.clean().move_iter().collect(),
             source: self.span.clean(),
             visibility: Some(vis),
+            stability: get_stability(ast_util::local_def(self.node.id)),
             def_id: ast_util::local_def(self.node.id),
             inner: StructFieldItem(TypedStructField(self.node.ty.clean())),
         }
@@ -1332,17 +1332,14 @@ impl Clean<Item> for ty::field_ty {
         } 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);
+        let cx = get_cx();
+        let ty = ty::lookup_item_type(cx.tcx(), self.id);
         Item {
             name: name.clean(),
-            attrs: inline::load_attrs(tcx, self.id),
+            attrs: inline::load_attrs(cx.tcx(), self.id),
             source: Span::empty(),
             visibility: Some(self.vis),
+            stability: get_stability(self.id),
             def_id: self.id,
             inner: StructFieldItem(TypedStructField(ty.ty.clean())),
         }
@@ -1373,6 +1370,7 @@ impl Clean<Item> for doctree::Struct {
             source: self.where.clean(),
             def_id: ast_util::local_def(self.id),
             visibility: self.vis.clean(),
+            stability: self.stab.clean(),
             inner: StructItem(Struct {
                 struct_type: self.struct_type,
                 generics: self.generics.clean(),
@@ -1418,6 +1416,7 @@ impl Clean<Item> for doctree::Enum {
             source: self.where.clean(),
             def_id: ast_util::local_def(self.id),
             visibility: self.vis.clean(),
+            stability: self.stab.clean(),
             inner: EnumItem(Enum {
                 variants: self.variants.clean(),
                 generics: self.generics.clean(),
@@ -1439,6 +1438,7 @@ impl Clean<Item> for doctree::Variant {
             attrs: self.attrs.clean(),
             source: self.where.clean(),
             visibility: self.vis.clean(),
+            stability: self.stab.clean(),
             def_id: ast_util::local_def(self.id),
             inner: VariantItem(Variant {
                 kind: self.kind.clean(),
@@ -1450,11 +1450,7 @@ 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 cx = get_cx();
         let kind = match self.arg_names.as_ref().map(|s| s.as_slice()) {
             None | Some([]) if self.args.len() == 0 => CLikeVariant,
             None | Some([]) => {
@@ -1470,6 +1466,7 @@ impl Clean<Item> for ty::VariantInfo {
                             name: Some(name.clean()),
                             attrs: Vec::new(),
                             visibility: Some(ast::Public),
+                            stability: get_stability(self.id),
                             // 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
@@ -1485,11 +1482,12 @@ impl Clean<Item> for ty::VariantInfo {
         };
         Item {
             name: Some(self.name.clean()),
-            attrs: inline::load_attrs(tcx, self.id),
+            attrs: inline::load_attrs(cx.tcx(), self.id),
             source: Span::empty(),
             visibility: Some(ast::Public),
             def_id: self.id,
             inner: VariantItem(Variant { kind: kind }),
+            stability: None,
         }
     }
 }
@@ -1626,6 +1624,7 @@ impl Clean<Item> for doctree::Typedef {
             source: self.where.clean(),
             def_id: ast_util::local_def(self.id.clone()),
             visibility: self.vis.clean(),
+            stability: self.stab.clean(),
             inner: TypedefItem(Typedef {
                 type_: self.ty.clean(),
                 generics: self.gen.clean(),
@@ -1675,6 +1674,7 @@ impl Clean<Item> for doctree::Static {
             source: self.where.clean(),
             def_id: ast_util::local_def(self.id),
             visibility: self.vis.clean(),
+            stability: self.stab.clean(),
             inner: StaticItem(Static {
                 type_: self.type_.clean(),
                 mutability: self.mutability.clean(),
@@ -1720,6 +1720,7 @@ impl Clean<Item> for doctree::Impl {
             source: self.where.clean(),
             def_id: ast_util::local_def(self.id),
             visibility: self.vis.clean(),
+            stability: self.stab.clean(),
             inner: ImplItem(Impl {
                 generics: self.generics.clean(),
                 trait_: self.trait_.clean(),
@@ -1754,6 +1755,7 @@ impl Clean<Vec<Item>> for ast::ViewItem {
                 source: self.span.clean(),
                 def_id: ast_util::local_def(0),
                 visibility: self.vis.clean(),
+                stability: None,
                 inner: ViewItemItem(ViewItem { inner: node.clean() }),
             }
         };
@@ -1895,6 +1897,7 @@ impl Clean<Item> for ast::ForeignItem {
             source: self.span.clean(),
             def_id: ast_util::local_def(self.id),
             visibility: self.vis.clean(),
+            stability: None,
             inner: inner,
         }
     }
@@ -1977,7 +1980,7 @@ fn name_from_pat(p: &ast::Pat) -> String {
 /// Given a Type, resolve it using the def_map
 fn resolve_type(path: Path, tpbs: Option<Vec<TyParamBound>>,
                 id: ast::NodeId) -> Type {
-    let cx = super::ctxtkey.get().unwrap();
+    let cx = get_cx();
     let tycx = match cx.maybe_typed {
         core::Typed(ref tycx) => tycx,
         // If we're extracting tests, this return value doesn't matter.
@@ -2012,7 +2015,7 @@ fn resolve_type(path: Path, tpbs: Option<Vec<TyParamBound>>,
         def::DefTyParamBinder(i) => return TyParamBinder(i),
         _ => {}
     };
-    let did = register_def(&**cx, def);
+    let did = register_def(&*cx, def);
     ResolvedPath { path: path, typarams: tpbs, did: did }
 }
 
@@ -2051,13 +2054,9 @@ fn resolve_use_source(path: Path, id: ast::NodeId) -> ImportSource {
 }
 
 fn resolve_def(id: ast::NodeId) -> Option<ast::DefId> {
-    let cx = super::ctxtkey.get().unwrap();
-    match cx.maybe_typed {
-        core::Typed(ref tcx) => {
-            tcx.def_map.borrow().find(&id).map(|&def| register_def(&**cx, def))
-        }
-        core::NotTyped(_) => None
-    }
+    get_cx().tcx_opt().and_then(|tcx| {
+        tcx.def_map.borrow().find(&id).map(|&def| register_def(&*get_cx(), def))
+    })
 }
 
 #[deriving(Clone, Encodable, Decodable)]
@@ -2072,6 +2071,7 @@ impl Clean<Item> for doctree::Macro {
             attrs: self.attrs.clean(),
             source: self.where.clean(),
             visibility: ast::Public.clean(),
+            stability: self.stab.clean(),
             def_id: ast_util::local_def(self.id),
             inner: MacroItem(Macro {
                 source: self.where.to_src(),
@@ -2079,3 +2079,19 @@ impl Clean<Item> for doctree::Macro {
         }
     }
 }
+
+#[deriving(Clone, Encodable, Decodable)]
+pub struct Stability {
+    pub level: attr::StabilityLevel,
+    pub text: String
+}
+
+impl Clean<Stability> for attr::Stability {
+    fn clean(&self) -> Stability {
+        Stability {
+            level: self.level,
+            text: self.text.as_ref().map_or("".to_string(),
+                                            |interned| interned.get().to_string()),
+        }
+    }
+}
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index ba0161da7e6..245b2d162a7 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -10,7 +10,7 @@
 
 use rustc;
 use rustc::{driver, middle};
-use rustc::middle::privacy;
+use rustc::middle::{privacy, ty};
 use rustc::lint;
 
 use syntax::ast;
@@ -26,6 +26,7 @@ use visit_ast::RustdocVisitor;
 use clean;
 use clean::Clean;
 
+/// Are we generating documentation (`Typed`) or tests (`NotTyped`)?
 pub enum MaybeTyped {
     Typed(middle::ty::ctxt),
     NotTyped(driver::session::Session)
@@ -52,6 +53,18 @@ impl DocContext {
             NotTyped(ref sess) => sess
         }
     }
+
+    pub fn tcx_opt<'a>(&'a self) -> Option<&'a ty::ctxt> {
+        match self.maybe_typed {
+            Typed(ref tcx) => Some(tcx),
+            NotTyped(_) => None
+        }
+    }
+
+    pub fn tcx<'a>(&'a self) -> &'a ty::ctxt {
+        let tcx_opt = self.tcx_opt();
+        tcx_opt.expect("tcx not present")
+    }
 }
 
 pub struct CrateAnalysis {
diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs
index b8a2a6195b7..313f1c81c79 100644
--- a/src/librustdoc/doctree.rs
+++ b/src/librustdoc/doctree.rs
@@ -14,6 +14,7 @@
 use syntax;
 use syntax::codemap::Span;
 use syntax::ast;
+use syntax::attr;
 use syntax::ast::{Ident, NodeId};
 
 use std::gc::Gc;
@@ -32,6 +33,7 @@ pub struct Module {
     pub statics: Vec<Static>,
     pub traits: Vec<Trait>,
     pub vis: ast::Visibility,
+    pub stab: Option<attr::Stability>,
     pub impls: Vec<Impl>,
     pub foreigns: Vec<ast::ForeignMod>,
     pub view_items: Vec<ast::ViewItem>,
@@ -45,6 +47,7 @@ impl Module {
             name       : name,
             id: 0,
             vis: ast::Inherited,
+            stab: None,
             where_outer: syntax::codemap::DUMMY_SP,
             where_inner: syntax::codemap::DUMMY_SP,
             attrs      : Vec::new(),
@@ -83,6 +86,7 @@ pub enum TypeBound {
 
 pub struct Struct {
     pub vis: ast::Visibility,
+    pub stab: Option<attr::Stability>,
     pub id: NodeId,
     pub struct_type: StructType,
     pub name: Ident,
@@ -94,6 +98,7 @@ pub struct Struct {
 
 pub struct Enum {
     pub vis: ast::Visibility,
+    pub stab: Option<attr::Stability>,
     pub variants: Vec<Variant>,
     pub generics: ast::Generics,
     pub attrs: Vec<ast::Attribute>,
@@ -108,6 +113,7 @@ pub struct Variant {
     pub kind: ast::VariantKind,
     pub id: ast::NodeId,
     pub vis: ast::Visibility,
+    pub stab: Option<attr::Stability>,
     pub where: Span,
 }
 
@@ -117,6 +123,7 @@ pub struct Function {
     pub id: NodeId,
     pub name: Ident,
     pub vis: ast::Visibility,
+    pub stab: Option<attr::Stability>,
     pub fn_style: ast::FnStyle,
     pub where: Span,
     pub generics: ast::Generics,
@@ -130,6 +137,7 @@ pub struct Typedef {
     pub attrs: Vec<ast::Attribute>,
     pub where: Span,
     pub vis: ast::Visibility,
+    pub stab: Option<attr::Stability>,
 }
 
 pub struct Static {
@@ -139,6 +147,7 @@ pub struct Static {
     pub name: Ident,
     pub attrs: Vec<ast::Attribute>,
     pub vis: ast::Visibility,
+    pub stab: Option<attr::Stability>,
     pub id: ast::NodeId,
     pub where: Span,
 }
@@ -152,6 +161,7 @@ pub struct Trait {
     pub id: ast::NodeId,
     pub where: Span,
     pub vis: ast::Visibility,
+    pub stab: Option<attr::Stability>,
 }
 
 pub struct Impl {
@@ -162,6 +172,7 @@ pub struct Impl {
     pub attrs: Vec<ast::Attribute>,
     pub where: Span,
     pub vis: ast::Visibility,
+    pub stab: Option<attr::Stability>,
     pub id: ast::NodeId,
 }
 
@@ -170,6 +181,7 @@ pub struct Macro {
     pub id: ast::NodeId,
     pub attrs: Vec<ast::Attribute>,
     pub where: Span,
+    pub stab: Option<attr::Stability>,
 }
 
 pub fn struct_type_from_def(sd: &ast::StructDef) -> StructType {
diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs
index 77b12aec97b..60853f450ab 100644
--- a/src/librustdoc/fold.rs
+++ b/src/librustdoc/fold.rs
@@ -19,7 +19,7 @@ pub trait DocFolder {
 
     /// don't override!
     fn fold_item_recur(&mut self, item: Item) -> Option<Item> {
-        let Item { attrs, name, source, visibility, def_id, inner } = item;
+        let Item { attrs, name, source, visibility, def_id, inner, stability } = item;
         let inner = inner;
         let inner = match inner {
             StructItem(mut i) => {
@@ -83,7 +83,7 @@ pub trait DocFolder {
         };
 
         Some(Item { attrs: attrs, name: name, source: source, inner: inner,
-                    visibility: visibility, def_id: def_id })
+                    visibility: visibility, stability: stability, def_id: def_id })
     }
 
     fn fold_mod(&mut self, m: Module) -> Module {
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index fa18b6291be..9677b9004cd 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -37,6 +37,10 @@ pub struct FnStyleSpace(pub ast::FnStyle);
 pub struct Method<'a>(pub &'a clean::SelfTy, pub &'a clean::FnDecl);
 /// Similar to VisSpace, but used for mutability
 pub struct MutableSpace(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>);
 
 impl VisSpace {
     pub fn get(&self) -> Option<ast::Visibility> {
@@ -596,3 +600,34 @@ impl fmt::Show for MutableSpace {
         }
     }
 }
+
+impl<'a> fmt::Show for Stability<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let Stability(stab) = *self;
+        match *stab {
+            Some(ref stability) => {
+                write!(f, "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
+                       lvl = stability.level.to_str(),
+                       reason = stability.text)
+            }
+            None => Ok(())
+        }
+    }
+}
+
+impl<'a> fmt::Show for ConciseStability<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let ConciseStability(stab) = *self;
+        match *stab {
+            Some(ref stability) => {
+                write!(f, "<a class='stability {lvl}' title='{lvl}{colon}{reason}'></a>",
+                       lvl = stability.level.to_str(),
+                       colon = if stability.text.len() > 0 { ": " } else { "" },
+                       reason = stability.text)
+            }
+            None => {
+                write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
+            }
+        }
+    }
+}
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index f5d379c4baf..917eab4eeb9 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -46,14 +46,13 @@ use externalfiles::ExternalHtml;
 use serialize::json::ToJson;
 use syntax::ast;
 use syntax::ast_util;
-use syntax::attr;
-use syntax::parse::token::InternedString;
 use rustc::util::nodemap::NodeSet;
 
 use clean;
 use doctree;
 use fold::DocFolder;
-use html::format::{VisSpace, Method, FnStyleSpace, MutableSpace};
+use html::format::{VisSpace, Method, FnStyleSpace, MutableSpace, Stability};
+use html::format::{ConciseStability};
 use html::highlight;
 use html::item_type::{ItemType, shortty};
 use html::item_type;
@@ -114,6 +113,15 @@ pub struct Implementor {
     generics: clean::Generics,
     trait_: clean::Type,
     for_: clean::Type,
+    stability: Option<clean::Stability>,
+}
+
+/// Metadata about implementations for a type.
+#[deriving(Clone)]
+pub struct Impl {
+    impl_: clean::Impl,
+    dox: Option<String>,
+    stability: Option<clean::Stability>,
 }
 
 /// This cache is used to store information about the `clean::Crate` being
@@ -137,7 +145,7 @@ pub struct Cache {
     ///
     /// The values of the map are a list of implementations and documentation
     /// found on that implementation.
-    pub impls: HashMap<ast::DefId, Vec<(clean::Impl, Option<String>)>>,
+    pub impls: HashMap<ast::DefId, Vec<Impl>>,
 
     /// 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
@@ -550,7 +558,8 @@ 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 {}","#,
+            try!(write!(&mut f, r#""{}impl{} {} for {}","#,
+                        ConciseStability(&imp.stability),
                         imp.generics, imp.trait_, imp.for_));
         }
         try!(writeln!(&mut f, r"];"));
@@ -782,6 +791,7 @@ impl DocFolder for Cache {
                             generics: i.generics.clone(),
                             trait_: i.trait_.get_ref().clone(),
                             for_: i.for_.clone(),
+                            stability: item.stability.clone(),
                         });
                     }
                     Some(..) | None => {}
@@ -967,7 +977,11 @@ impl DocFolder for Cache {
                                 let v = self.impls.find_or_insert_with(did, |_| {
                                     Vec::new()
                                 });
-                                v.push((i, dox));
+                                v.push(Impl {
+                                    impl_: i,
+                                    dox: dox,
+                                    stability: item.stability.clone(),
+                                });
                             }
                             None => {}
                         }
@@ -1248,19 +1262,8 @@ impl<'a> fmt::Show for Item<'a> {
         try!(write!(fmt, "<a class='{}' href=''>{}</a>",
                     shortty(self.item), self.item.name.get_ref().as_slice()));
 
-        // Write stability attributes
-        match attr::find_stability_generic(self.item.attrs.iter()) {
-            Some((ref stability, _)) => {
-                try!(write!(fmt,
-                       "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
-                       lvl = stability.level.to_str(),
-                       reason = match stability.text {
-                           Some(ref s) => (*s).clone(),
-                           None => InternedString::new(""),
-                       }));
-            }
-            None => {}
-        }
+        // Write stability level
+        try!(write!(fmt, "{}", Stability(&self.item.stability)));
 
         // Write `src` tag
         //
@@ -1454,10 +1457,11 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
 
                 try!(write!(w, "
                     <tr>
-                        <td><code>{}static {}{}: {}</code>{}</td>
+                        <td>{}<code>{}static {}{}: {}</code>{}</td>
                         <td class='docblock'>{}&nbsp;</td>
                     </tr>
                 ",
+                ConciseStability(&myitem.stability),
                 VisSpace(myitem.visibility),
                 MutableSpace(s.mutability),
                 *myitem.name.get_ref(),
@@ -1492,7 +1496,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
                 if myitem.name.is_none() { continue }
                 try!(write!(w, "
                     <tr>
-                        <td><a class='{class}' href='{href}'
+                        <td>{stab}<a class='{class}' href='{href}'
                                title='{title}'>{}</a></td>
                         <td class='docblock short'>{}</td>
                     </tr>
@@ -1501,7 +1505,8 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
                 Markdown(shorter(myitem.doc_value())),
                 class = shortty(myitem),
                 href = item_path(myitem),
-                title = full_path(cx, myitem)));
+                title = full_path(cx, myitem),
+                stab = ConciseStability(&myitem.stability)));
             }
         }
     }
@@ -1565,9 +1570,10 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
     try!(document(w, it));
 
     fn meth(w: &mut fmt::Formatter, m: &clean::TraitMethod) -> fmt::Result {
-        try!(write!(w, "<h3 id='{}.{}' class='method'><code>",
-                      shortty(m.item()),
-                      *m.item().name.get_ref()));
+        try!(write!(w, "<h3 id='{}.{}' class='method'>{}<code>",
+                    shortty(m.item()),
+                    *m.item().name.get_ref(),
+                    ConciseStability(&m.item().stability)));
         try!(render_method(w, m.item()));
         try!(write!(w, "</code></h3>"));
         try!(document(w, m.item()));
@@ -1604,7 +1610,8 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
     match cache.implementors.find(&it.def_id) {
         Some(implementors) => {
             for i in implementors.iter() {
-                try!(writeln!(w, "<li><code>impl{} {} for {}</code></li>",
+                try!(writeln!(w, "<li>{}<code>impl{} {} for {}</code></li>",
+                              ConciseStability(&i.stability),
                               i.generics, i.trait_, i.for_));
             }
         }
@@ -1677,7 +1684,8 @@ fn item_struct(w: &mut fmt::Formatter, it: &clean::Item,
             try!(write!(w, "<h2 class='fields'>Fields</h2>\n<table>"));
             for field in fields {
                 try!(write!(w, "<tr><td id='structfield.{name}'>\
-                                  <code>{name}</code></td><td>",
+                                  {stab}<code>{name}</code></td><td>",
+                              stab = ConciseStability(&field.stability),
                               name = field.name.get_ref().as_slice()));
                 try!(document(w, field));
                 try!(write!(w, "</td></tr>"));
@@ -1743,7 +1751,8 @@ fn item_enum(w: &mut fmt::Formatter, it: &clean::Item,
     if e.variants.len() > 0 {
         try!(write!(w, "<h2 class='variants'>Variants</h2>\n<table>"));
         for variant in e.variants.iter() {
-            try!(write!(w, "<tr><td id='variant.{name}'><code>{name}</code></td><td>",
+            try!(write!(w, "<tr><td id='variant.{name}'>{stab}<code>{name}</code></td><td>",
+                          stab = ConciseStability(&variant.stability),
                           name = variant.name.get_ref().as_slice()));
             try!(document(w, variant));
             match variant.inner {
@@ -1853,39 +1862,25 @@ 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) {
         Some(v) => {
-            let mut non_trait = v.iter().filter(|p| {
-                p.ref0().trait_.is_none()
-            });
-            let non_trait = non_trait.collect::<Vec<&(clean::Impl, Option<String>)>>();
-            let mut traits = v.iter().filter(|p| {
-                p.ref0().trait_.is_some()
-            });
-            let traits = traits.collect::<Vec<&(clean::Impl, Option<String>)>>();
-
+            let (non_trait, traits) = v.partitioned(|i| i.impl_.trait_.is_none());
             if non_trait.len() > 0 {
                 try!(write!(w, "<h2 id='methods'>Methods</h2>"));
-                for &(ref i, ref dox) in non_trait.move_iter() {
-                    try!(render_impl(w, i, dox));
+                for i in non_trait.iter() {
+                    try!(render_impl(w, i));
                 }
             }
             if traits.len() > 0 {
                 try!(write!(w, "<h2 id='implementations'>Trait \
                                   Implementations</h2>"));
-                let mut any_derived = false;
-                for & &(ref i, ref dox) in traits.iter() {
-                    if !i.derived {
-                        try!(render_impl(w, i, dox));
-                    } else {
-                        any_derived = true;
-                    }
+                let (derived, manual) = traits.partition(|i| i.impl_.derived);
+                for i in manual.iter() {
+                    try!(render_impl(w, i));
                 }
-                if any_derived {
+                if derived.len() > 0 {
                     try!(write!(w, "<h3 id='derived_implementations'>Derived Implementations \
                                 </h3>"));
-                    for &(ref i, ref dox) in traits.move_iter() {
-                        if i.derived {
-                            try!(render_impl(w, i, dox));
-                        }
+                    for i in derived.iter() {
+                        try!(render_impl(w, i));
                     }
                 }
             }
@@ -1895,15 +1890,16 @@ fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
     Ok(())
 }
 
-fn render_impl(w: &mut fmt::Formatter, i: &clean::Impl,
-               dox: &Option<String>) -> fmt::Result {
-    try!(write!(w, "<h3 class='impl'><code>impl{} ", i.generics));
-    match i.trait_ {
+fn render_impl(w: &mut fmt::Formatter, i: &Impl) -> fmt::Result {
+    try!(write!(w, "<h3 class='impl'>{}<code>impl{} ",
+                ConciseStability(&i.stability),
+                i.impl_.generics));
+    match i.impl_.trait_ {
         Some(ref ty) => try!(write!(w, "{} for ", *ty)),
         None => {}
     }
-    try!(write!(w, "{}</code></h3>", i.for_));
-    match *dox {
+    try!(write!(w, "{}</code></h3>", i.impl_.for_));
+    match i.dox {
         Some(ref dox) => {
             try!(write!(w, "<div class='docblock'>{}</div>",
                           Markdown(dox.as_slice())));
@@ -1913,8 +1909,9 @@ fn render_impl(w: &mut fmt::Formatter, i: &clean::Impl,
 
     fn docmeth(w: &mut fmt::Formatter, item: &clean::Item,
                dox: bool) -> fmt::Result {
-        try!(write!(w, "<h4 id='method.{}' class='method'><code>",
-                      *item.name.get_ref()));
+        try!(write!(w, "<h4 id='method.{}' class='method'>{}<code>",
+                    *item.name.get_ref(),
+                    ConciseStability(&item.stability)));
         try!(render_method(w, item));
         try!(write!(w, "</code></h4>\n"));
         match item.doc_value() {
@@ -1926,8 +1923,8 @@ fn render_impl(w: &mut fmt::Formatter, i: &clean::Impl,
         }
     }
 
-    try!(write!(w, "<div class='methods'>"));
-    for meth in i.methods.iter() {
+    try!(write!(w, "<div class='impl-methods'>"));
+    for meth in i.impl_.methods.iter() {
         try!(docmeth(w, meth, true));
     }
 
@@ -1948,11 +1945,11 @@ fn render_impl(w: &mut fmt::Formatter, i: &clean::Impl,
 
     // If we've implemented a trait, then also emit documentation for all
     // default methods which weren't overridden in the implementation block.
-    match i.trait_ {
+    match i.impl_.trait_ {
         Some(clean::ResolvedPath { did, .. }) => {
             try!({
                 match cache_key.get().unwrap().traits.find(&did) {
-                    Some(t) => try!(render_default_methods(w, t, i)),
+                    Some(t) => try!(render_default_methods(w, t, &i.impl_)),
                     None => {}
                 }
                 Ok(())
diff --git a/src/librustdoc/html/static/main.css b/src/librustdoc/html/static/main.css
index a88992f6c4c..97048229ac4 100644
--- a/src/librustdoc/html/static/main.css
+++ b/src/librustdoc/html/static/main.css
@@ -258,8 +258,9 @@ nav.sub {
 .content .multi-column li { width: 100%; display: inline-block; }
 
 .content .method { font-size: 1em; }
-.content .methods { margin-left: 20px; }
-.content .methods .docblock { margin-left: 20px; }
+.content .methods .docblock { margin-left: 40px; }
+
+.content .impl-methods .docblock { margin-left: 40px; }
 
 nav {
     border-bottom: 1px solid #e0e0e0;
@@ -372,20 +373,29 @@ p a:hover { text-decoration: underline; }
 }
 
 .stability {
-    border-left: 6px solid #000;
+    border-left: 6px solid;
+    padding: 3px 6px;
     border-radius: 3px;
-    font-weight: 400;
-    padding: 4px 10px;
+}
+
+h1 .stability {
     text-transform: lowercase;
+    font-weight: 400;
     margin-left: 14px;
+    padding: 4px 10px;
+}
+
+.impl-methods .stability {
+    margin-right: 20px;
 }
 
-.stability.Deprecated { border-color: #D60027; color: #880017; }
-.stability.Experimental { border-color: #EC5315; color: #a53c0e; }
-.stability.Unstable { border-color: #FFD700; color: #b39800; }
-.stability.Stable { border-color: #AEC516; color: #7c8b10; }
+.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: #FFFFFF; }
 
 :target { background: #FDFFD3; }
 
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 00fe0134f00..b7ef0956a7c 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -15,9 +15,12 @@ use syntax::abi;
 use syntax::ast;
 use syntax::ast_util;
 use syntax::ast_map;
+use syntax::attr;
 use syntax::attr::AttrMetaMethods;
 use syntax::codemap::Span;
 
+use rustc::middle::stability;
+
 use std::gc::{Gc, GC};
 
 use core;
@@ -41,6 +44,14 @@ impl<'a> RustdocVisitor<'a> {
         }
     }
 
+    fn stability(&self, id: ast::NodeId) -> Option<attr::Stability> {
+        let tcx = match self.cx.maybe_typed {
+            core::Typed(ref tcx) => tcx,
+            core::NotTyped(_) => return None
+        };
+        stability::lookup(tcx, ast_util::local_def(id))
+    }
+
     pub fn visit(&mut self, krate: &ast::Crate) {
         self.attrs = krate.attrs.iter().map(|x| (*x).clone()).collect();
 
@@ -65,6 +76,7 @@ impl<'a> RustdocVisitor<'a> {
             struct_type: struct_type,
             name: item.ident,
             vis: item.vis,
+            stab: self.stability(item.id),
             attrs: item.attrs.iter().map(|x| *x).collect(),
             generics: generics.clone(),
             fields: sd.fields.iter().map(|x| (*x).clone()).collect(),
@@ -81,6 +93,7 @@ impl<'a> RustdocVisitor<'a> {
                 name: x.node.name,
                 attrs: x.node.attrs.iter().map(|x| *x).collect(),
                 vis: x.node.vis,
+                stab: self.stability(x.node.id),
                 id: x.node.id,
                 kind: x.node.kind.clone(),
                 where: x.span,
@@ -90,6 +103,7 @@ impl<'a> RustdocVisitor<'a> {
             name: it.ident,
             variants: vars,
             vis: it.vis,
+            stab: self.stability(it.id),
             generics: params.clone(),
             attrs: it.attrs.iter().map(|x| *x).collect(),
             id: it.id,
@@ -104,6 +118,7 @@ impl<'a> RustdocVisitor<'a> {
         Function {
             id: item.id,
             vis: item.vis,
+            stab: self.stability(item.id),
             attrs: item.attrs.iter().map(|x| *x).collect(),
             decl: fd.clone(),
             name: item.ident,
@@ -125,6 +140,7 @@ impl<'a> RustdocVisitor<'a> {
         om.where_inner = m.inner;
         om.attrs = attrs;
         om.vis = vis;
+        om.stab = self.stability(id);
         om.id = id;
         for i in m.items.iter() {
             self.visit_item(&**i, &mut om);
@@ -258,6 +274,7 @@ impl<'a> RustdocVisitor<'a> {
                     attrs: item.attrs.iter().map(|x| *x).collect(),
                     where: item.span,
                     vis: item.vis,
+                    stab: self.stability(item.id),
                 };
                 om.typedefs.push(t);
             },
@@ -271,6 +288,7 @@ impl<'a> RustdocVisitor<'a> {
                     attrs: item.attrs.iter().map(|x| *x).collect(),
                     where: item.span,
                     vis: item.vis,
+                    stab: self.stability(item.id),
                 };
                 om.statics.push(s);
             },
@@ -284,6 +302,7 @@ impl<'a> RustdocVisitor<'a> {
                     attrs: item.attrs.iter().map(|x| *x).collect(),
                     where: item.span,
                     vis: item.vis,
+                    stab: self.stability(item.id),
                 };
                 om.traits.push(t);
             },
@@ -297,6 +316,7 @@ impl<'a> RustdocVisitor<'a> {
                     id: item.id,
                     where: item.span,
                     vis: item.vis,
+                    stab: self.stability(item.id),
                 };
                 om.impls.push(i);
             },
@@ -309,6 +329,7 @@ impl<'a> RustdocVisitor<'a> {
                     attrs: item.attrs.iter().map(|x| *x).collect(),
                     name: item.ident,
                     where: item.span,
+                    stab: self.stability(item.id),
                 })
             }
         }