diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 185f897c1baa1..c5562ae3b7feb 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -18,7 +18,7 @@ use rustc::hir; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::DefId; use rustc::hir::print as pprust; -use rustc::ty::{self, TyCtxt}; +use rustc::ty; use rustc::util::nodemap::FxHashSet; use rustc_const_eval::lookup_const_by_id; @@ -43,17 +43,13 @@ use super::Clean; /// of a vector of items if it was successfully expanded. pub fn try_inline(cx: &DocContext, id: ast::NodeId, into: Option) -> Option> { - let tcx = match cx.tcx_opt() { - Some(tcx) => tcx, - None => return None, - }; - let def = match tcx.expect_def_or_none(id) { + let def = match cx.tcx.expect_def_or_none(id) { Some(def) => def, None => return None, }; let did = def.def_id(); if did.is_local() { return None } - try_inline_def(cx, tcx, def).map(|vec| { + try_inline_def(cx, def).map(|vec| { vec.into_iter().map(|mut item| { match into { Some(into) if item.name.is_some() => { @@ -66,39 +62,38 @@ pub fn try_inline(cx: &DocContext, id: ast::NodeId, into: Option) }) } -fn try_inline_def<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, - def: Def) -> Option> { +fn try_inline_def(cx: &DocContext, def: Def) -> Option> { + let tcx = cx.tcx; let mut ret = Vec::new(); - let did = def.def_id(); let inner = match def { Def::Trait(did) => { record_extern_fqn(cx, did, clean::TypeKind::Trait); - ret.extend(build_impls(cx, tcx, did)); - clean::TraitItem(build_external_trait(cx, tcx, did)) + ret.extend(build_impls(cx, did)); + clean::TraitItem(build_external_trait(cx, did)) } Def::Fn(did) => { record_extern_fqn(cx, did, clean::TypeKind::Function); - clean::FunctionItem(build_external_function(cx, tcx, did)) + clean::FunctionItem(build_external_function(cx, did)) } Def::Struct(did) => { record_extern_fqn(cx, did, clean::TypeKind::Struct); - ret.extend(build_impls(cx, tcx, did)); - clean::StructItem(build_struct(cx, tcx, did)) + ret.extend(build_impls(cx, did)); + clean::StructItem(build_struct(cx, did)) } Def::Union(did) => { record_extern_fqn(cx, did, clean::TypeKind::Union); - ret.extend(build_impls(cx, tcx, did)); - clean::UnionItem(build_union(cx, tcx, did)) + ret.extend(build_impls(cx, did)); + clean::UnionItem(build_union(cx, did)) } Def::TyAlias(did) => { record_extern_fqn(cx, did, clean::TypeKind::Typedef); - ret.extend(build_impls(cx, tcx, did)); - clean::TypedefItem(build_type_alias(cx, tcx, did), false) + ret.extend(build_impls(cx, did)); + clean::TypedefItem(build_type_alias(cx, did), false) } Def::Enum(did) => { record_extern_fqn(cx, did, clean::TypeKind::Enum); - ret.extend(build_impls(cx, tcx, did)); - clean::EnumItem(build_enum(cx, tcx, did)) + ret.extend(build_impls(cx, did)); + clean::EnumItem(build_enum(cx, did)) } // Assume that the enum type is reexported next to the variant, and // variants don't show up in documentation specially. @@ -108,23 +103,24 @@ fn try_inline_def<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, Def::StructCtor(..) => return Some(Vec::new()), Def::Mod(did) => { record_extern_fqn(cx, did, clean::TypeKind::Module); - clean::ModuleItem(build_module(cx, tcx, did)) + clean::ModuleItem(build_module(cx, did)) } Def::Static(did, mtbl) => { record_extern_fqn(cx, did, clean::TypeKind::Static); - clean::StaticItem(build_static(cx, tcx, did, mtbl)) + clean::StaticItem(build_static(cx, did, mtbl)) } Def::Const(did) => { record_extern_fqn(cx, did, clean::TypeKind::Const); - clean::ConstantItem(build_const(cx, tcx, did)) + clean::ConstantItem(build_const(cx, did)) } _ => return None, }; + let did = def.def_id(); cx.renderinfo.borrow_mut().inlined.insert(did); ret.push(clean::Item { source: clean::Span::empty(), name: Some(tcx.item_name(did).to_string()), - attrs: load_attrs(cx, tcx, did), + attrs: load_attrs(cx, did), inner: inner, visibility: Some(clean::Public), stability: tcx.lookup_stability(did).clean(cx), @@ -134,9 +130,8 @@ fn try_inline_def<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, Some(ret) } -pub fn load_attrs<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, - did: DefId) -> Vec { - tcx.get_attrs(did).iter().map(|a| a.clean(cx)).collect() +pub fn load_attrs(cx: &DocContext, did: DefId) -> clean::Attributes { + cx.tcx.get_attrs(did).clean(cx) } /// Record an external fully qualified name in the external_paths cache. @@ -144,27 +139,24 @@ pub fn load_attrs<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, /// These names are used later on by HTML rendering to generate things like /// source links back to the original item. pub fn record_extern_fqn(cx: &DocContext, did: DefId, kind: clean::TypeKind) { - if let Some(tcx) = cx.tcx_opt() { - let crate_name = tcx.sess.cstore.crate_name(did.krate).to_string(); - let relative = tcx.def_path(did).data.into_iter().filter_map(|elem| { - // extern blocks have an empty name - let s = elem.data.to_string(); - if !s.is_empty() { - Some(s) - } else { - None - } - }); - let fqn = once(crate_name).chain(relative).collect(); - cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind)); - } + let crate_name = cx.tcx.sess.cstore.crate_name(did.krate).to_string(); + let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| { + // extern blocks have an empty name + let s = elem.data.to_string(); + if !s.is_empty() { + Some(s) + } else { + None + } + }); + let fqn = once(crate_name).chain(relative).collect(); + cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind)); } -pub fn build_external_trait<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, - did: DefId) -> clean::Trait { - let def = tcx.lookup_trait_def(did); - let trait_items = tcx.associated_items(did).map(|item| item.clean(cx)).collect(); - let predicates = tcx.item_predicates(did); +pub fn build_external_trait(cx: &DocContext, did: DefId) -> clean::Trait { + let def = cx.tcx.lookup_trait_def(did); + let trait_items = cx.tcx.associated_items(did).map(|item| item.clean(cx)).collect(); + let predicates = cx.tcx.item_predicates(did); let generics = (def.generics, &predicates).clean(cx); let generics = filter_non_trait_generics(did, generics); let (generics, supertrait_bounds) = separate_supertrait_bounds(generics); @@ -176,45 +168,42 @@ pub fn build_external_trait<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tc } } -fn build_external_function<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, - did: DefId) -> clean::Function { - let ty = tcx.item_type(did); +fn build_external_function(cx: &DocContext, did: DefId) -> clean::Function { + let ty = cx.tcx.item_type(did); let (decl, style, abi) = match ty.sty { ty::TyFnDef(.., ref f) => ((did, &f.sig).clean(cx), f.unsafety, f.abi), _ => panic!("bad function"), }; - let constness = if tcx.sess.cstore.is_const_fn(did) { + let constness = if cx.tcx.sess.cstore.is_const_fn(did) { hir::Constness::Const } else { hir::Constness::NotConst }; - let predicates = tcx.item_predicates(did); + let predicates = cx.tcx.item_predicates(did); clean::Function { decl: decl, - generics: (tcx.item_generics(did), &predicates).clean(cx), + generics: (cx.tcx.item_generics(did), &predicates).clean(cx), unsafety: style, constness: constness, abi: abi, } } -fn build_enum<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, - did: DefId) -> clean::Enum { - let predicates = tcx.item_predicates(did); +fn build_enum(cx: &DocContext, did: DefId) -> clean::Enum { + let predicates = cx.tcx.item_predicates(did); clean::Enum { - generics: (tcx.item_generics(did), &predicates).clean(cx), + generics: (cx.tcx.item_generics(did), &predicates).clean(cx), variants_stripped: false, - variants: tcx.lookup_adt_def(did).variants.clean(cx), + variants: cx.tcx.lookup_adt_def(did).variants.clean(cx), } } -fn build_struct<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, - did: DefId) -> clean::Struct { - let predicates = tcx.item_predicates(did); - let variant = tcx.lookup_adt_def(did).struct_variant(); +fn build_struct(cx: &DocContext, did: DefId) -> clean::Struct { + let predicates = cx.tcx.item_predicates(did); + let variant = cx.tcx.lookup_adt_def(did).struct_variant(); clean::Struct { struct_type: match variant.ctor_kind { @@ -222,44 +211,41 @@ fn build_struct<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, CtorKind::Fn => doctree::Tuple, CtorKind::Const => doctree::Unit, }, - generics: (tcx.item_generics(did), &predicates).clean(cx), + generics: (cx.tcx.item_generics(did), &predicates).clean(cx), fields: variant.fields.clean(cx), fields_stripped: false, } } -fn build_union<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, - did: DefId) -> clean::Union { - let predicates = tcx.item_predicates(did); - let variant = tcx.lookup_adt_def(did).struct_variant(); +fn build_union(cx: &DocContext, did: DefId) -> clean::Union { + let predicates = cx.tcx.item_predicates(did); + let variant = cx.tcx.lookup_adt_def(did).struct_variant(); clean::Union { struct_type: doctree::Plain, - generics: (tcx.item_generics(did), &predicates).clean(cx), + generics: (cx.tcx.item_generics(did), &predicates).clean(cx), fields: variant.fields.clean(cx), fields_stripped: false, } } -fn build_type_alias<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, - did: DefId) -> clean::Typedef { - let predicates = tcx.item_predicates(did); +fn build_type_alias(cx: &DocContext, did: DefId) -> clean::Typedef { + let predicates = cx.tcx.item_predicates(did); clean::Typedef { - type_: tcx.item_type(did).clean(cx), - generics: (tcx.item_generics(did), &predicates).clean(cx), + type_: cx.tcx.item_type(did).clean(cx), + generics: (cx.tcx.item_generics(did), &predicates).clean(cx), } } -pub fn build_impls<'a, 'tcx>(cx: &DocContext, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - did: DefId) -> Vec { +pub fn build_impls(cx: &DocContext, did: DefId) -> Vec { + let tcx = cx.tcx; tcx.populate_inherent_implementations_for_type_if_necessary(did); let mut impls = Vec::new(); if let Some(i) = tcx.inherent_impls.borrow().get(&did) { for &did in i.iter() { - build_impl(cx, tcx, did, &mut impls); + build_impl(cx, did, &mut impls); } } // If this is the first time we've inlined something from another crate, then @@ -277,7 +263,7 @@ pub fn build_impls<'a, 'tcx>(cx: &DocContext, cx.populated_all_crate_impls.set(true); for did in tcx.sess.cstore.implementations_of_trait(None) { - build_impl(cx, tcx, did, &mut impls); + build_impl(cx, did, &mut impls); } // Also try to inline primitive impls from other crates. @@ -303,22 +289,20 @@ pub fn build_impls<'a, 'tcx>(cx: &DocContext, for def_id in primitive_impls.iter().filter_map(|&def_id| def_id) { if !def_id.is_local() { - build_impl(cx, tcx, def_id, &mut impls); + build_impl(cx, def_id, &mut impls); } } impls } -pub fn build_impl<'a, 'tcx>(cx: &DocContext, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - did: DefId, - ret: &mut Vec) { +pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec) { if !cx.renderinfo.borrow_mut().inlined.insert(did) { return } - let attrs = load_attrs(cx, tcx, did); + let attrs = load_attrs(cx, did); + let tcx = cx.tcx; let associated_trait = tcx.impl_trait_ref(did); // Only inline impl if the implemented trait is @@ -377,7 +361,7 @@ pub fn build_impl<'a, 'tcx>(cx: &DocContext, default, ), source: clean::Span::empty(), - attrs: vec![], + attrs: clean::Attributes::default(), visibility: None, stability: tcx.lookup_stability(item.def_id).clean(cx), deprecation: tcx.lookup_deprecation(item.def_id).clean(cx), @@ -424,7 +408,7 @@ pub fn build_impl<'a, 'tcx>(cx: &DocContext, name: Some(item.name.clean(cx)), inner: clean::TypedefItem(typedef, true), source: clean::Span::empty(), - attrs: vec![], + attrs: clean::Attributes::default(), visibility: None, stability: tcx.lookup_stability(item.def_id).clean(cx), deprecation: tcx.lookup_deprecation(item.def_id).clean(cx), @@ -440,15 +424,15 @@ pub fn build_impl<'a, 'tcx>(cx: &DocContext, clean::RegionBound(..) => unreachable!(), } }); - if trait_.def_id() == cx.deref_trait_did.get() { + if trait_.def_id() == tcx.lang_items.deref_trait() { super::build_deref_target_impls(cx, &trait_items, ret); } let provided = trait_.def_id().map(|did| { - cx.tcx().provided_trait_methods(did) - .into_iter() - .map(|meth| meth.name.to_string()) - .collect() + tcx.provided_trait_methods(did) + .into_iter() + .map(|meth| meth.name.to_string()) + .collect() }).unwrap_or(FxHashSet()); ret.push(clean::Item { @@ -471,26 +455,24 @@ pub fn build_impl<'a, 'tcx>(cx: &DocContext, }); } -fn build_module<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, - did: DefId) -> clean::Module { +fn build_module(cx: &DocContext, did: DefId) -> clean::Module { let mut items = Vec::new(); - fill_in(cx, tcx, did, &mut items); + fill_in(cx, did, &mut items); return clean::Module { items: items, is_crate: false, }; - fn fill_in<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, - did: DefId, items: &mut Vec) { + fn fill_in(cx: &DocContext, did: DefId, items: &mut Vec) { // If we're reexporting a reexport it may actually reexport something in // two namespaces, so the target may be listed twice. Make sure we only // visit each node at most once. let mut visited = FxHashSet(); - for item in tcx.sess.cstore.item_children(did) { + for item in cx.tcx.sess.cstore.item_children(did) { let def_id = item.def.def_id(); - if tcx.sess.cstore.visibility(def_id) == ty::Visibility::Public { + if cx.tcx.sess.cstore.visibility(def_id) == ty::Visibility::Public { if !visited.insert(def_id) { continue } - if let Some(i) = try_inline_def(cx, tcx, item.def) { + if let Some(i) = try_inline_def(cx, item.def) { items.extend(i) } } @@ -498,9 +480,8 @@ fn build_module<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -fn build_const<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, - did: DefId) -> clean::Constant { - let (expr, ty) = lookup_const_by_id(tcx, did, None).unwrap_or_else(|| { +fn build_const(cx: &DocContext, did: DefId) -> clean::Constant { + let (expr, ty) = lookup_const_by_id(cx.tcx, did, None).unwrap_or_else(|| { panic!("expected lookup_const_by_id to succeed for {:?}", did); }); debug!("converting constant expr {:?} to snippet", expr); @@ -508,16 +489,14 @@ fn build_const<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, debug!("got snippet {}", sn); clean::Constant { - type_: ty.map(|t| t.clean(cx)).unwrap_or_else(|| tcx.item_type(did).clean(cx)), + type_: ty.map(|t| t.clean(cx)).unwrap_or_else(|| cx.tcx.item_type(did).clean(cx)), expr: sn } } -fn build_static<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, - did: DefId, - mutable: bool) -> clean::Static { +fn build_static(cx: &DocContext, did: DefId, mutable: bool) -> clean::Static { clean::Static { - type_: tcx.item_type(did).clean(cx), + type_: cx.tcx.item_type(did).clean(cx), mutability: if mutable {clean::Mutable} else {clean::Immutable}, expr: "\n\n\n".to_string(), // trigger the "[definition]" links } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2cc1882ce3eae..a19ec4e8b5edb 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -14,7 +14,6 @@ pub use self::Type::*; pub use self::Mutability::*; pub use self::ItemEnum::*; -pub use self::Attribute::*; pub use self::TyParamBound::*; pub use self::SelfTy::*; pub use self::FunctionRetTy::*; @@ -25,7 +24,6 @@ use syntax::ast; use syntax::attr; use syntax::codemap::Spanned; use syntax::ptr::P; -use syntax::print::pprust as syntax_pprust; use syntax::symbol::keywords; use syntax_pos::{self, DUMMY_SP, Pos}; @@ -44,6 +42,7 @@ use rustc::hir; use std::path::PathBuf; use std::rc::Rc; +use std::slice; use std::sync::Arc; use std::u32; use std::env::current_dir; @@ -59,11 +58,11 @@ mod simplify; // extract the stability index for a node from tcx, if possible fn get_stability(cx: &DocContext, def_id: DefId) -> Option { - cx.tcx_opt().and_then(|tcx| tcx.lookup_stability(def_id)).clean(cx) + cx.tcx.lookup_stability(def_id).clean(cx) } fn get_deprecation(cx: &DocContext, def_id: DefId) -> Option { - cx.tcx_opt().and_then(|tcx| tcx.lookup_deprecation(def_id)).clean(cx) + cx.tcx.lookup_deprecation(def_id).clean(cx) } pub trait Clean { @@ -126,20 +125,17 @@ impl<'a, 'tcx> Clean for visit_ast::RustdocVisitor<'a, 'tcx> { use rustc::session::config::Input; use ::visit_lib::LibEmbargoVisitor; - if let Some(t) = cx.tcx_opt() { - cx.deref_trait_did.set(t.lang_items.deref_trait()); - cx.renderinfo.borrow_mut().deref_trait_did = cx.deref_trait_did.get(); - cx.deref_mut_trait_did.set(t.lang_items.deref_mut_trait()); - cx.renderinfo.borrow_mut().deref_mut_trait_did = cx.deref_mut_trait_did.get(); + { + let mut r = cx.renderinfo.borrow_mut(); + r.deref_trait_did = cx.tcx.lang_items.deref_trait(); + r.deref_mut_trait_did = cx.tcx.lang_items.deref_mut_trait(); } let mut externs = Vec::new(); for cnum in cx.sess().cstore.crates() { externs.push((cnum, CrateNum(cnum).clean(cx))); - if cx.tcx_opt().is_some() { - // Analyze doc-reachability for extern items - LibEmbargoVisitor::new(cx).visit_lib(cnum); - } + // Analyze doc-reachability for extern items + LibEmbargoVisitor::new(cx).visit_lib(cnum); } externs.sort_by(|&(a, _), &(b, _)| a.cmp(&b)); @@ -227,7 +223,7 @@ impl<'a, 'tcx> Clean for visit_ast::RustdocVisitor<'a, 'tcx> { #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct ExternalCrate { pub name: String, - pub attrs: Vec, + pub attrs: Attributes, pub primitives: Vec, } @@ -235,12 +231,10 @@ impl Clean for CrateNum { fn clean(&self, cx: &DocContext) -> ExternalCrate { let mut primitives = Vec::new(); let root = DefId { krate: self.0, index: CRATE_DEF_INDEX }; - cx.tcx_opt().map(|tcx| { - for item in tcx.sess.cstore.item_children(root) { - let attrs = inline::load_attrs(cx, tcx, item.def.def_id()); - PrimitiveType::find(&attrs).map(|prim| primitives.push(prim)); - } - }); + for item in cx.tcx.sess.cstore.item_children(root) { + let attrs = inline::load_attrs(cx, item.def.def_id()); + PrimitiveType::find(&attrs).map(|prim| primitives.push(prim)); + } ExternalCrate { name: cx.sess().cstore.crate_name(self.0).to_string(), attrs: cx.sess().cstore.item_attrs(root).clean(cx), @@ -258,7 +252,7 @@ pub struct Item { pub source: Span, /// Not everything has a name. E.g., impls pub name: Option, - pub attrs: Vec, + pub attrs: Attributes, pub inner: ItemEnum, pub visibility: Option, pub def_id: DefId, @@ -270,7 +264,7 @@ impl Item { /// Finds the `doc` attribute as a NameValue and returns the corresponding /// value found. pub fn doc_value<'a>(&'a self) -> Option<&'a str> { - self.attrs.value("doc") + self.attrs.doc_value() } pub fn is_crate(&self) -> bool { match self.inner { @@ -450,7 +444,7 @@ impl Clean for doctree::Module { visibility: self.vis.clean(cx), stability: self.stab.clean(cx), deprecation: self.depr.clean(cx), - def_id: cx.map.local_def_id(self.id), + def_id: cx.tcx.map.local_def_id(self.id), inner: ModuleItem(Module { is_crate: self.is_crate, items: items @@ -459,86 +453,104 @@ impl Clean for doctree::Module { } } -pub trait Attributes { - fn has_word(&self, &str) -> bool; - fn value<'a>(&'a self, &str) -> Option<&'a str>; - fn list<'a>(&'a self, &str) -> &'a [Attribute]; +pub struct ListAttributesIter<'a> { + attrs: slice::Iter<'a, ast::Attribute>, + current_list: slice::Iter<'a, ast::NestedMetaItem>, + name: &'a str } -impl Attributes for [Attribute] { - /// Returns whether the attribute list contains a specific `Word` - fn has_word(&self, word: &str) -> bool { - for attr in self { - if let Word(ref w) = *attr { - if word == *w { - return true; - } - } +impl<'a> Iterator for ListAttributesIter<'a> { + type Item = &'a ast::NestedMetaItem; + + fn next(&mut self) -> Option { + if let Some(nested) = self.current_list.next() { + return Some(nested); } - false - } - /// Finds an attribute as NameValue and returns the corresponding value found. - fn value<'a>(&'a self, name: &str) -> Option<&'a str> { - for attr in self { - if let NameValue(ref x, ref v) = *attr { - if name == *x { - return Some(v); + for attr in &mut self.attrs { + if let Some(ref list) = attr.meta_item_list() { + if attr.check_name(self.name) { + self.current_list = list.iter(); + if let Some(nested) = self.current_list.next() { + return Some(nested); + } } } } + None } +} +pub trait AttributesExt { /// Finds an attribute as List and returns the list of attributes nested inside. - fn list<'a>(&'a self, name: &str) -> &'a [Attribute] { - for attr in self { - if let List(ref x, ref list) = *attr { - if name == *x { - return &list[..]; - } - } + fn lists<'a>(&'a self, &'a str) -> ListAttributesIter<'a>; +} + +impl AttributesExt for [ast::Attribute] { + fn lists<'a>(&'a self, name: &'a str) -> ListAttributesIter<'a> { + ListAttributesIter { + attrs: self.iter(), + current_list: [].iter(), + name: name } - &[] } } -/// This is a flattened version of the AST's Attribute + MetaItem. -#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug)] -pub enum Attribute { - Word(String), - List(String, Vec), - NameValue(String, String), - Literal(String), +pub trait NestedAttributesExt { + /// Returns whether the attribute list contains a specific `Word` + fn has_word(self, &str) -> bool; +} + +impl<'a, I: IntoIterator> NestedAttributesExt for I { + fn has_word(self, word: &str) -> bool { + self.into_iter().any(|attr| attr.is_word() && attr.check_name(word)) + } +} + +#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug, Default)] +pub struct Attributes { + pub doc_strings: Vec, + pub other_attrs: Vec } -impl Clean for ast::NestedMetaItem { - fn clean(&self, cx: &DocContext) -> Attribute { - if let Some(mi) = self.meta_item() { - mi.clean(cx) - } else { // must be a literal - let lit = self.literal().unwrap(); - Literal(syntax_pprust::lit_to_string(lit)) +impl Attributes { + pub fn from_ast(attrs: &[ast::Attribute]) -> Attributes { + let mut doc_strings = vec![]; + let other_attrs = attrs.iter().filter_map(|attr| { + attr.with_desugared_doc(|attr| { + if let Some(value) = attr.value_str() { + if attr.check_name("doc") { + doc_strings.push(value.to_string()); + return None; + } + } + + Some(attr.clone()) + }) + }).collect(); + Attributes { + doc_strings: doc_strings, + other_attrs: other_attrs } } + + /// Finds the `doc` attribute as a NameValue and returns the corresponding + /// value found. + pub fn doc_value<'a>(&'a self) -> Option<&'a str> { + self.doc_strings.first().map(|s| &s[..]) + } } -impl Clean for ast::MetaItem { - fn clean(&self, cx: &DocContext) -> Attribute { - if self.is_word() { - Word(self.name().to_string()) - } else if let Some(v) = self.value_str() { - NameValue(self.name().to_string(), v.to_string()) - } else { // must be a list - let l = self.meta_item_list().unwrap(); - List(self.name().to_string(), l.clean(cx)) - } +impl AttributesExt for Attributes { + fn lists<'a>(&'a self, name: &'a str) -> ListAttributesIter<'a> { + self.other_attrs.lists(name) } } -impl Clean for ast::Attribute { - fn clean(&self, cx: &DocContext) -> Attribute { - self.with_desugared_doc(|a| a.meta().clean(cx)) +impl Clean for [ast::Attribute] { + fn clean(&self, _cx: &DocContext) -> Attributes { + Attributes::from_ast(self) } } @@ -554,7 +566,7 @@ impl Clean for hir::TyParam { fn clean(&self, cx: &DocContext) -> TyParam { TyParam { name: self.name.clean(cx), - did: cx.map.local_def_id(self.id), + did: cx.tcx.map.local_def_id(self.id), bounds: self.bounds.clean(cx), default: self.default.clean(cx), } @@ -591,11 +603,9 @@ impl TyParamBound { fn is_sized_bound(&self, cx: &DocContext) -> bool { use rustc::hir::TraitBoundModifier as TBM; - if let Some(tcx) = cx.tcx_opt() { - if let TyParamBound::TraitBound(PolyTrait { ref trait_, .. }, TBM::None) = *self { - if trait_.def_id() == tcx.lang_items.sized_trait() { - return true; - } + if let TyParamBound::TraitBound(PolyTrait { ref trait_, .. }, TBM::None) = *self { + if trait_.def_id() == cx.tcx.lang_items.sized_trait() { + return true; } } false @@ -616,9 +626,9 @@ fn external_path_params(cx: &DocContext, trait_did: Option, has_self: boo let lifetimes = substs.regions().filter_map(|v| v.clean(cx)).collect(); let types = substs.types().skip(has_self as usize).collect::>(); - match (trait_did, cx.tcx_opt()) { + match trait_did { // Attempt to sugar an external path like Fn<(A, B,), C> to Fn(A, B) -> C - (Some(did), Some(ref tcx)) if tcx.lang_items.fn_trait_kind(did).is_some() => { + Some(did) if cx.tcx.lang_items.fn_trait_kind(did).is_some() => { assert_eq!(types.len(), 1); let inputs = match types[0].sty { ty::TyTuple(ref tys) => tys.iter().map(|t| t.clean(cx)).collect(), @@ -641,7 +651,7 @@ fn external_path_params(cx: &DocContext, trait_did: Option, has_self: boo output: output } }, - (..) => { + _ => { PathParameters::AngleBracketed { lifetimes: lifetimes, types: types.clean(cx), @@ -666,10 +676,7 @@ fn external_path(cx: &DocContext, name: &str, trait_did: Option, has_self impl Clean for ty::BuiltinBound { fn clean(&self, cx: &DocContext) -> TyParamBound { - let tcx = match cx.tcx_opt() { - Some(tcx) => tcx, - None => return RegionBound(Lifetime::statik()) - }; + let tcx = cx.tcx; let empty = tcx.intern_substs(&[]); let (did, path) = match *self { ty::BoundSend => @@ -700,12 +707,8 @@ impl Clean for ty::BuiltinBound { impl<'tcx> Clean for ty::TraitRef<'tcx> { fn clean(&self, cx: &DocContext) -> TyParamBound { - let tcx = match cx.tcx_opt() { - Some(tcx) => tcx, - None => return RegionBound(Lifetime::statik()) - }; inline::record_extern_fqn(cx, self.def_id, TypeKind::Trait); - let path = external_path(cx, &tcx.item_name(self.def_id).as_str(), + let path = external_path(cx, &cx.tcx.item_name(self.def_id).as_str(), Some(self.def_id), true, vec![], self.substs); debug!("ty::TraitRef\n subst: {:?}\n", self.substs); @@ -772,18 +775,16 @@ impl Lifetime { impl Clean for hir::Lifetime { fn clean(&self, cx: &DocContext) -> Lifetime { - if let Some(tcx) = cx.tcx_opt() { - let def = tcx.named_region_map.defs.get(&self.id).cloned(); - match def { - Some(DefEarlyBoundRegion(_, node_id)) | - Some(DefLateBoundRegion(_, node_id)) | - Some(DefFreeRegion(_, node_id)) => { - if let Some(lt) = cx.lt_substs.borrow().get(&node_id).cloned() { - return lt; - } + let def = cx.tcx.named_region_map.defs.get(&self.id).cloned(); + match def { + Some(DefEarlyBoundRegion(_, node_id)) | + Some(DefLateBoundRegion(_, node_id)) | + Some(DefFreeRegion(_, node_id)) => { + if let Some(lt) = cx.lt_substs.borrow().get(&node_id).cloned() { + return lt; } - _ => {} } + _ => {} } Lifetime(self.name.to_string()) } @@ -1048,7 +1049,7 @@ impl Clean for hir::MethodSig { }, output: self.decl.output.clean(cx), variadic: false, - attrs: Vec::new() + attrs: Attributes::default() }; Method { generics: self.generics.clean(cx), @@ -1076,7 +1077,7 @@ impl Clean for hir::MethodSig { }, output: self.decl.output.clean(cx), variadic: false, - attrs: Vec::new() + attrs: Attributes::default() }; TyMethod { unsafety: self.unsafety.clone(), @@ -1105,7 +1106,7 @@ impl Clean for doctree::Function { visibility: self.vis.clean(cx), stability: self.stab.clean(cx), deprecation: self.depr.clean(cx), - def_id: cx.map.local_def_id(self.id), + def_id: cx.tcx.map.local_def_id(self.id), inner: FunctionItem(Function { decl: self.decl.clean(cx), generics: self.generics.clean(cx), @@ -1122,7 +1123,7 @@ pub struct FnDecl { pub inputs: Arguments, pub output: FunctionRetTy, pub variadic: bool, - pub attrs: Vec, + pub attrs: Attributes, } impl FnDecl { @@ -1148,7 +1149,7 @@ impl Clean for hir::FnDecl { }, output: self.output.clean(cx), variadic: self.variadic, - attrs: Vec::new() + attrs: Attributes::default() } } } @@ -1156,14 +1157,14 @@ impl Clean for hir::FnDecl { impl<'a, 'tcx> Clean for (DefId, &'a ty::PolyFnSig<'tcx>) { fn clean(&self, cx: &DocContext) -> FnDecl { let (did, sig) = *self; - let mut names = if cx.map.as_local_node_id(did).is_some() { + let mut names = if cx.tcx.map.as_local_node_id(did).is_some() { vec![].into_iter() } else { - cx.tcx().sess.cstore.fn_arg_names(did).into_iter() + cx.tcx.sess.cstore.fn_arg_names(did).into_iter() }.peekable(); FnDecl { output: Return(sig.0.output.clean(cx)), - attrs: Vec::new(), + attrs: Attributes::default(), variadic: sig.0.variadic, inputs: Arguments { values: sig.0.inputs.iter().map(|t| { @@ -1247,7 +1248,7 @@ impl Clean for doctree::Trait { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.map.local_def_id(self.id), + def_id: cx.tcx.map.local_def_id(self.id), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), deprecation: self.depr.clean(cx), @@ -1297,10 +1298,10 @@ impl Clean for hir::TraitItem { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.span.clean(cx), - def_id: cx.map.local_def_id(self.id), + def_id: cx.tcx.map.local_def_id(self.id), visibility: None, - stability: get_stability(cx, cx.map.local_def_id(self.id)), - deprecation: get_deprecation(cx, cx.map.local_def_id(self.id)), + stability: get_stability(cx, cx.tcx.map.local_def_id(self.id)), + deprecation: get_deprecation(cx, cx.tcx.map.local_def_id(self.id)), inner: inner } } @@ -1329,10 +1330,10 @@ impl Clean for hir::ImplItem { name: Some(self.name.clean(cx)), source: self.span.clean(cx), attrs: self.attrs.clean(cx), - def_id: cx.map.local_def_id(self.id), + def_id: cx.tcx.map.local_def_id(self.id), visibility: self.vis.clean(cx), - stability: get_stability(cx, cx.map.local_def_id(self.id)), - deprecation: get_deprecation(cx, cx.map.local_def_id(self.id)), + stability: get_stability(cx, cx.tcx.map.local_def_id(self.id)), + deprecation: get_deprecation(cx, cx.tcx.map.local_def_id(self.id)), inner: inner } } @@ -1342,13 +1343,13 @@ impl<'tcx> Clean for ty::AssociatedItem { fn clean(&self, cx: &DocContext) -> Item { let inner = match self.kind { ty::AssociatedKind::Const => { - let ty = cx.tcx().item_type(self.def_id); + let ty = cx.tcx.item_type(self.def_id); AssociatedConstItem(ty.clean(cx), None) } ty::AssociatedKind::Method => { - let generics = (cx.tcx().item_generics(self.def_id), - &cx.tcx().item_predicates(self.def_id)).clean(cx); - let fty = match cx.tcx().item_type(self.def_id).sty { + let generics = (cx.tcx.item_generics(self.def_id), + &cx.tcx.item_predicates(self.def_id)).clean(cx); + let fty = match cx.tcx.item_type(self.def_id).sty { ty::TyFnDef(_, _, f) => f, _ => unreachable!() }; @@ -1357,9 +1358,9 @@ impl<'tcx> Clean for ty::AssociatedItem { if self.method_has_self_argument { let self_ty = match self.container { ty::ImplContainer(def_id) => { - cx.tcx().item_type(def_id) + cx.tcx.item_type(def_id) } - ty::TraitContainer(_) => cx.tcx().mk_self_type() + ty::TraitContainer(_) => cx.tcx.mk_self_type() }; let self_arg_ty = *fty.sig.input(0).skip_binder(); if self_arg_ty == self_ty { @@ -1405,8 +1406,8 @@ impl<'tcx> Clean for ty::AssociatedItem { // are actually located on the trait/impl itself, so we need to load // all of the generics from there and then look for bounds that are // applied to this associated type in question. - let def = cx.tcx().lookup_trait_def(did); - let predicates = cx.tcx().item_predicates(did); + let def = cx.tcx.lookup_trait_def(did); + let predicates = cx.tcx.item_predicates(did); let generics = (def.generics, &predicates).clean(cx); generics.where_predicates.iter().filter_map(|pred| { let (name, self_type, trait_, bounds) = match *pred { @@ -1442,7 +1443,7 @@ impl<'tcx> Clean for ty::AssociatedItem { } let ty = if self.defaultness.has_value() { - Some(cx.tcx().item_type(self.def_id)) + Some(cx.tcx.item_type(self.def_id)) } else { None }; @@ -1457,7 +1458,7 @@ impl<'tcx> Clean for ty::AssociatedItem { stability: get_stability(cx, self.def_id), deprecation: get_deprecation(cx, self.def_id), def_id: self.def_id, - attrs: inline::load_attrs(cx, cx.tcx(), self.def_id), + attrs: inline::load_attrs(cx, self.def_id), source: Span::empty(), inner: inner, } @@ -1616,11 +1617,11 @@ impl PrimitiveType { } } - fn find(attrs: &[Attribute]) -> Option { - for attr in attrs.list("doc") { - if let NameValue(ref k, ref v) = *attr { - if "primitive" == *k { - if let ret@Some(..) = PrimitiveType::from_str(v) { + fn find(attrs: &Attributes) -> Option { + for attr in attrs.lists("doc") { + if let Some(v) = attr.value_str() { + if attr.check_name("primitive") { + if let ret@Some(..) = PrimitiveType::from_str(&v.as_str()) { return ret; } } @@ -1710,53 +1711,44 @@ impl Clean for hir::Ty { type_: box m.ty.clean(cx)}, TySlice(ref ty) => Vector(box ty.clean(cx)), TyArray(ref ty, ref e) => { - let n = if let Some(tcx) = cx.tcx_opt() { - use rustc_const_math::{ConstInt, ConstUsize}; - use rustc_const_eval::eval_const_expr; - use rustc::middle::const_val::ConstVal; - match eval_const_expr(tcx, e) { - ConstVal::Integral(ConstInt::Usize(u)) => match u { - ConstUsize::Us16(u) => u.to_string(), - ConstUsize::Us32(u) => u.to_string(), - ConstUsize::Us64(u) => u.to_string(), - }, - // after type checking this can't fail - _ => unreachable!(), - } - } else { - pprust::expr_to_string(e) + use rustc_const_math::{ConstInt, ConstUsize}; + use rustc_const_eval::eval_const_expr; + use rustc::middle::const_val::ConstVal; + + let n = match eval_const_expr(cx.tcx, e) { + ConstVal::Integral(ConstInt::Usize(u)) => match u { + ConstUsize::Us16(u) => u.to_string(), + ConstUsize::Us32(u) => u.to_string(), + ConstUsize::Us64(u) => u.to_string(), + }, + // after type checking this can't fail + _ => unreachable!(), }; FixedVector(box ty.clean(cx), n) }, TyTup(ref tys) => Tuple(tys.clean(cx)), TyPath(None, ref path) => { - let tcx_and_def = cx.tcx_opt().map(|tcx| (tcx, tcx.expect_def(self.id))); - if let Some((_, def)) = tcx_and_def { - if let Some(new_ty) = cx.ty_substs.borrow().get(&def).cloned() { - return new_ty; - } + let def = cx.tcx.expect_def(self.id); + if let Some(new_ty) = cx.ty_substs.borrow().get(&def).cloned() { + return new_ty; } - let tcx_and_alias = tcx_and_def.and_then(|(tcx, def)| { - if let Def::TyAlias(def_id) = def { - // Substitute private type aliases - tcx.map.as_local_node_id(def_id).and_then(|node_id| { - if !cx.access_levels.borrow().is_exported(def_id) { - Some((tcx, &tcx.map.expect_item(node_id).node)) - } else { - None - } - }) - } else { - None + let mut alias = None; + if let Def::TyAlias(def_id) = def { + // Substitute private type aliases + if let Some(node_id) = cx.tcx.map.as_local_node_id(def_id) { + if !cx.access_levels.borrow().is_exported(def_id) { + alias = Some(&cx.tcx.map.expect_item(node_id).node); + } } - }); - if let Some((tcx, &hir::ItemTy(ref ty, ref generics))) = tcx_and_alias { + }; + + if let Some(&hir::ItemTy(ref ty, ref generics)) = alias { let provided_params = &path.segments.last().unwrap().parameters; let mut ty_substs = FxHashMap(); let mut lt_substs = FxHashMap(); for (i, ty_param) in generics.ty_params.iter().enumerate() { - let ty_param_def = tcx.expect_def(ty_param.id); + let ty_param_def = cx.tcx.expect_def(ty_param.id); if let Some(ty) = provided_params.types().get(i).cloned() .cloned() { ty_substs.insert(ty_param_def, ty.unwrap().clean(cx)); @@ -1824,9 +1816,7 @@ impl<'tcx> Clean for ty::Ty<'tcx> { ty::TyFloat(float_ty) => Primitive(float_ty.into()), ty::TyStr => Primitive(PrimitiveType::Str), ty::TyBox(t) => { - let box_did = cx.tcx_opt().and_then(|tcx| { - tcx.lang_items.owned_box() - }); + let box_did = cx.tcx.lang_items.owned_box(); lang_struct(cx, box_did, t, "Box", Unique) } ty::TySlice(ty) => Vector(box ty.clean(cx)), @@ -1846,7 +1836,7 @@ impl<'tcx> Clean for ty::Ty<'tcx> { type_params: Vec::new(), where_predicates: Vec::new() }, - decl: (cx.map.local_def_id(ast::CRATE_NODE_ID), &fty.sig).clean(cx), + decl: (cx.tcx.map.local_def_id(ast::CRATE_NODE_ID), &fty.sig).clean(cx), abi: fty.abi, }), ty::TyAdt(def, substs) => { @@ -1857,7 +1847,7 @@ impl<'tcx> Clean for ty::Ty<'tcx> { AdtKind::Enum => TypeKind::Enum, }; inline::record_extern_fqn(cx, did, kind); - let path = external_path(cx, &cx.tcx().item_name(did).as_str(), + let path = external_path(cx, &cx.tcx.item_name(did).as_str(), None, false, vec![], substs); ResolvedPath { path: path, @@ -1884,7 +1874,7 @@ impl<'tcx> Clean for ty::Ty<'tcx> { }); } - let path = external_path(cx, &cx.tcx().item_name(did).as_str(), + let path = external_path(cx, &cx.tcx.item_name(did).as_str(), Some(did), false, bindings, obj.principal.0.substs); ResolvedPath { path: path, @@ -1902,9 +1892,9 @@ impl<'tcx> Clean for ty::Ty<'tcx> { ty::TyAnon(def_id, substs) => { // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, // by looking up the projections associated with the def_id. - let item_predicates = cx.tcx().item_predicates(def_id); - let substs = cx.tcx().lift(&substs).unwrap(); - let bounds = item_predicates.instantiate(cx.tcx(), substs); + let item_predicates = cx.tcx.item_predicates(def_id); + let substs = cx.tcx.lift(&substs).unwrap(); + let bounds = item_predicates.instantiate(cx.tcx, substs); ImplTrait(bounds.predicates.into_iter().filter_map(|predicate| { predicate.to_opt_poly_trait_ref().clean(cx) }).collect()) @@ -1925,9 +1915,9 @@ impl Clean for hir::StructField { attrs: self.attrs.clean(cx), source: self.span.clean(cx), visibility: self.vis.clean(cx), - stability: get_stability(cx, cx.map.local_def_id(self.id)), - deprecation: get_deprecation(cx, cx.map.local_def_id(self.id)), - def_id: cx.map.local_def_id(self.id), + stability: get_stability(cx, cx.tcx.map.local_def_id(self.id)), + deprecation: get_deprecation(cx, cx.tcx.map.local_def_id(self.id)), + def_id: cx.tcx.map.local_def_id(self.id), inner: StructFieldItem(self.ty.clean(cx)), } } @@ -1937,7 +1927,7 @@ impl<'tcx> Clean for ty::FieldDefData<'tcx, 'static> { fn clean(&self, cx: &DocContext) -> Item { Item { name: Some(self.name).clean(cx), - attrs: cx.tcx().get_attrs(self.did).clean(cx), + attrs: cx.tcx.get_attrs(self.did).clean(cx), source: Span::empty(), visibility: self.vis.clean(cx), stability: get_stability(cx, self.did), @@ -1988,7 +1978,7 @@ impl Clean for doctree::Struct { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.map.local_def_id(self.id), + def_id: cx.tcx.map.local_def_id(self.id), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), deprecation: self.depr.clean(cx), @@ -2008,7 +1998,7 @@ impl Clean for doctree::Union { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.map.local_def_id(self.id), + def_id: cx.tcx.map.local_def_id(self.id), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), deprecation: self.depr.clean(cx), @@ -2055,7 +2045,7 @@ impl Clean for doctree::Enum { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.map.local_def_id(self.id), + def_id: cx.tcx.map.local_def_id(self.id), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), deprecation: self.depr.clean(cx), @@ -2082,7 +2072,7 @@ impl Clean for doctree::Variant { visibility: None, stability: self.stab.clean(cx), deprecation: self.depr.clean(cx), - def_id: cx.map.local_def_id(self.def.id()), + def_id: cx.tcx.map.local_def_id(self.def.id()), inner: VariantItem(Variant { kind: self.def.clean(cx), }), @@ -2107,7 +2097,7 @@ impl<'tcx> Clean for ty::VariantDefData<'tcx, 'static> { Item { source: Span::empty(), name: Some(field.name.clean(cx)), - attrs: cx.tcx().get_attrs(field.did).clean(cx), + attrs: cx.tcx.get_attrs(field.did).clean(cx), visibility: field.vis.clean(cx), def_id: field.did, stability: get_stability(cx, field.did), @@ -2120,7 +2110,7 @@ impl<'tcx> Clean for ty::VariantDefData<'tcx, 'static> { }; Item { name: Some(self.name.clean(cx)), - attrs: inline::load_attrs(cx, cx.tcx(), self.did), + attrs: inline::load_attrs(cx, self.did), source: Span::empty(), visibility: Some(Inherited), def_id: self.did, @@ -2305,7 +2295,7 @@ impl Clean for doctree::Typedef { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.map.local_def_id(self.id.clone()), + def_id: cx.tcx.map.local_def_id(self.id.clone()), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), deprecation: self.depr.clean(cx), @@ -2357,7 +2347,7 @@ impl Clean for doctree::Static { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.map.local_def_id(self.id), + def_id: cx.tcx.map.local_def_id(self.id), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), deprecation: self.depr.clean(cx), @@ -2382,7 +2372,7 @@ impl Clean for doctree::Constant { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.map.local_def_id(self.id), + def_id: cx.tcx.map.local_def_id(self.id), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), deprecation: self.depr.clean(cx), @@ -2443,24 +2433,22 @@ impl Clean> for doctree::Impl { // If this impl block is an implementation of the Deref trait, then we // need to try inlining the target's inherent impl blocks as well. - if trait_.def_id() == cx.deref_trait_did.get() { + if trait_.def_id() == cx.tcx.lang_items.deref_trait() { build_deref_target_impls(cx, &items, &mut ret); } - let provided = trait_.def_id().and_then(|did| { - cx.tcx_opt().map(|tcx| { - tcx.provided_trait_methods(did) - .into_iter() - .map(|meth| meth.name.to_string()) - .collect() - }) + let provided = trait_.def_id().map(|did| { + cx.tcx.provided_trait_methods(did) + .into_iter() + .map(|meth| meth.name.to_string()) + .collect() }).unwrap_or(FxHashSet()); ret.push(Item { name: None, attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.map.local_def_id(self.id), + def_id: cx.tcx.map.local_def_id(self.id), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), deprecation: self.depr.clean(cx), @@ -2481,10 +2469,7 @@ impl Clean> for doctree::Impl { fn build_deref_target_impls(cx: &DocContext, items: &[Item], ret: &mut Vec) { - let tcx = match cx.tcx_opt() { - Some(t) => t, - None => return, - }; + let tcx = cx.tcx; for item in items { let target = match item.inner { @@ -2494,7 +2479,7 @@ fn build_deref_target_impls(cx: &DocContext, let primitive = match *target { ResolvedPath { did, .. } if did.is_local() => continue, ResolvedPath { did, .. } => { - ret.extend(inline::build_impls(cx, tcx, did)); + ret.extend(inline::build_impls(cx, did)); continue } _ => match target.primitive_type() { @@ -2525,7 +2510,7 @@ fn build_deref_target_impls(cx: &DocContext, }; if let Some(did) = did { if !did.is_local() { - inline::build_impl(cx, tcx, did, ret); + inline::build_impl(cx, did, ret); } } } @@ -2543,7 +2528,7 @@ impl Clean for doctree::DefaultImpl { name: None, attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.map.local_def_id(self.id), + def_id: cx.tcx.map.local_def_id(self.id), visibility: Some(Public), stability: None, deprecation: None, @@ -2627,7 +2612,7 @@ impl Clean> for doctree::Import { name: None, attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.map.local_def_id(ast::CRATE_NODE_ID), + def_id: cx.tcx.map.local_def_id(ast::CRATE_NODE_ID), visibility: self.vis.clean(cx), stability: None, deprecation: None, @@ -2706,10 +2691,10 @@ impl Clean for hir::ForeignItem { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.span.clean(cx), - def_id: cx.map.local_def_id(self.id), + def_id: cx.tcx.map.local_def_id(self.id), visibility: self.vis.clean(cx), - stability: get_stability(cx, cx.map.local_def_id(self.id)), - deprecation: get_deprecation(cx, cx.map.local_def_id(self.id)), + stability: get_stability(cx, cx.tcx.map.local_def_id(self.id)), + deprecation: get_deprecation(cx, cx.tcx.map.local_def_id(self.id)), inner: inner, } } @@ -2776,22 +2761,7 @@ fn resolve_type(cx: &DocContext, path: Path, id: ast::NodeId) -> Type { debug!("resolve_type({:?},{:?})", path, id); - let tcx = match cx.tcx_opt() { - Some(tcx) => tcx, - // If we're extracting tests, this return value's accuracy is not - // important, all we want is a string representation to help people - // figure out what doctests are failing. - None => { - let did = DefId::local(DefIndex::from_u32(0)); - return ResolvedPath { - path: path, - typarams: None, - did: did, - is_generic: false - }; - } - }; - let def = tcx.expect_def(id); + let def = cx.tcx.expect_def(id); debug!("resolve_type: def={:?}", def); let is_generic = match def { @@ -2816,8 +2786,6 @@ fn resolve_type(cx: &DocContext, fn register_def(cx: &DocContext, def: Def) -> DefId { debug!("register_def({:?})", def); - let tcx = cx.tcx(); - let (did, kind) = match def { Def::Fn(i) => (i, TypeKind::Function), Def::TyAlias(i) => (i, TypeKind::Typedef), @@ -2827,7 +2795,7 @@ fn register_def(cx: &DocContext, def: Def) -> DefId { Def::Union(i) => (i, TypeKind::Union), Def::Mod(i) => (i, TypeKind::Module), Def::Static(i, _) => (i, TypeKind::Static), - Def::Variant(i) => (tcx.parent_def_id(i).unwrap(), TypeKind::Enum), + Def::Variant(i) => (cx.tcx.parent_def_id(i).unwrap(), TypeKind::Enum), Def::SelfTy(Some(def_id), _) => (def_id, TypeKind::Trait), Def::SelfTy(_, Some(impl_def_id)) => { return impl_def_id @@ -2837,7 +2805,7 @@ fn register_def(cx: &DocContext, def: Def) -> DefId { if did.is_local() { return did } inline::record_extern_fqn(cx, did, kind); if let TypeKind::Trait = kind { - let t = inline::build_external_trait(cx, tcx, did); + let t = inline::build_external_trait(cx, did); cx.external_traits.borrow_mut().insert(did, t); } did @@ -2851,9 +2819,7 @@ fn resolve_use_source(cx: &DocContext, path: Path, id: ast::NodeId) -> ImportSou } fn resolve_def(cx: &DocContext, id: ast::NodeId) -> Option { - cx.tcx_opt().and_then(|tcx| { - tcx.expect_def_or_none(id).map(|def| register_def(cx, def)) - }) + cx.tcx.expect_def_or_none(id).map(|def| register_def(cx, def)) } #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] @@ -2872,7 +2838,7 @@ impl Clean for doctree::Macro { visibility: Some(Public), stability: self.stab.clean(cx), deprecation: self.depr.clean(cx), - def_id: cx.map.local_def_id(self.id), + def_id: cx.tcx.map.local_def_id(self.id), inner: MacroItem(Macro { source: format!("macro_rules! {} {{\n{}}}", name, diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 19e084905aa92..7240f0aedbd27 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -153,7 +153,7 @@ fn trait_is_same_or_supertrait(cx: &DocContext, child: DefId, if child == trait_ { return true } - let predicates = cx.tcx().item_super_predicates(child).predicates; + let predicates = cx.tcx.item_super_predicates(child).predicates; predicates.iter().filter_map(|pred| { if let ty::Predicate::Trait(ref pred) = *pred { if pred.0.trait_ref.self_ty().is_self() { diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index a25cb0bacc5cf..7d7b7fead5854 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -7,7 +7,6 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use self::MaybeTyped::*; use rustc_lint; use rustc_driver::{driver, target_features, abort_on_err}; @@ -42,21 +41,12 @@ use html::render::RenderInfo; pub use rustc::session::config::Input; pub use rustc::session::search_paths::SearchPaths; -/// Are we generating documentation (`Typed`) or tests (`NotTyped`)? -pub enum MaybeTyped<'a, 'tcx: 'a> { - Typed(TyCtxt<'a, 'tcx, 'tcx>), - NotTyped(&'a session::Session) -} - pub type ExternalPaths = FxHashMap, clean::TypeKind)>; pub struct DocContext<'a, 'tcx: 'a> { - pub map: &'a hir_map::Map<'tcx>, - pub maybe_typed: MaybeTyped<'a, 'tcx>, + pub tcx: TyCtxt<'a, 'tcx, 'tcx>, pub input: Input, pub populated_all_crate_impls: Cell, - pub deref_trait_did: Cell>, - pub deref_mut_trait_did: Cell>, // Note that external items for which `doc(hidden)` applies to are shown as // non-reachable while local items aren't. This is because we're reusing // the access levels from crateanalysis. @@ -77,24 +67,9 @@ pub struct DocContext<'a, 'tcx: 'a> { pub export_map: ExportMap, } -impl<'b, 'tcx> DocContext<'b, 'tcx> { - pub fn sess<'a>(&'a self) -> &'a session::Session { - match self.maybe_typed { - Typed(tcx) => &tcx.sess, - NotTyped(ref sess) => sess - } - } - - pub fn tcx_opt<'a>(&'a self) -> Option> { - match self.maybe_typed { - Typed(tcx) => Some(tcx), - NotTyped(_) => None - } - } - - pub fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> { - let tcx_opt = self.tcx_opt(); - tcx_opt.expect("tcx not present") +impl<'a, 'tcx> DocContext<'a, 'tcx> { + pub fn sess(&self) -> &session::Session { + &self.tcx.sess } /// Call the closure with the given parameters set as @@ -208,12 +183,9 @@ pub fn run_core(search_paths: SearchPaths, }; let ctxt = DocContext { - map: &tcx.map, - maybe_typed: Typed(tcx), + tcx: tcx, input: input, populated_all_crate_impls: Cell::new(false), - deref_trait_did: Cell::new(None), - deref_mut_trait_did: Cell::new(None), access_levels: RefCell::new(access_levels), external_traits: Default::default(), renderinfo: Default::default(), @@ -221,11 +193,11 @@ pub fn run_core(search_paths: SearchPaths, lt_substs: Default::default(), export_map: export_map, }; - debug!("crate: {:?}", ctxt.map.krate()); + debug!("crate: {:?}", tcx.map.krate()); let krate = { let mut v = RustdocVisitor::new(&ctxt); - v.visit(ctxt.map.krate()); + v.visit(tcx.map.krate()); v.clean(&ctxt) }; diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 31fbcb5059f82..757db81c44021 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -53,7 +53,7 @@ use std::sync::Arc; use externalfiles::ExternalHtml; use serialize::json::{ToJson, Json, as_json}; -use syntax::abi; +use syntax::{abi, ast}; use syntax::feature_gate::UnstableFeatures; use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId, LOCAL_CRATE}; use rustc::middle::privacy::AccessLevels; @@ -62,7 +62,7 @@ use rustc::hir; use rustc::util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::flock; -use clean::{self, Attributes, GetDefId, SelfTy, Mutability}; +use clean::{self, AttributesExt, GetDefId, SelfTy, Mutability}; use doctree; use fold::DocFolder; use html::escape::Escape; @@ -453,30 +453,26 @@ pub fn run(mut krate: clean::Crate, // Crawl the crate attributes looking for attributes which control how we're // going to emit HTML - if let Some(attrs) = krate.module.as_ref().map(|m| m.attrs.list("doc")) { - for attr in attrs { - match *attr { - clean::NameValue(ref x, ref s) - if "html_favicon_url" == *x => { + if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) { + for attr in attrs.lists("doc") { + let name = attr.name().map(|s| s.as_str()); + match (name.as_ref().map(|s| &s[..]), attr.value_str()) { + (Some("html_favicon_url"), Some(s)) => { scx.layout.favicon = s.to_string(); } - clean::NameValue(ref x, ref s) - if "html_logo_url" == *x => { + (Some("html_logo_url"), Some(s)) => { scx.layout.logo = s.to_string(); } - clean::NameValue(ref x, ref s) - if "html_playground_url" == *x => { + (Some("html_playground_url"), Some(s)) => { markdown::PLAYGROUND.with(|slot| { let name = krate.name.clone(); - *slot.borrow_mut() = Some((Some(name), s.clone())); + *slot.borrow_mut() = Some((Some(name), s.to_string())); }); } - clean::NameValue(ref x, ref s) - if "issue_tracker_base_url" == *x => { + (Some("issue_tracker_base_url"), Some(s)) => { scx.issue_tracker_base_url = Some(s.to_string()); } - clean::Word(ref x) - if "html_no_source" == *x => { + (Some("html_no_source"), None) if attr.is_word() => { scx.include_sources = false; } _ => {} @@ -860,13 +856,16 @@ fn extern_location(e: &clean::ExternalCrate, dst: &Path) -> ExternalLocation { // Failing that, see if there's an attribute specifying where to find this // external crate - e.attrs.list("doc").value("html_root_url").map(|url| { - let mut url = url.to_owned(); + e.attrs.lists("doc") + .filter(|a| a.check_name("html_root_url")) + .filter_map(|a| a.value_str()) + .map(|url| { + let mut url = url.to_string(); if !url.ends_with("/") { url.push('/') } Remote(url) - }).unwrap_or(Unknown) // Well, at least we tried. + }).next().unwrap_or(Unknown) // Well, at least we tried. } impl<'a> DocFolder for SourceCollector<'a> { @@ -2511,49 +2510,47 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, Ok(()) } -fn attribute_without_value(s: &str) -> bool { - ["must_use", "no_mangle", "unsafe_destructor_blind_to_params"].iter().any(|x| x == &s) -} - -fn attribute_with_value(s: &str) -> bool { - ["export_name", "lang", "link_section", "must_use"].iter().any(|x| x == &s) -} - -fn attribute_with_values(s: &str) -> bool { - ["repr"].iter().any(|x| x == &s) -} +fn render_attribute(attr: &ast::MetaItem) -> Option { + let name = attr.name(); -fn render_attribute(attr: &clean::Attribute, recurse: bool) -> Option { - match *attr { - clean::Word(ref s) if attribute_without_value(&*s) || recurse => { - Some(format!("{}", s)) - } - clean::NameValue(ref k, ref v) if attribute_with_value(&*k) => { - Some(format!("{} = \"{}\"", k, v)) - } - clean::List(ref k, ref values) if attribute_with_values(&*k) => { - let display: Vec<_> = values.iter() - .filter_map(|value| render_attribute(value, true)) - .map(|entry| format!("{}", entry)) - .collect(); + if attr.is_word() { + Some(format!("{}", name)) + } else if let Some(v) = attr.value_str() { + Some(format!("{} = {:?}", name, &v.as_str()[..])) + } else if let Some(values) = attr.meta_item_list() { + let display: Vec<_> = values.iter().filter_map(|attr| { + attr.meta_item().and_then(|mi| render_attribute(mi)) + }).collect(); - if display.len() > 0 { - Some(format!("{}({})", k, display.join(", "))) - } else { - None - } - } - _ => { + if display.len() > 0 { + Some(format!("{}({})", name, display.join(", "))) + } else { None } + } else { + None } } +const ATTRIBUTE_WHITELIST: &'static [&'static str] = &[ + "export_name", + "lang", + "link_section", + "must_use", + "no_mangle", + "repr", + "unsafe_destructor_blind_to_params" +]; + fn render_attributes(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result { let mut attrs = String::new(); - for attr in &it.attrs { - if let Some(s) = render_attribute(attr, false) { + for attr in &it.attrs.other_attrs { + let name = attr.name(); + if !ATTRIBUTE_WHITELIST.contains(&&name.as_str()[..]) { + continue; + } + if let Some(s) = render_attribute(attr.meta()) { attrs.push_str(&format!("#[{}]\n", s)); } } @@ -2810,7 +2807,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi } write!(w, "")?; write!(w, "\n")?; - if let Some(ref dox) = i.impl_item.attrs.value("doc") { + if let Some(ref dox) = i.impl_item.doc_value() { write!(w, "
{}
", Markdown(dox))?; } } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 3af7c20c1336e..60ce7ea53953a 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -89,7 +89,7 @@ pub mod visit_ast; pub mod visit_lib; pub mod test; -use clean::Attributes; +use clean::AttributesExt; struct Output { krate: clean::Crate, @@ -280,43 +280,45 @@ pub fn main_args(args: &[String]) -> isize { !matches.opt_present("markdown-no-toc")), (false, false) => {} } - let out = match acquire_input(input, externs, &matches) { - Ok(out) => out, - Err(s) => { - println!("input error: {}", s); - return 1; - } - }; - let Output { krate, passes, renderinfo } = out; - info!("going to format"); - match matches.opt_str("w").as_ref().map(|s| &**s) { - Some("html") | None => { - html::render::run(krate, &external_html, - output.unwrap_or(PathBuf::from("doc")), - passes.into_iter().collect(), - css_file_extension, - renderinfo) - .expect("failed to generate documentation"); - 0 - } - Some(s) => { - println!("unknown output format: {}", s); - 1 + + let output_format = matches.opt_str("w"); + let res = acquire_input(input, externs, &matches, move |out| { + let Output { krate, passes, renderinfo } = out; + info!("going to format"); + match output_format.as_ref().map(|s| &**s) { + Some("html") | None => { + html::render::run(krate, &external_html, + output.unwrap_or(PathBuf::from("doc")), + passes.into_iter().collect(), + css_file_extension, + renderinfo) + .expect("failed to generate documentation"); + 0 + } + Some(s) => { + println!("unknown output format: {}", s); + 1 + } } - } + }); + res.unwrap_or_else(|s| { + println!("input error: {}", s); + 1 + }) } /// Looks inside the command line arguments to extract the relevant input format /// and files and then generates the necessary rustdoc output for formatting. -fn acquire_input(input: &str, - externs: Externs, - matches: &getopts::Matches) -> Result { +fn acquire_input(input: &str, + externs: Externs, + matches: &getopts::Matches, + f: F) + -> Result +where R: 'static + Send, F: 'static + Send + FnOnce(Output) -> R { match matches.opt_str("r").as_ref().map(|s| &**s) { - Some("rust") => Ok(rust_input(input, externs, matches)), + Some("rust") => Ok(rust_input(input, externs, matches, f)), Some(s) => Err(format!("unknown input format: {}", s)), - None => { - Ok(rust_input(input, externs, matches)) - } + None => Ok(rust_input(input, externs, matches, f)) } } @@ -342,7 +344,8 @@ fn parse_externs(matches: &getopts::Matches) -> Result { /// generated from the cleaned AST of the crate. /// /// This form of input will run all of the plug/cleaning passes -fn rust_input(cratefile: &str, externs: Externs, matches: &getopts::Matches) -> Output { +fn rust_input(cratefile: &str, externs: Externs, matches: &getopts::Matches, f: F) -> R +where R: 'static + Send, F: 'static + Send + FnOnce(Output) -> R { let mut default_passes = !matches.opt_present("no-defaults"); let mut passes = matches.opt_strs("passes"); let mut plugins = matches.opt_strs("plugins"); @@ -355,6 +358,8 @@ fn rust_input(cratefile: &str, externs: Externs, matches: &getopts::Matches) -> let cfgs = matches.opt_strs("cfg"); let triple = matches.opt_str("target"); let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from); + let crate_name = matches.opt_str("crate-name"); + let plugin_path = matches.opt_str("plugin-path"); let cr = PathBuf::from(cratefile); info!("starting to run rustc"); @@ -363,67 +368,68 @@ fn rust_input(cratefile: &str, externs: Externs, matches: &getopts::Matches) -> rustc_driver::monitor(move || { use rustc::session::config::Input; - tx.send(core::run_core(paths, cfgs, externs, Input::File(cr), - triple, maybe_sysroot)).unwrap(); - }); - let (mut krate, renderinfo) = rx.recv().unwrap(); - info!("finished with rustc"); + let (mut krate, renderinfo) = + core::run_core(paths, cfgs, externs, Input::File(cr), triple, maybe_sysroot); - if let Some(name) = matches.opt_str("crate-name") { - krate.name = name - } + info!("finished with rustc"); - // Process all of the crate attributes, extracting plugin metadata along - // with the passes which we are supposed to run. - for attr in krate.module.as_ref().unwrap().attrs.list("doc") { - match *attr { - clean::Word(ref w) if "no_default_passes" == *w => { - default_passes = false; - }, - clean::NameValue(ref name, ref value) => { - let sink = match &name[..] { - "passes" => &mut passes, - "plugins" => &mut plugins, + if let Some(name) = crate_name { + krate.name = name + } + + // Process all of the crate attributes, extracting plugin metadata along + // with the passes which we are supposed to run. + for attr in krate.module.as_ref().unwrap().attrs.lists("doc") { + let name = attr.name().map(|s| s.as_str()); + let name = name.as_ref().map(|s| &s[..]); + if attr.is_word() { + if name == Some("no_default_passes") { + default_passes = false; + } + } else if let Some(value) = attr.value_str() { + let sink = match name { + Some("passes") => &mut passes, + Some("plugins") => &mut plugins, _ => continue, }; - for p in value.split_whitespace() { + for p in value.as_str().split_whitespace() { sink.push(p.to_string()); } } - _ => (), } - } - if default_passes { - for name in passes::DEFAULT_PASSES.iter().rev() { - passes.insert(0, name.to_string()); + if default_passes { + for name in passes::DEFAULT_PASSES.iter().rev() { + passes.insert(0, name.to_string()); + } } - } - // Load all plugins/passes into a PluginManager - let path = matches.opt_str("plugin-path") - .unwrap_or("/tmp/rustdoc/plugins".to_string()); - let mut pm = plugins::PluginManager::new(PathBuf::from(path)); - for pass in &passes { - let plugin = match passes::PASSES.iter() - .position(|&(p, ..)| { - p == *pass - }) { - Some(i) => passes::PASSES[i].1, - None => { - error!("unknown pass {}, skipping", *pass); - continue - }, - }; - pm.add_plugin(plugin); - } - info!("loading plugins..."); - for pname in plugins { - pm.load_plugin(pname); - } + // Load all plugins/passes into a PluginManager + let path = plugin_path.unwrap_or("/tmp/rustdoc/plugins".to_string()); + let mut pm = plugins::PluginManager::new(PathBuf::from(path)); + for pass in &passes { + let plugin = match passes::PASSES.iter() + .position(|&(p, ..)| { + p == *pass + }) { + Some(i) => passes::PASSES[i].1, + None => { + error!("unknown pass {}, skipping", *pass); + continue + }, + }; + pm.add_plugin(plugin); + } + info!("loading plugins..."); + for pname in plugins { + pm.load_plugin(pname); + } + + // Run everything! + info!("Executing passes/plugins"); + let krate = pm.run_plugins(krate); - // Run everything! - info!("Executing passes/plugins"); - let krate = pm.run_plugins(krate); - Output { krate: krate, renderinfo: renderinfo, passes: passes } + tx.send(f(Output { krate: krate, renderinfo: renderinfo, passes: passes })).unwrap(); + }); + rx.recv().unwrap() } diff --git a/src/librustdoc/passes/collapse_docs.rs b/src/librustdoc/passes/collapse_docs.rs index c034ef9326846..3c63302127c5e 100644 --- a/src/librustdoc/passes/collapse_docs.rs +++ b/src/librustdoc/passes/collapse_docs.rs @@ -8,40 +8,33 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::string::String; - use clean::{self, Item}; use plugins; use fold; use fold::DocFolder; pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult { - let mut collapser = Collapser; - let krate = collapser.fold_crate(krate); - krate + Collapser.fold_crate(krate) } struct Collapser; impl fold::DocFolder for Collapser { fn fold_item(&mut self, mut i: Item) -> Option { - let mut docstr = String::new(); - for attr in &i.attrs { - if let clean::NameValue(ref x, ref s) = *attr { - if "doc" == *x { - docstr.push_str(s); - docstr.push('\n'); - } - } - } - let mut a: Vec = i.attrs.iter().filter(|&a| match a { - &clean::NameValue(ref x, _) if "doc" == *x => false, - _ => true - }).cloned().collect(); - if !docstr.is_empty() { - a.push(clean::NameValue("doc".to_string(), docstr)); - } - i.attrs = a; + i.attrs.collapse_doc_comments(); self.fold_item_recur(i) } } + +impl clean::Attributes { + pub fn collapse_doc_comments(&mut self) { + let mut doc_string = self.doc_strings.join("\n"); + if doc_string.is_empty() { + self.doc_strings = vec![]; + } else { + // FIXME(eddyb) Is this still needed? + doc_string.push('\n'); + self.doc_strings = vec![doc_string]; + } + } +} diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index 927ccf9171999..68c1231fc6f7c 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -11,7 +11,7 @@ use rustc::util::nodemap::DefIdSet; use std::mem; -use clean::{self, Attributes}; +use clean::{self, AttributesExt, NestedAttributesExt}; use clean::Item; use plugins; use fold; @@ -41,7 +41,7 @@ struct Stripper<'a> { impl<'a> fold::DocFolder for Stripper<'a> { fn fold_item(&mut self, i: Item) -> Option { - if i.attrs.list("doc").has_word("hidden") { + if i.attrs.lists("doc").has_word("hidden") { debug!("found one in strip_hidden; removing"); // use a dedicated hidden item for given item type if any match i.inner { diff --git a/src/librustdoc/passes/unindent_comments.rs b/src/librustdoc/passes/unindent_comments.rs index 20640f3f88518..4d94c30847852 100644 --- a/src/librustdoc/passes/unindent_comments.rs +++ b/src/librustdoc/passes/unindent_comments.rs @@ -17,31 +17,26 @@ use plugins; use fold::{self, DocFolder}; pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult { - let mut cleaner = CommentCleaner; - let krate = cleaner.fold_crate(krate); - krate + CommentCleaner.fold_crate(krate) } struct CommentCleaner; impl fold::DocFolder for CommentCleaner { fn fold_item(&mut self, mut i: Item) -> Option { - let mut avec: Vec = Vec::new(); - for attr in &i.attrs { - match attr { - &clean::NameValue(ref x, ref s) - if "doc" == *x => { - avec.push(clean::NameValue("doc".to_string(), - unindent(s))) - } - x => avec.push(x.clone()) - } - } - i.attrs = avec; + i.attrs.unindent_doc_comments(); self.fold_item_recur(i) } } +impl clean::Attributes { + pub fn unindent_doc_comments(&mut self) { + for doc_string in &mut self.doc_strings { + *doc_string = unindent(doc_string); + } + } +} + fn unindent(s: &str) -> String { let lines = s.lines().collect:: >(); let mut saw_first_line = false; diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 12d33dcb207f7..9f29319430dd5 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::cell::Cell; use std::env; use std::ffi::OsString; use std::io::prelude::*; @@ -23,7 +22,8 @@ use std::sync::{Arc, Mutex}; use testing; use rustc_lint; use rustc::dep_graph::DepGraph; -use rustc::hir::map as hir_map; +use rustc::hir; +use rustc::hir::intravisit; use rustc::session::{self, config}; use rustc::session::config::{OutputType, OutputTypes, Externs}; use rustc::session::search_paths::{SearchPaths, PathKind}; @@ -33,18 +33,15 @@ use rustc_driver::{driver, Compilation}; use rustc_driver::driver::phase_2_configure_and_expand; use rustc_metadata::cstore::CStore; use rustc_resolve::MakeGlobMap; +use rustc_trans::back::link; +use syntax::ast; use syntax::codemap::CodeMap; use syntax::feature_gate::UnstableFeatures; use errors; use errors::emitter::ColorConfig; -use core; -use clean; -use clean::Clean; -use fold::DocFolder; +use clean::Attributes; use html::markdown; -use passes; -use visit_ast::RustdocVisitor; #[derive(Clone, Default)] pub struct TestOptions { @@ -87,48 +84,36 @@ pub fn run(input: &str, config::build_configuration(&sess, config::parse_cfgspecs(cfgs.clone())); let krate = panictry!(driver::phase_1_parse_input(&sess, &input)); - let driver::ExpansionResult { defs, mut hir_forest, analysis, .. } = { + let driver::ExpansionResult { defs, mut hir_forest, .. } = { phase_2_configure_and_expand( &sess, &cstore, krate, None, "rustdoc-test", None, MakeGlobMap::No, |_| Ok(()) ).expect("phase_2_configure_and_expand aborted in rustdoc!") }; - let dep_graph = DepGraph::new(false); + let crate_name = crate_name.unwrap_or_else(|| { + link::find_crate_name(None, &hir_forest.krate().attrs, &input) + }); let opts = scrape_test_config(hir_forest.krate()); - let _ignore = dep_graph.in_ignore(); - let map = hir_map::map_crate(&mut hir_forest, defs); - - let ctx = core::DocContext { - map: &map, - maybe_typed: core::NotTyped(&sess), - input: input, - populated_all_crate_impls: Cell::new(false), - external_traits: Default::default(), - deref_trait_did: Cell::new(None), - deref_mut_trait_did: Cell::new(None), - access_levels: Default::default(), - renderinfo: Default::default(), - ty_substs: Default::default(), - lt_substs: Default::default(), - export_map: analysis.export_map, - }; - - let mut v = RustdocVisitor::new(&ctx); - v.visit(ctx.map.krate()); - let mut krate = v.clean(&ctx); - if let Some(name) = crate_name { - krate.name = name; - } - let krate = passes::collapse_docs(krate); - let krate = passes::unindent_comments(krate); - - let mut collector = Collector::new(krate.name.to_string(), + let mut collector = Collector::new(crate_name, cfgs, libs, externs, false, opts); - collector.fold_crate(krate); + + { + let dep_graph = DepGraph::new(false); + let _ignore = dep_graph.in_ignore(); + let map = hir::map::map_crate(&mut hir_forest, defs); + let krate = map.krate(); + let mut hir_collector = HirCollector { + collector: &mut collector, + map: &map + }; + hir_collector.visit_testable("".to_string(), &krate.attrs, |this| { + intravisit::walk_crate(this, krate); + }); + } test_args.insert(0, "rustdoctest".to_string()); @@ -472,56 +457,84 @@ impl Collector { } } -impl DocFolder for Collector { - fn fold_item(&mut self, item: clean::Item) -> Option { - let current_name = match item.name { - Some(ref name) if !name.is_empty() => Some(name.clone()), - _ => typename_if_impl(&item) - }; +struct HirCollector<'a, 'hir: 'a> { + collector: &'a mut Collector, + map: &'a hir::map::Map<'hir> +} - let pushed = current_name.map(|name| self.names.push(name)).is_some(); +impl<'a, 'hir> HirCollector<'a, 'hir> { + fn visit_testable(&mut self, + name: String, + attrs: &[ast::Attribute], + nested: F) { + let has_name = !name.is_empty(); + if has_name { + self.collector.names.push(name); + } - if let Some(doc) = item.doc_value() { - self.cnt = 0; - markdown::find_testable_code(doc, &mut *self); + let mut attrs = Attributes::from_ast(attrs); + attrs.collapse_doc_comments(); + attrs.unindent_doc_comments(); + if let Some(doc) = attrs.doc_value() { + self.collector.cnt = 0; + markdown::find_testable_code(doc, self.collector); } - let ret = self.fold_item_recur(item); - if pushed { - self.names.pop(); + nested(self); + + if has_name { + self.collector.names.pop(); } + } +} + +impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> { + fn nested_visit_map(&mut self) -> Option<&hir::map::Map<'hir>> { + Some(self.map) + } + + fn visit_item(&mut self, item: &'hir hir::Item) { + let name = if let hir::ItemImpl(.., ref ty, _) = item.node { + hir::print::ty_to_string(ty) + } else { + item.name.to_string() + }; - return ret; + self.visit_testable(name, &item.attrs, |this| { + intravisit::walk_item(this, item); + }); + } - // FIXME: it would be better to not have the escaped version in the first place - fn unescape_for_testname(mut s: String) -> String { - // for refs `&foo` - if s.contains("&") { - s = s.replace("&", "&"); + fn visit_trait_item(&mut self, item: &'hir hir::TraitItem) { + self.visit_testable(item.name.to_string(), &item.attrs, |this| { + intravisit::walk_trait_item(this, item); + }); + } - // `::&'a mut Foo::` looks weird, let's make it `::<&'a mut Foo>`:: - if let Some('&') = s.chars().nth(0) { - s = format!("<{}>", s); - } - } + fn visit_impl_item(&mut self, item: &'hir hir::ImplItem) { + self.visit_testable(item.name.to_string(), &item.attrs, |this| { + intravisit::walk_impl_item(this, item); + }); + } - // either `<..>` or `->` - if s.contains(">") { - s.replace(">", ">") - .replace("<", "<") - } else { - s - } - } + fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem) { + self.visit_testable(item.name.to_string(), &item.attrs, |this| { + intravisit::walk_foreign_item(this, item); + }); + } - fn typename_if_impl(item: &clean::Item) -> Option { - if let clean::ItemEnum::ImplItem(ref impl_) = item.inner { - let path = impl_.for_.to_string(); - let unescaped_path = unescape_for_testname(path); - Some(unescaped_path) - } else { - None - } - } + fn visit_variant(&mut self, + v: &'hir hir::Variant, + g: &'hir hir::Generics, + item_id: ast::NodeId) { + self.visit_testable(v.node.name.to_string(), &v.node.attrs, |this| { + intravisit::walk_variant(this, v, g, item_id); + }); + } + + fn visit_struct_field(&mut self, f: &'hir hir::StructField) { + self.visit_testable(f.name.to_string(), &f.attrs, |this| { + intravisit::walk_struct_field(this, f); + }); } } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 939fd6ccfc88e..8ed0567d820ac 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -28,7 +28,7 @@ use rustc::util::nodemap::FxHashSet; use rustc::hir; use core; -use clean::{self, Clean, Attributes}; +use clean::{self, AttributesExt, NestedAttributesExt}; use doctree::*; // looks to me like the first two of these are actually @@ -65,18 +65,13 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } fn stability(&self, id: ast::NodeId) -> Option { - self.cx.tcx_opt().and_then(|tcx| { - self.cx.map.opt_local_def_id(id) - .and_then(|def_id| tcx.lookup_stability(def_id)) - .cloned() - }) + self.cx.tcx.map.opt_local_def_id(id) + .and_then(|def_id| self.cx.tcx.lookup_stability(def_id)).cloned() } fn deprecation(&self, id: ast::NodeId) -> Option { - self.cx.tcx_opt().and_then(|tcx| { - self.cx.map.opt_local_def_id(id) - .and_then(|def_id| tcx.lookup_deprecation(def_id)) - }) + self.cx.tcx.map.opt_local_def_id(id) + .and_then(|def_id| self.cx.tcx.lookup_deprecation(def_id)) } pub fn visit(&mut self, krate: &hir::Crate) { @@ -196,7 +191,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let orig_inside_public_path = self.inside_public_path; self.inside_public_path &= vis == hir::Public; for i in &m.item_ids { - let item = self.cx.map.expect_item(i.id); + let item = self.cx.tcx.map.expect_item(i.id); self.visit_item(item, None, &mut om); } self.inside_public_path = orig_inside_public_path; @@ -279,10 +274,9 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { glob: bool, om: &mut Module, please_inline: bool) -> bool { fn inherits_doc_hidden(cx: &core::DocContext, mut node: ast::NodeId) -> bool { - while let Some(id) = cx.map.get_enclosing_scope(node) { + while let Some(id) = cx.tcx.map.get_enclosing_scope(node) { node = id; - let attrs = cx.map.attrs(node).clean(cx); - if attrs.list("doc").has_word("hidden") { + if cx.tcx.map.attrs(node).lists("doc").has_word("hidden") { return true; } if node == ast::CRATE_NODE_ID { @@ -292,25 +286,22 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { false } - let tcx = match self.cx.tcx_opt() { - Some(tcx) => tcx, - None => return false - }; + let tcx = self.cx.tcx; let def = tcx.expect_def(id); let def_did = def.def_id(); - let use_attrs = tcx.map.attrs(id).clean(self.cx); + let use_attrs = tcx.map.attrs(id); // Don't inline doc(hidden) imports so they can be stripped at a later stage. - let is_no_inline = use_attrs.list("doc").has_word("no_inline") || - use_attrs.list("doc").has_word("hidden"); + let is_no_inline = use_attrs.lists("doc").has_word("no_inline") || + use_attrs.lists("doc").has_word("hidden"); // For cross-crate impl inlining we need to know whether items are // reachable in documentation - a previously nonreachable item can be // made reachable by cross-crate inlining which we're checking here. // (this is done here because we need to know this upfront) if !def_did.is_local() && !is_no_inline { - let attrs = clean::inline::load_attrs(self.cx, tcx, def_did); - let self_is_hidden = attrs.list("doc").has_word("hidden"); + let attrs = clean::inline::load_attrs(self.cx, def_did); + let self_is_hidden = attrs.lists("doc").has_word("hidden"); match def { Def::Trait(did) | Def::Struct(did) | @@ -348,7 +339,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { match it.node { hir::ItemMod(ref m) => { for i in &m.item_ids { - let i = self.cx.map.expect_item(i.id); + let i = self.cx.tcx.map.expect_item(i.id); self.visit_item(i, None, om); } } @@ -507,7 +498,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // regardless of where they're located. if !self.inlining { let items = item_ids.iter() - .map(|ii| self.cx.map.impl_item(ii.id).clone()) + .map(|ii| self.cx.tcx.map.impl_item(ii.id).clone()) .collect(); let i = Impl { unsafety: unsafety, diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs index 6d2830c56192b..cee292f99153e 100644 --- a/src/librustdoc/visit_lib.rs +++ b/src/librustdoc/visit_lib.rs @@ -16,7 +16,7 @@ use rustc::ty::Visibility; use std::cell::RefMut; -use clean::{Attributes, Clean}; +use clean::{AttributesExt, NestedAttributesExt}; // FIXME: this may not be exhaustive, but is sufficient for rustdocs current uses @@ -49,10 +49,7 @@ impl<'a, 'b, 'tcx> LibEmbargoVisitor<'a, 'b, 'tcx> { // Updates node level and returns the updated level fn update(&mut self, did: DefId, level: Option) -> Option { - let attrs: Vec<_> = self.cx.tcx().get_attrs(did).iter() - .map(|a| a.clean(self.cx)) - .collect(); - let is_hidden = attrs.list("doc").has_word("hidden"); + let is_hidden = self.cx.tcx.get_attrs(did).lists("doc").has_word("hidden"); let old_level = self.access_levels.map.get(&did).cloned(); // Accessibility levels can only grow