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.
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() => {
})
}
-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.
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),
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.
/// 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);
}
}
-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 {
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
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.
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
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),
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),
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 {
});
}
-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)
}
}
}
}
-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);
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
}
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::*;
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};
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> {
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));
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct ExternalCrate {
pub name: String,
- pub attrs: Vec<Attribute>,
+ pub attrs: Attributes,
pub primitives: Vec<PrimitiveType>,
}
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),
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,
/// 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 {
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
}
}
-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)
}
}
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),
}
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
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(),
output: output
}
},
- (..) => {
+ _ => {
PathParameters::AngleBracketed {
lifetimes: lifetimes,
types: types.clean(cx),
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 =>
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);
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())
}
},
output: self.decl.output.clean(cx),
variadic: false,
- attrs: Vec::new()
+ attrs: Attributes::default()
};
Method {
generics: self.generics.clean(cx),
},
output: self.decl.output.clean(cx),
variadic: false,
- attrs: Vec::new()
+ attrs: Attributes::default()
};
TyMethod {
unsafety: self.unsafety.clone(),
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),
pub inputs: Arguments,
pub output: FunctionRetTy,
pub variadic: bool,
- pub attrs: Vec<Attribute>,
+ pub attrs: Attributes,
}
impl FnDecl {
},
output: self.output.clean(cx),
variadic: self.variadic,
- attrs: Vec::new()
+ attrs: Attributes::default()
}
}
}
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| {
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),
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
}
}
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
}
}
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!()
};
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 {
// 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 {
}
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
};
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,
}
}
}
- 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;
}
}
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));
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)),
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) => {
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,
});
}
- 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,
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())
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)),
}
}
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),
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),
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),
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),
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),
}),
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),
};
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,
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),
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),
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),
// 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),
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 {
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() {
};
if let Some(did) = did {
if !did.is_local() {
- inline::build_impl(cx, tcx, did, ret);
+ inline::build_impl(cx, did, ret);
}
}
}
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,
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,
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,
}
}
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 {
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),
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
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
}
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)]
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,
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() {
// <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.
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
};
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(),
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)
};
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;
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;
// 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;
}
_ => {}
// 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> {
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));
}
}
}
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))?;
}
}
pub mod visit_lib;
pub mod test;
-use clean::Attributes;
+use clean::AttributesExt;
struct Output {
krate: clean::Crate,
!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))
}
}
/// 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");
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");
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()
}
// 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];
+ }
+ }
+}
use rustc::util::nodemap::DefIdSet;
use std::mem;
-use clean::{self, Attributes};
+use clean::{self, AttributesExt, NestedAttributesExt};
use clean::Item;
use plugins;
use fold;
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 {
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;
// 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::*;
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 {
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());
}
}
-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("&") {
- s = s.replace("&", "&");
+ fn visit_trait_item(&mut self, item: &'hir hir::TraitItem) {
+ self.visit_testable(item.name.to_string(), &item.attrs, |this| {
+ intravisit::walk_trait_item(this, item);
+ });
+ }
- // `::&'a mut Foo::` looks weird, let's make it `::<&'a mut Foo>`::
- if let Some('&') = s.chars().nth(0) {
- s = format!("<{}>", s);
- }
- }
+ fn visit_impl_item(&mut self, item: &'hir hir::ImplItem) {
+ self.visit_testable(item.name.to_string(), &item.attrs, |this| {
+ intravisit::walk_impl_item(this, item);
+ });
+ }
- // either `<..>` or `->`
- if s.contains(">") {
- s.replace(">", ">")
- .replace("<", "<")
- } else {
- s
- }
- }
+ fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem) {
+ self.visit_testable(item.name.to_string(), &item.attrs, |this| {
+ intravisit::walk_foreign_item(this, item);
+ });
+ }
- fn typename_if_impl(item: &clean::Item) -> Option<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);
+ });
}
}
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
}
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) {
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;
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 {
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) |
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);
}
}
// 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,
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
// 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