]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #37890 - eddyb:rustdoc-1, r=nrc
authorbors <bors@rust-lang.org>
Thu, 24 Nov 2016 06:26:44 +0000 (00:26 -0600)
committerGitHub <noreply@github.com>
Thu, 24 Nov 2016 06:26:44 +0000 (00:26 -0600)
rustdoc: separate test collection from the main "clean"-ing pipeline.

While reusing the documentation "clean"-ing infrastructure for collecting code examples to test may have seemed appealing at some point, doing the same through a HIR visitor is barely any harder.
At the same time, supporting both "regular documentation" and "test collection" modes in `rustdoc::clean` has its cost, requiring any use of a `TyCtxt` to be speculative, and provide some sort of fallback.

This simplification is the first step towards bringing rustdoc closer to the compiler, and perhaps even unifying the "local crate" (based on the HIR AST) and "inlinined across crates" (based on crate metadata and typesystem information) implementations of rustdoc.

Sadly, not all possible changes to rustdoc will be uncontroversial, so I'm starting small with this patch.

12 files changed:
src/librustdoc/clean/inline.rs
src/librustdoc/clean/mod.rs
src/librustdoc/clean/simplify.rs
src/librustdoc/core.rs
src/librustdoc/html/render.rs
src/librustdoc/lib.rs
src/librustdoc/passes/collapse_docs.rs
src/librustdoc/passes/strip_hidden.rs
src/librustdoc/passes/unindent_comments.rs
src/librustdoc/test.rs
src/librustdoc/visit_ast.rs
src/librustdoc/visit_lib.rs

index 185f897c1baa1cfb82226cb4d59e3abe8204e382..c5562ae3b7febf5753f6a649a011ccc824847e1c 100644 (file)
@@ -18,7 +18,7 @@
 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;
 /// of a vector of items if it was successfully expanded.
 pub fn try_inline(cx: &DocContext, id: ast::NodeId, into: Option<ast::Name>)
                   -> Option<Vec<clean::Item>> {
-    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<ast::Name>)
     })
 }
 
