about summary refs log tree commit diff
path: root/src/librustdoc
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 /src/librustdoc
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.
Diffstat (limited to 'src/librustdoc')
-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
9 files changed, 250 insertions, 143 deletions
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),
                 })
             }
         }