From 256df5e3df5fecbd8e9a8ca1b7a370b0ce22efee Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Thu, 26 Jun 2014 11:37:39 -0700 Subject: [PATCH] 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. --- src/librustc/lint/builtin.rs | 11 +- src/librustc/metadata/encoder.rs | 18 ++-- src/librustc/middle/stability.rs | 32 +++--- src/librustdoc/clean/inline.rs | 3 + src/librustdoc/clean/mod.rs | 152 +++++++++++++++------------- src/librustdoc/core.rs | 15 ++- src/librustdoc/doctree.rs | 12 +++ src/librustdoc/fold.rs | 4 +- src/librustdoc/html/format.rs | 35 +++++++ src/librustdoc/html/render.rs | 123 +++++++++++----------- src/librustdoc/html/static/main.css | 28 +++-- src/librustdoc/visit_ast.rs | 21 ++++ 12 files changed, 282 insertions(+), 172 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index b562d48f49d32..30296cb318617 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 c5789e4442a9d..96284f8de261a 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 fc76648ec5524..ac17bd0750352 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 { - 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 { + // 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 { - self.local.find_copy(&id) - } } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index d243c61ddaff8..2d498e7f302d3 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 d7bbb439dbb77..87151708812e5 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 { + *super::ctxtkey.get().unwrap() +} + +// extract the stability index for a node from TLD, if possible +fn get_stability(def_id: ast::DefId) -> Option { + get_cx().tcx_opt().and_then(|tcx| stability::lookup(tcx, def_id)) + .map(|stab| stab.clean()) +} + pub trait Clean { fn clean(&self) -> T; } @@ -97,7 +109,7 @@ pub struct Crate { impl<'a> Clean 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 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 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, pub def_id: ast::DefId, + pub stability: Option, } impl Item { @@ -380,6 +387,7 @@ impl Clean 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 for ast::TyParam { impl Clean 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 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 for ty::BuiltinBound { impl Clean 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 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 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 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 for ast::FnDecl { impl<'a> Clean 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 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 for ast::TraitMethod { impl Clean 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 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 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 = fqn.move_iter().map(|i| { i.to_str() }).collect(); @@ -1277,8 +1276,8 @@ impl Clean 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 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 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 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 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 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 for doctree::Variant { impl Clean 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 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 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 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 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 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> 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 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>, 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>, 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 { - 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 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 for doctree::Macro { } } } + +#[deriving(Clone, Encodable, Decodable)] +pub struct Stability { + pub level: attr::StabilityLevel, + pub text: String +} + +impl Clean 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 ba0161da7e664..245b2d162a77b 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 b8a2a6195b7a5..313f1c81c79e8 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, pub traits: Vec, pub vis: ast::Visibility, + pub stab: Option, pub impls: Vec, pub foreigns: Vec, pub view_items: Vec, @@ -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, 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, pub variants: Vec, pub generics: ast::Generics, pub attrs: Vec, @@ -108,6 +113,7 @@ pub struct Variant { pub kind: ast::VariantKind, pub id: ast::NodeId, pub vis: ast::Visibility, + pub stab: Option, pub where: Span, } @@ -117,6 +123,7 @@ pub struct Function { pub id: NodeId, pub name: Ident, pub vis: ast::Visibility, + pub stab: Option, pub fn_style: ast::FnStyle, pub where: Span, pub generics: ast::Generics, @@ -130,6 +137,7 @@ pub struct Typedef { pub attrs: Vec, pub where: Span, pub vis: ast::Visibility, + pub stab: Option, } pub struct Static { @@ -139,6 +147,7 @@ pub struct Static { pub name: Ident, pub attrs: Vec, pub vis: ast::Visibility, + pub stab: Option, 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, } pub struct Impl { @@ -162,6 +172,7 @@ pub struct Impl { pub attrs: Vec, pub where: Span, pub vis: ast::Visibility, + pub stab: Option, pub id: ast::NodeId, } @@ -170,6 +181,7 @@ pub struct Macro { pub id: ast::NodeId, pub attrs: Vec, pub where: Span, + pub stab: Option, } pub fn struct_type_from_def(sd: &ast::StructDef) -> StructType { diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index 77b12aec97b41..60853f450ab32 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 { - 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 fa18b6291be74..9677b9004cdf1 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); +/// Wrapper struct for emitting the stability level concisely. +pub struct ConciseStability<'a>(pub &'a Option); impl VisSpace { pub fn get(&self) -> Option { @@ -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, "{lvl}", + 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, "", + lvl = stability.level.to_str(), + colon = if stability.text.len() > 0 { ": " } else { "" }, + reason = stability.text) + } + None => { + write!(f, "") + } + } + } +} diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index f5d379c4bafa1..917eab4eeb991 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, +} + +/// Metadata about implementations for a type. +#[deriving(Clone)] +pub struct Impl { + impl_: clean::Impl, + dox: Option, + stability: Option, } /// 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)>>, + pub impls: HashMap>, /// 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, "{}", 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, - "{lvl}", - 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, " - {}static {}{}: {}{} + {}{}static {}{}: {}{} {}  ", + 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, " - {stab}{} {} @@ -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, "

", - shortty(m.item()), - *m.item().name.get_ref())); + try!(write!(w, "

{}", + shortty(m.item()), + *m.item().name.get_ref(), + ConciseStability(&m.item().stability))); try!(render_method(w, m.item())); try!(write!(w, "

")); 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, "
  • impl{} {} for {}
  • ", + try!(writeln!(w, "
  • {}impl{} {} for {}
  • ", + 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, "

    Fields

    \n")); for field in fields { try!(write!(w, "")); @@ -1743,7 +1751,8 @@ fn item_enum(w: &mut fmt::Formatter, it: &clean::Item, if e.variants.len() > 0 { try!(write!(w, "

    Variants

    \n
    \ - {name}", + {stab}{name}", + stab = ConciseStability(&field.stability), name = field.name.get_ref().as_slice())); try!(document(w, field)); try!(write!(w, "
    ")); for variant in e.variants.iter() { - try!(write!(w, "
    {name}", + try!(write!(w, "
    {stab}{name}", + 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::)>>(); - let mut traits = v.iter().filter(|p| { - p.ref0().trait_.is_some() - }); - let traits = traits.collect::)>>(); - + let (non_trait, traits) = v.partitioned(|i| i.impl_.trait_.is_none()); if non_trait.len() > 0 { try!(write!(w, "

    Methods

    ")); - 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, "

    Trait \ Implementations

    ")); - 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, "

    Derived Implementations \

    ")); - 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) -> fmt::Result { - try!(write!(w, "

    impl{} ", i.generics)); - match i.trait_ { +fn render_impl(w: &mut fmt::Formatter, i: &Impl) -> fmt::Result { + try!(write!(w, "

    {}impl{} ", + ConciseStability(&i.stability), + i.impl_.generics)); + match i.impl_.trait_ { Some(ref ty) => try!(write!(w, "{} for ", *ty)), None => {} } - try!(write!(w, "{}

    ", i.for_)); - match *dox { + try!(write!(w, "{}

    ", i.impl_.for_)); + match i.dox { Some(ref dox) => { try!(write!(w, "
    {}
    ", 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, "

    ", - *item.name.get_ref())); + try!(write!(w, "

    {}", + *item.name.get_ref(), + ConciseStability(&item.stability))); try!(render_method(w, item)); try!(write!(w, "

    \n")); match item.doc_value() { @@ -1926,8 +1923,8 @@ fn render_impl(w: &mut fmt::Formatter, i: &clean::Impl, } } - try!(write!(w, "
    ")); - for meth in i.methods.iter() { + try!(write!(w, "
    ")); + 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 a88992f6c4c50..97048229ac4c1 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 00fe0134f00ee..b7ef0956a7c76 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 { + 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), }) } }