-fn try_inline_def<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                            def: Def) -> Option<Vec<clean::Item>> {
+fn try_inline_def(cx: &DocContext, def: Def) -> Option<Vec<clean::Item>> {
+    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<clean::Attribute> {
-    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<clean::Item> {
+pub fn build_impls(cx: &DocContext, did: DefId) -> Vec<clean::Item> {
+    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<clean::Item>) {
+pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
     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<clean::Item>) {
+    fn fill_in(cx: &DocContext, did: DefId, items: &mut Vec<clean::Item>) {
         // 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 fill_in<'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
     }
index 2cc1882ce3eae3411adaf179d03132f1127d51af..a19ec4e8b5edbf6bd57d71163b324764f2f7db5b 100644 (file)
@@ -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::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 std::path::PathBuf;
 use std::rc::Rc;
+use std::slice;
 use std::sync::Arc;
 use std::u32;
 use std::env::current_dir;
 
 // extract the stability index for a node from tcx, if possible
 fn get_stability(cx: &DocContext, def_id: DefId) -> Option<Stability> {
-    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<Deprecation> {
-    cx.tcx_opt().and_then(|tcx| tcx.lookup_deprecation(def_id)).clean(cx)
+    cx.tcx.lookup_deprecation(def_id).clean(cx)
 }
 
 pub trait Clean<T> {
@@ -126,20 +125,17 @@ fn clean(&self, cx: &DocContext) -> Crate {
         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 @@ fn clean(&self, cx: &DocContext) -> Crate {
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct ExternalCrate {
     pub name: String,
-    pub attrs: Vec<Attribute>,
+    pub attrs: Attributes,
     pub primitives: Vec<PrimitiveType>,
 }
 
@@ -235,12 +231,10 @@ impl Clean<ExternalCrate> 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<String>,
-    pub attrs: Vec<Attribute>,
+    pub attrs: Attributes,
     pub inner: ItemEnum,
     pub visibility: Option<Visibility>,
     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 @@ fn clean(&self, cx: &DocContext) -> Item {
             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 @@ fn clean(&self, cx: &DocContext) -> Item {
     }
 }
 
-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<Self::Item> {
+        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<Attribute>),
-    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<Item=&'a ast::NestedMetaItem>> 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<String>,
+    pub other_attrs: Vec<ast::Attribute>
 }
 
-impl Clean<Attribute> 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<Attribute> 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<Attribute> for ast::Attribute {
-    fn clean(&self, cx: &DocContext) -> Attribute {
-        self.with_desugared_doc(|a| a.meta().clean(cx))
+impl Clean<Attributes> for [ast::Attribute] {
+    fn clean(&self, _cx: &DocContext) -> Attributes {
+        Attributes::from_ast(self)
     }
 }
 
@@ -554,7 +566,7 @@ impl Clean<TyParam> 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 @@ fn maybe_sized(cx: &DocContext) -> 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<DefId>, has_self: boo
     let lifetimes = substs.regions().filter_map(|v| v.clean(cx)).collect();
     let types = substs.types().skip(has_self as usize).collect::<Vec<_>>();
 
-    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<DefId>, 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<DefId>, has_self
 
 impl Clean<TyParamBound> 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 @@ fn clean(&self, cx: &DocContext) -> TyParamBound {
 
 impl<'tcx> Clean<TyParamBound> 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 @@ pub fn statik() -> Lifetime {
 
 impl Clean<Lifetime> 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 @@ fn clean(&self, cx: &DocContext) -> Method {
             },
             output: self.decl.output.clean(cx),
             variadic: false,
-            attrs: Vec::new()
+            attrs: Attributes::default()
         };
         Method {
             generics: self.generics.clean(cx),
@@ -1076,7 +1077,7 @@ fn clean(&self, cx: &DocContext) -> TyMethod {
             },
             output: self.decl.output.clean(cx),
             variadic: false,
-            attrs: Vec::new()
+            attrs: Attributes::default()
         };
         TyMethod {
             unsafety: self.unsafety.clone(),
@@ -1105,7 +1106,7 @@ fn clean(&self, cx: &DocContext) -> Item {
             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<Attribute>,
+    pub attrs: Attributes,
 }
 
 impl FnDecl {
@@ -1148,7 +1149,7 @@ fn clean(&self, cx: &DocContext) -> FnDecl {
             },
             output: self.output.clean(cx),
             variadic: self.variadic,
-            attrs: Vec::new()
+            attrs: Attributes::default()
         }
     }
 }
@@ -1156,14 +1157,14 @@ fn clean(&self, cx: &DocContext) -> FnDecl {
 impl<'a, 'tcx> Clean<FnDecl> 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 @@ fn clean(&self, cx: &DocContext) -> Item {
             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 @@ fn clean(&self, cx: &DocContext) -> Item {
             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 @@ fn clean(&self, cx: &DocContext) -> Item {
             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<Item> 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 @@ fn clean(&self, cx: &DocContext) -> Item {
                 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 @@ fn clean(&self, cx: &DocContext) -> Item {
                     // 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 @@ fn clean(&self, cx: &DocContext) -> Item {
                 }
 
                 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 @@ fn clean(&self, cx: &DocContext) -> Item {
             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 @@ fn from_str(s: &str) -> Option<PrimitiveType> {
         }
     }
 
-    fn find(attrs: &[Attribute]) -> Option<PrimitiveType> {
-        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<PrimitiveType> {
+        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 @@ fn clean(&self, cx: &DocContext) -> Type {
                              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 @@ fn clean(&self, cx: &DocContext) -> Type {
             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 @@ fn clean(&self, cx: &DocContext) -> Type {
                     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 @@ fn clean(&self, cx: &DocContext) -> Type {
                     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 @@ fn clean(&self, cx: &DocContext) -> Type {
                     });
                 }
 
-                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 @@ fn clean(&self, cx: &DocContext) -> Type {
             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 @@ fn clean(&self, cx: &DocContext) -> Item {
             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<Item> 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 @@ fn clean(&self, cx: &DocContext) -> Item {
             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 @@ fn clean(&self, cx: &DocContext) -> Item {
             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 @@ fn clean(&self, cx: &DocContext) -> Item {
             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 @@ fn clean(&self, cx: &DocContext) -> Item {
             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 @@ fn clean(&self, cx: &DocContext) -> Item {
                         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 @@ fn clean(&self, cx: &DocContext) -> Item {
         };
         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 @@ fn clean(&self, cx: &DocContext) -> Item {
             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 @@ fn clean(&self, cx: &DocContext) -> Item {
             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 @@ fn clean(&self, cx: &DocContext) -> Item {
             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 @@ fn clean(&self, cx: &DocContext) -> Vec<Item> {
 
         // 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 @@ fn clean(&self, cx: &DocContext) -> Vec<Item> {
 fn build_deref_target_impls(cx: &DocContext,
                             items: &[Item],
                             ret: &mut Vec<Item>) {
-    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 @@ fn clean(&self, cx: &DocContext) -> 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: Some(Public),
             stability: None,
             deprecation: None,
@@ -2627,7 +2612,7 @@ fn clean(&self, cx: &DocContext) -> Vec<Item> {
             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 @@ fn clean(&self, cx: &DocContext) -> Item {
             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<DefId> {
-    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 @@ fn clean(&self, cx: &DocContext) -> Item {
             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,
index 19e084905aa92a03f0804fc93a6a7b0aac1238e3..7240f0aedbd2770fd38ab4da16a0fdff138faa41 100644 (file)
@@ -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() {
index a25cb0bacc5cf785610a2bc634d8535868b80882..7d7b7fead5854d9fbd9944dc32f7e904fb6e8d32 100644 (file)
@@ -7,7 +7,6 @@
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
-pub use self::MaybeTyped::*;
 
 use rustc_lint;
 use rustc_driver::{driver, target_features, abort_on_err};
 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<DefId, (Vec<String>, 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<bool>,
-    pub deref_trait_did: Cell<Option<DefId>>,
-    pub deref_mut_trait_did: Cell<Option<DefId>>,
     // 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<TyCtxt<'a, 'tcx, 'tcx>> {
-        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)
         };
 
index 31fbcb5059f82188f25ee6f370e788a8fa3ec827..757db81c4402196fc43e77a6547639117b591b0e 100644 (file)
@@ -53,7 +53,7 @@
 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::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<String> {
+    let name = attr.name();
 
-fn render_attribute(attr: &clean::Attribute, recurse: bool) -> Option<String> {
-    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, "</span>")?;
         write!(w, "</h3>\n")?;
-        if let Some(ref dox) = i.impl_item.attrs.value("doc") {
+        if let Some(ref dox) = i.impl_item.doc_value() {
             write!(w, "<div class='docblock'>{}</div>", Markdown(dox))?;
         }
     }
index 3af7c20c1336e91f1941e0ceb50c449b246822ff..60ce7ea53953a5c5dbdef176dd55d0b4d3408785 100644 (file)
@@ -89,7 +89,7 @@ pub mod html {
 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<Output, String> {
+fn acquire_input<R, F>(input: &str,
+                       externs: Externs,
+                       matches: &getopts::Matches,
+                       f: F)
+                       -> Result<R, String>
+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<Externs, String> {
 /// 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<R, F>(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()
 }
index c034ef9326846c349b7761606918c72bd1fad35d..3c63302127c5eef17b7df6e4818d3851d1f9db0d 100644 (file)
@@ -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<Item> {
-        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<clean::Attribute> = 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];
+        }
+    }
+}
index 927ccf917199978221c2488262aa97e838869e03..68c1231fc6f7c58e7266ee3e3c52708fa36e7b68 100644 (file)
@@ -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<Item> {
-        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 {
index 20640f3f88518f98d2c8f7269a9377a9f698559e..4d94c30847852f42ce5393049f4b3ee22d6cecea 100644 (file)
 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<Item> {
-        let mut avec: Vec<clean::Attribute> = 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::<Vec<&str> >();
     let mut saw_first_line = false;
index 12d33dcb207f7c78ab170e5cbb69439a8b27b098..9f29319430dd5f4688444904fa55295170f32ed8 100644 (file)
@@ -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 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};
 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 @@ pub fn register_header(&mut self, name: &str, level: u32) {
     }
 }
 
-impl DocFolder for Collector {
-    fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
-        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<F: FnOnce(&mut Self)>(&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("&amp;") {
-                s = s.replace("&amp;", "&");
+    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("&gt;") {
-                s.replace("&gt;", ">")
-                 .replace("&lt;", "<")
-            } 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<String> {
-            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);
+        });
     }
 }
index 939fd6ccfc88e0e6f337d4fe04a4cc03d867f31e..8ed0567d820ac4af27ea5df04f26f3700374e3ee 100644 (file)
@@ -28,7 +28,7 @@
 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 @@ pub fn new(cx: &'a core::DocContext<'a, 'tcx>) -> RustdocVisitor<'a, 'tcx> {
     }
 
     fn stability(&self, id: ast::NodeId) -> Option<attr::Stability> {
-        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<attr::Deprecation> {
-        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 @@ pub fn visit_mod_contents(&mut self, span: Span, attrs: hir::HirVec<ast::Attribu
         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 @@ fn maybe_inline_local(&mut self, id: ast::NodeId, renamed: Option<ast::Name>,
                   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 @@ fn inherits_doc_hidden(cx: &core::DocContext, mut node: ast::NodeId) -> bool {
             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 @@ fn inherits_doc_hidden(cx: &core::DocContext, mut node: ast::NodeId) -> bool {
                     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 @@ pub fn visit_item(&mut self, item: &hir::Item,
                 // 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,
index 6d2830c56192bb434b73485384b6a9681d8d7d8b..cee292f99153e4fc318463094d1633f8ac8e6074 100644 (file)
@@ -16,7 +16,7 @@
 
 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 @@ pub fn visit_lib(&mut self, cnum: CrateNum) {
 
     // Updates node level and returns the updated level
     fn update(&mut self, did: DefId, level: Option<AccessLevel>) -> Option<AccessLevel> {
-        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