auto merge of #14391 : alexcrichton/rust/more-rustdoc-inline, r=huonw

As part of the libstd facade (cc #13851), rustdoc is taught to inline documentation across crate boundaries through the usage of a `pub use` statement. This is done to allow libstd to maintain the facade that it is a standalone library with a defined public interface (allowing us to shuffle around what's underneath it).

A preview is available at http://people.mozilla.org/~acrichton/doc/std/index.html
This commit is contained in:
bors 2014-05-25 13:56:21 -07:00
commit 0fca6c6a02
14 changed files with 734 additions and 177 deletions

View file

@ -41,6 +41,31 @@ pub fn recalibrate() {
# }
~~~
Documentation can also be controlled via the `doc` attribute on items. This is
implicitly done by the compiler when using the above form of doc comments
(converting the slash-based comments to `#[doc]` attributes).
~~~
#[doc = "
Calculates the factorial of a number.
Given the input integer `n`, this function will calculate `n!` and return it.
"]
pub fn factorial(n: int) -> int { if n < 2 {1} else {n * factorial(n)} }
# fn main() {}
~~~
The `doc` attribute can also be used to control how rustdoc emits documentation
in some cases.
```
// Rustdoc will inline documentation of a `pub use` into this crate when the
// `pub use` reaches across crates, but this behavior can also be disabled.
#[doc(no_inline)]
pub use std::option::Option;
# fn main() {}
```
Doc comments are markdown, and are currently parsed with the
[sundown][sundown] library. rustdoc does not yet do any fanciness such as
referencing other items inline, like javadoc's `@see`. One exception to this

View file

@ -206,6 +206,9 @@ pub static tag_crate_triple: uint = 0x66;
pub static tag_dylib_dependency_formats: uint = 0x67;
pub static tag_method_argument_names: uint = 0x8e;
pub static tag_method_argument_name: uint = 0x8f;
#[deriving(Clone, Show)]
pub struct LinkMeta {
pub crateid: CrateId,

View file

@ -306,3 +306,10 @@ pub fn get_missing_lang_items(cstore: &cstore::CStore, cnum: ast::CrateNum)
let cdata = cstore.get_crate_data(cnum);
decoder::get_missing_lang_items(&*cdata)
}
pub fn get_method_arg_names(cstore: &cstore::CStore, did: ast::DefId)
-> Vec<String>
{
let cdata = cstore.get_crate_data(did.krate);
decoder::get_method_arg_names(&*cdata, did.node)
}

View file

@ -1309,3 +1309,18 @@ pub fn get_missing_lang_items(cdata: Cmd)
});
return result;
}
pub fn get_method_arg_names(cdata: Cmd, id: ast::NodeId) -> Vec<String> {
let mut ret = Vec::new();
let method_doc = lookup_item(id, cdata.data());
match reader::maybe_get_doc(method_doc, tag_method_argument_names) {
Some(args_doc) => {
reader::tagged_docs(args_doc, tag_method_argument_name, |name_doc| {
ret.push(name_doc.as_str_slice().to_strbuf());
true
});
}
None => {}
}
return ret;
}

View file

@ -536,6 +536,7 @@ fn encode_reexports(ecx: &EncodeContext,
fn encode_info_for_mod(ecx: &EncodeContext,
ebml_w: &mut Encoder,
md: &Mod,
attrs: &[Attribute],
id: NodeId,
path: PathElems,
name: Ident,
@ -584,6 +585,7 @@ fn encode_info_for_mod(ecx: &EncodeContext,
debug!("(encoding info for module) encoding reexports for {}", id);
encode_reexports(ecx, ebml_w, id, path);
}
encode_attributes(ebml_w, attrs);
ebml_w.end_tag();
}
@ -774,11 +776,30 @@ fn encode_info_for_method(ecx: &EncodeContext,
} else {
encode_symbol(ecx, ebml_w, m.def_id.node);
}
encode_method_argument_names(ebml_w, &*ast_method.decl);
}
ebml_w.end_tag();
}
fn encode_method_argument_names(ebml_w: &mut Encoder,
decl: &ast::FnDecl) {
ebml_w.start_tag(tag_method_argument_names);
for arg in decl.inputs.iter() {
ebml_w.start_tag(tag_method_argument_name);
match arg.pat.node {
ast::PatIdent(_, ref name, _) => {
let name = name.segments.last().unwrap().identifier;
let name = token::get_ident(name);
ebml_w.writer.write(name.get().as_bytes());
}
_ => {}
}
ebml_w.end_tag();
}
ebml_w.end_tag();
}
fn encode_inlined_item(ecx: &EncodeContext,
ebml_w: &mut Encoder,
ii: InlinedItemRef) {
@ -895,7 +916,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
encode_visibility(ebml_w, vis);
ebml_w.end_tag();
}
ItemFn(_, fn_style, _, ref generics, _) => {
ItemFn(ref decl, fn_style, _, ref generics, _) => {
add_to_index(item, ebml_w, index);
ebml_w.start_tag(tag_items_data_item);
encode_def_id(ebml_w, def_id);
@ -911,6 +932,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
encode_symbol(ecx, ebml_w, item.id);
}
encode_visibility(ebml_w, vis);
encode_method_argument_names(ebml_w, &**decl);
ebml_w.end_tag();
}
ItemMod(ref m) => {
@ -918,6 +940,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
encode_info_for_mod(ecx,
ebml_w,
m,
item.attrs.as_slice(),
item.id,
path,
item.ident,
@ -1317,6 +1340,7 @@ fn encode_info_for_items(ecx: &EncodeContext,
encode_info_for_mod(ecx,
ebml_w,
&krate.module,
&[],
CRATE_NODE_ID,
ast_map::Values([].iter()).chain(None),
syntax::parse::token::special_idents::invalid,

View file

@ -0,0 +1,278 @@
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Support for inlining external documentation into the current AST.
use syntax::ast;
use syntax::ast_util;
use syntax::attr::AttrMetaMethods;
use rustc::metadata::csearch;
use rustc::metadata::decoder;
use rustc::middle::ty;
use core;
use doctree;
use clean;
use super::Clean;
/// Attempt to inline the definition of a local node id into this AST.
///
/// This function will fetch the definition of the id specified, and if it is
/// from another crate it will attempt to inline the documentation from the
/// other crate into this crate.
///
/// This is primarily used for `pub use` statements which are, in general,
/// implementation details. Inlining the documentation should help provide a
/// better experience when reading the documentation in this use case.
///
/// The returned value is `None` if the `id` could not be inlined, and `Some`
/// of a vector of items if it was successfully expanded.
pub fn try_inline(id: ast::NodeId) -> Option<Vec<clean::Item>> {
let cx = ::ctxtkey.get().unwrap();
let tcx = match cx.maybe_typed {
core::Typed(ref tycx) => tycx,
core::NotTyped(_) => return None,
};
let def = match tcx.def_map.borrow().find(&id) {
Some(def) => *def,
None => return None,
};
let did = ast_util::def_id_of_def(def);
if ast_util::is_local(did) { return None }
try_inline_def(&**cx, tcx, def)
}
fn try_inline_def(cx: &core::DocContext,
tcx: &ty::ctxt,
def: ast::Def) -> Option<Vec<clean::Item>> {
let mut ret = Vec::new();
let did = ast_util::def_id_of_def(def);
let inner = match def {
ast::DefTrait(did) => {
record_extern_fqn(cx, did, clean::TypeTrait);
clean::TraitItem(build_external_trait(tcx, did))
}
ast::DefFn(did, style) => {
record_extern_fqn(cx, did, clean::TypeFunction);
clean::FunctionItem(build_external_function(tcx, did, style))
}
ast::DefStruct(did) => {
record_extern_fqn(cx, did, clean::TypeStruct);
ret.extend(build_impls(tcx, did).move_iter());
clean::StructItem(build_struct(tcx, did))
}
ast::DefTy(did) => {
record_extern_fqn(cx, did, clean::TypeEnum);
ret.extend(build_impls(tcx, did).move_iter());
build_type(tcx, did)
}
// Assume that the enum type is reexported next to the variant, and
// variants don't show up in documentation specially.
ast::DefVariant(..) => return Some(Vec::new()),
ast::DefMod(did) => {
record_extern_fqn(cx, did, clean::TypeModule);
clean::ModuleItem(build_module(cx, tcx, did))
}
_ => return None,
};
let fqn = csearch::get_item_path(tcx, did);
ret.push(clean::Item {
source: clean::Span::empty(),
name: Some(fqn.last().unwrap().to_str().to_strbuf()),
attrs: load_attrs(tcx, did),
inner: inner,
visibility: Some(ast::Public),
def_id: did,
});
Some(ret)
}
pub fn load_attrs(tcx: &ty::ctxt, did: ast::DefId) -> Vec<clean::Attribute> {
let mut attrs = Vec::new();
csearch::get_item_attrs(&tcx.sess.cstore, did, |v| {
attrs.extend(v.move_iter().map(|mut a| {
// FIXME this isn't quite always true, it's just true about 99% of
// the time when dealing with documentation. For example,
// this would treat doc comments of the form `#[doc = "foo"]`
// incorrectly.
if a.name().get() == "doc" && a.value_str().is_some() {
a.node.is_sugared_doc = true;
}
a.clean()
}));
});
attrs
}
/// 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: &core::DocContext,
did: ast::DefId,
kind: clean::TypeKind) {
match cx.maybe_typed {
core::Typed(ref tcx) => {
let fqn = csearch::get_item_path(tcx, did);
let fqn = fqn.move_iter().map(|i| i.to_str().to_strbuf()).collect();
cx.external_paths.borrow_mut().get_mut_ref().insert(did, (fqn, kind));
}
core::NotTyped(..) => {}
}
}
pub fn build_external_trait(tcx: &ty::ctxt, did: ast::DefId) -> clean::Trait {
let def = ty::lookup_trait_def(tcx, did);
let methods = ty::trait_methods(tcx, did);
clean::Trait {
generics: def.generics.clean(),
methods: methods.iter().map(|i| i.clean()).collect(),
parents: Vec::new(), // FIXME: this is likely wrong
}
}
fn build_external_function(tcx: &ty::ctxt,
did: ast::DefId,
style: ast::FnStyle) -> clean::Function {
let t = ty::lookup_item_type(tcx, did);
clean::Function {
decl: match ty::get(t.ty).sty {
ty::ty_bare_fn(ref f) => (did, &f.sig).clean(),
_ => fail!("bad function"),
},
generics: t.generics.clean(),
fn_style: style,
}
}
fn build_struct(tcx: &ty::ctxt, did: ast::DefId) -> clean::Struct {
use syntax::parse::token::special_idents::unnamed_field;
let t = ty::lookup_item_type(tcx, did);
let fields = ty::lookup_struct_fields(tcx, did);
clean::Struct {
struct_type: match fields.as_slice() {
[] => doctree::Unit,
[ref f] if f.name == unnamed_field.name => doctree::Newtype,
[ref f, ..] if f.name == unnamed_field.name => doctree::Tuple,
_ => doctree::Plain,
},
generics: t.generics.clean(),
fields: fields.iter().map(|f| f.clean()).collect(),
fields_stripped: false,
}
}
fn build_type(tcx: &ty::ctxt, did: ast::DefId) -> clean::ItemEnum {
let t = ty::lookup_item_type(tcx, did);
match ty::get(t.ty).sty {
ty::ty_enum(edid, _) => {
return clean::EnumItem(clean::Enum {
generics: t.generics.clean(),
variants_stripped: false,
variants: ty::enum_variants(tcx, edid).clean(),
})
}
_ => {}
}
clean::TypedefItem(clean::Typedef {
type_: t.ty.clean(),
generics: t.generics.clean(),
})
}
fn build_impls(tcx: &ty::ctxt,
did: ast::DefId) -> Vec<clean::Item> {
ty::populate_implementations_for_type_if_necessary(tcx, did);
let mut impls = Vec::new();
match tcx.inherent_impls.borrow().find(&did) {
None => {}
Some(i) => {
impls.extend(i.borrow().iter().map(|&did| { build_impl(tcx, did) }));
}
}
impls
}
fn build_impl(tcx: &ty::ctxt, did: ast::DefId) -> clean::Item {
let associated_trait = csearch::get_impl_trait(tcx, did);
let attrs = load_attrs(tcx, did);
let ty = ty::lookup_item_type(tcx, did);
let methods = csearch::get_impl_methods(&tcx.sess.cstore, did).iter().map(|did| {
let mut item = match ty::method(tcx, *did).clean() {
clean::Provided(item) => item,
clean::Required(item) => item,
};
item.inner = match item.inner.clone() {
clean::TyMethodItem(clean::TyMethod {
fn_style, decl, self_, generics
}) => {
clean::MethodItem(clean::Method {
fn_style: fn_style,
decl: decl,
self_: self_,
generics: generics,
})
}
_ => fail!("not a tymethod"),
};
item
}).collect();
clean::Item {
inner: clean::ImplItem(clean::Impl {
derived: clean::detect_derived(attrs.as_slice()),
trait_: associated_trait.clean().map(|bound| {
match bound {
clean::TraitBound(ty) => ty,
clean::RegionBound => unreachable!(),
}
}),
for_: ty.ty.clean(),
generics: ty.generics.clean(),
methods: methods,
}),
source: clean::Span::empty(),
name: None,
attrs: attrs,
visibility: Some(ast::Inherited),
def_id: did,
}
}
fn build_module(cx: &core::DocContext, tcx: &ty::ctxt,
did: ast::DefId) -> clean::Module {
let mut items = Vec::new();
// FIXME: this doesn't handle reexports inside the module itself.
// Should they be handled?
csearch::each_child_of_item(&tcx.sess.cstore, did, |def, _, _| {
match def {
decoder::DlDef(def) => {
match try_inline_def(cx, tcx, def) {
Some(i) => items.extend(i.move_iter()),
None => {}
}
}
decoder::DlImpl(did) => items.push(build_impl(tcx, did)),
decoder::DlField => fail!("unimplemented field"),
}
});
clean::Module {
items: items,
is_crate: false,
}
}

View file

@ -15,7 +15,7 @@ use syntax;
use syntax::ast;
use syntax::ast_util;
use syntax::attr;
use syntax::attr::AttributeMethods;
use syntax::attr::{AttributeMethods, AttrMetaMethods};
use syntax::codemap::Pos;
use syntax::parse::token::InternedString;
use syntax::parse::token;
@ -27,7 +27,7 @@ use rustc::metadata::csearch;
use rustc::metadata::decoder;
use rustc::middle::ty;
use std::string::String;
use std::rc::Rc;
use core;
use doctree;
@ -37,6 +37,8 @@ use visit_ast;
/// Increment this when the `Crate` and related structures change.
pub static SCHEMA_VERSION: &'static str = "0.8.2";
mod inline;
pub trait Clean<T> {
fn clean(&self) -> T;
}
@ -53,6 +55,12 @@ impl<T: Clean<U>, U> Clean<U> for @T {
}
}
impl<T: Clean<U>, U> Clean<U> for Rc<T> {
fn clean(&self) -> U {
(**self).clean()
}
}
impl<T: Clean<U>, U> Clean<Option<U>> for Option<T> {
fn clean(&self) -> Option<U> {
match self {
@ -250,7 +258,8 @@ impl Clean<Item> for doctree::Module {
self.statics.clean().move_iter().collect(),
self.traits.clean().move_iter().collect(),
self.impls.clean().move_iter().collect(),
self.view_items.clean().move_iter().collect(),
self.view_items.clean().move_iter()
.flat_map(|s| s.move_iter()).collect(),
self.macros.clean().move_iter().collect()
);
@ -336,6 +345,14 @@ impl attr::AttrMetaMethods for Attribute {
None
}
}
impl<'a> attr::AttrMetaMethods for &'a Attribute {
fn name(&self) -> InternedString { (**self).name() }
fn value_str(&self) -> Option<InternedString> { (**self).value_str() }
fn meta_item_list<'a>(&'a self) -> Option<&'a [@ast::MetaItem]> { None }
fn name_str_pair(&self) -> Option<(InternedString, InternedString)> {
None
}
}
#[deriving(Clone, Encodable, Decodable)]
pub struct TyParam {
@ -473,7 +490,7 @@ impl Clean<Option<Vec<TyParamBound>>> for ty::substs {
}
}
#[deriving(Clone, Encodable, Decodable)]
#[deriving(Clone, Encodable, Decodable, Eq)]
pub struct Lifetime(String);
impl Lifetime {
@ -614,7 +631,7 @@ impl Clean<Item> for ast::TypeMethod {
}
}
#[deriving(Clone, Encodable, Decodable)]
#[deriving(Clone, Encodable, Decodable, Eq)]
pub enum SelfTy {
SelfStatic,
SelfValue,
@ -707,18 +724,32 @@ impl Clean<FnDecl> for ast::FnDecl {
}
}
impl Clean<FnDecl> for ty::FnSig {
impl<'a> Clean<FnDecl> for (ast::DefId, &'a ty::FnSig) {
fn clean(&self) -> FnDecl {
let cx = super::ctxtkey.get().unwrap();
let tcx = match cx.maybe_typed {
core::Typed(ref tcx) => tcx,
core::NotTyped(_) => unreachable!(),
};
let (did, sig) = *self;
let mut names = if did.node != 0 {
csearch::get_method_arg_names(&tcx.sess.cstore, did).move_iter()
} else {
Vec::new().move_iter()
}.peekable();
if names.peek().map(|s| s.as_slice()) == Some("self") {
let _ = names.next();
}
FnDecl {
output: self.output.clean(),
output: sig.output.clean(),
cf: Return,
attrs: Vec::new(), // FIXME: this is likely wrong
attrs: Vec::new(),
inputs: Arguments {
values: self.inputs.iter().map(|t| {
values: sig.inputs.iter().map(|t| {
Argument {
type_: t.clean(),
id: 0,
name: "".to_strbuf(), // FIXME: where are the names?
name: names.next().unwrap_or("".to_strbuf()),
}
}).collect(),
},
@ -830,12 +861,8 @@ impl Clean<TraitMethod> for ty::Method {
let cx = super::ctxtkey.get().unwrap();
let tcx = match cx.maybe_typed {
core::Typed(ref tcx) => tcx,
core::NotTyped(_) => fail!(),
core::NotTyped(_) => unreachable!(),
};
let mut attrs = Vec::new();
csearch::get_item_attrs(&tcx.sess.cstore, self.def_id, |v| {
attrs.extend(v.move_iter().map(|i| i.clean()));
});
let (self_, sig) = match self.explicit_self {
ast::SelfStatic => (ast::SelfStatic.clean(), self.fty.sig.clone()),
s => {
@ -857,20 +884,18 @@ impl Clean<TraitMethod> for ty::Method {
(s, sig)
}
};
m(Item {
name: Some(self.ident.clean()),
visibility: Some(ast::Inherited),
def_id: self.def_id,
attrs: attrs,
source: Span {
filename: "".to_strbuf(),
loline: 0, locol: 0, hiline: 0, hicol: 0,
},
attrs: inline::load_attrs(tcx, self.def_id),
source: Span::empty(),
inner: TyMethodItem(TyMethod {
fn_style: self.fty.fn_style,
generics: self.generics.clean(),
self_: self_,
decl: sig.clean(),
decl: (self.def_id, &sig).clean(),
})
})
}
@ -987,13 +1012,13 @@ impl Clean<Type> for ty::t {
generics: Generics {
lifetimes: Vec::new(), type_params: Vec::new()
},
decl: fty.sig.clean(),
decl: (ast_util::local_def(0), &fty.sig).clean(),
abi: fty.abi.to_str().to_strbuf(),
}),
ty::ty_closure(ref fty) => {
let decl = box ClosureDecl {
lifetimes: Vec::new(), // FIXME: this looks wrong...
decl: fty.sig.clean(),
decl: (ast_util::local_def(0), &fty.sig).clean(),
onceness: fty.onceness,
fn_style: fty.fn_style,
bounds: fty.bounds.iter().map(|i| i.clean()).collect(),
@ -1009,7 +1034,7 @@ impl Clean<Type> for ty::t {
let cx = super::ctxtkey.get().unwrap();
let tcx = match cx.maybe_typed {
core::Typed(ref tycx) => tycx,
core::NotTyped(_) => fail!(),
core::NotTyped(_) => unreachable!(),
};
let fqn = csearch::get_item_path(tcx, did);
let fqn: Vec<String> = fqn.move_iter().map(|i| {
@ -1073,6 +1098,31 @@ impl Clean<Item> for ast::StructField {
}
}
impl Clean<Item> for ty::field_ty {
fn clean(&self) -> Item {
use syntax::parse::token::special_idents::unnamed_field;
let name = if self.name == unnamed_field.name {
None
} else {
Some(self.name)
};
let cx = super::ctxtkey.get().unwrap();
let tcx = match cx.maybe_typed {
core::Typed(ref tycx) => tycx,
core::NotTyped(_) => unreachable!(),
};
let ty = ty::lookup_item_type(tcx, self.id);
Item {
name: name.clean(),
attrs: inline::load_attrs(tcx, self.id),
source: Span::empty(),
visibility: Some(self.vis),
def_id: self.id,
inner: StructFieldItem(TypedStructField(ty.ty.clean())),
}
}
}
pub type Visibility = ast::Visibility;
impl Clean<Option<Visibility>> for ast::Visibility {
@ -1171,6 +1221,53 @@ impl Clean<Item> for doctree::Variant {
}
}
impl Clean<Item> for ty::VariantInfo {
fn clean(&self) -> Item {
// use syntax::parse::token::special_idents::unnamed_field;
let cx = super::ctxtkey.get().unwrap();
let tcx = match cx.maybe_typed {
core::Typed(ref tycx) => tycx,
core::NotTyped(_) => fail!("tcx not present"),
};
let kind = match self.arg_names.as_ref().map(|s| s.as_slice()) {
None | Some([]) if self.args.len() == 0 => CLikeVariant,
None | Some([]) => {
TupleVariant(self.args.iter().map(|t| t.clean()).collect())
}
Some(s) => {
StructVariant(VariantStruct {
struct_type: doctree::Plain,
fields_stripped: false,
fields: s.iter().zip(self.args.iter()).map(|(name, ty)| {
Item {
source: Span::empty(),
name: Some(name.clean()),
attrs: Vec::new(),
visibility: Some(ast::Public),
// FIXME: this is not accurate, we need an id for
// the specific field but we're using the id
// for the whole variant. Nothing currently
// uses this so we should be good for now.
def_id: self.id,
inner: StructFieldItem(
TypedStructField(ty.clean())
)
}
}).collect()
})
}
};
Item {
name: Some(self.name.clean()),
attrs: inline::load_attrs(tcx, self.id),
source: Span::empty(),
visibility: Some(ast::Public),
def_id: self.id,
inner: VariantItem(Variant { kind: kind }),
}
}
}
#[deriving(Clone, Encodable, Decodable)]
pub enum VariantKind {
CLikeVariant,
@ -1202,6 +1299,16 @@ pub struct Span {
pub hicol: uint,
}
impl Span {
fn empty() -> Span {
Span {
filename: "".to_strbuf(),
loline: 0, locol: 0,
hiline: 0, hicol: 0,
}
}
}
impl Clean<Span> for syntax::codemap::Span {
fn clean(&self) -> Span {
let ctxt = super::ctxtkey.get().unwrap();
@ -1273,6 +1380,12 @@ impl Clean<String> for ast::Ident {
}
}
impl Clean<String> for ast::Name {
fn clean(&self) -> String {
token::get_name(*self).get().to_strbuf()
}
}
#[deriving(Clone, Encodable, Decodable)]
pub struct Typedef {
pub type_: Type,
@ -1345,7 +1458,7 @@ impl Clean<Item> for doctree::Static {
}
}
#[deriving(Show, Clone, Encodable, Decodable)]
#[deriving(Show, Clone, Encodable, Decodable, Eq)]
pub enum Mutability {
Mutable,
Immutable,
@ -1369,19 +1482,12 @@ pub struct Impl {
pub derived: bool,
}
fn detect_derived<M: AttrMetaMethods>(attrs: &[M]) -> bool {
attr::contains_name(attrs, "automatically_derived")
}
impl Clean<Item> for doctree::Impl {
fn clean(&self) -> Item {
let mut derived = false;
for attr in self.attrs.iter() {
match attr.node.value.node {
ast::MetaWord(ref s) => {
if s.get() == "automatically_derived" {
derived = true;
}
}
_ => {}
}
}
Item {
name: None,
attrs: self.attrs.clean(),
@ -1393,7 +1499,7 @@ impl Clean<Item> for doctree::Impl {
trait_: self.trait_.clean(),
for_: self.for_.clean(),
methods: self.methods.clean(),
derived: derived,
derived: detect_derived(self.attrs.as_slice()),
}),
}
}
@ -1404,18 +1510,63 @@ pub struct ViewItem {
pub inner: ViewItemInner,
}
impl Clean<Item> for ast::ViewItem {
fn clean(&self) -> Item {
Item {
name: None,
attrs: self.attrs.clean().move_iter().collect(),
source: self.span.clean(),
def_id: ast_util::local_def(0),
visibility: self.vis.clean(),
inner: ViewItemItem(ViewItem {
inner: self.node.clean()
}),
impl Clean<Vec<Item>> for ast::ViewItem {
fn clean(&self) -> Vec<Item> {
// We consider inlining the documentation of `pub use` statments, but we
// forcefully don't inline if this is not public or if the
// #[doc(no_inline)] attribute is present.
let denied = self.vis != ast::Public || self.attrs.iter().any(|a| {
a.name().get() == "doc" && match a.meta_item_list() {
Some(l) => attr::contains_name(l, "no_inline"),
None => false,
}
});
let convert = |node: &ast::ViewItem_| {
Item {
name: None,
attrs: self.attrs.clean().move_iter().collect(),
source: self.span.clean(),
def_id: ast_util::local_def(0),
visibility: self.vis.clean(),
inner: ViewItemItem(ViewItem { inner: node.clean() }),
}
};
let mut ret = Vec::new();
match self.node {
ast::ViewItemUse(ref path) if !denied => {
match path.node {
ast::ViewPathGlob(..) => ret.push(convert(&self.node)),
ast::ViewPathList(ref a, ref list, ref b) => {
// Attempt to inline all reexported items, but be sure
// to keep any non-inlineable reexports so they can be
// listed in the documentation.
let remaining = list.iter().filter(|path| {
match inline::try_inline(path.node.id) {
Some(items) => {
ret.extend(items.move_iter()); false
}
None => true,
}
}).map(|a| a.clone()).collect::<Vec<ast::PathListIdent>>();
if remaining.len() > 0 {
let path = ast::ViewPathList(a.clone(),
remaining,
b.clone());
let path = syntax::codemap::dummy_spanned(path);
ret.push(convert(&ast::ViewItemUse(@path)));
}
}
ast::ViewPathSimple(_, _, id) => {
match inline::try_inline(id) {
Some(items) => ret.extend(items.move_iter()),
None => ret.push(convert(&self.node)),
}
}
}
}
ref n => ret.push(convert(n)),
}
return ret;
}
}
@ -1630,13 +1781,10 @@ fn register_def(cx: &core::DocContext, def: ast::Def) -> ast::DefId {
core::Typed(ref t) => t,
core::NotTyped(_) => return did
};
let fqn = csearch::get_item_path(tcx, did);
let fqn = fqn.move_iter().map(|i| i.to_str().to_strbuf()).collect();
debug!("recording {} => {}", did, fqn);
cx.external_paths.borrow_mut().get_mut_ref().insert(did, (fqn, kind));
inline::record_extern_fqn(cx, did, kind);
match kind {
TypeTrait => {
let t = build_external_trait(tcx, did);
let t = inline::build_external_trait(tcx, did);
cx.external_traits.borrow_mut().get_mut_ref().insert(did, t);
}
_ => {}
@ -1644,16 +1792,6 @@ fn register_def(cx: &core::DocContext, def: ast::Def) -> ast::DefId {
return did;
}
fn build_external_trait(tcx: &ty::ctxt, did: ast::DefId) -> Trait {
let def = csearch::get_trait_def(tcx, did);
let methods = ty::trait_methods(tcx, did);
Trait {
generics: def.generics.clean(),
methods: methods.iter().map(|i| i.clean()).collect(),
parents: Vec::new(), // FIXME: this is likely wrong
}
}
fn resolve_use_source(path: Path, id: ast::NodeId) -> ImportSource {
ImportSource {
path: path,

View file

@ -99,7 +99,7 @@ impl fmt::Show for clean::TyParamBound {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
clean::RegionBound => {
f.write("::".as_bytes())
f.write("'static".as_bytes())
}
clean::TraitBound(ref ty) => {
write!(f, "{}", *ty)
@ -150,7 +150,7 @@ fn resolved_path(w: &mut fmt::Formatter, did: ast::DefId, p: &clean::Path,
print_all: bool) -> fmt::Result {
path(w, p, print_all,
|cache, loc| {
if ast_util::is_local(did) {
if ast_util::is_local(did) || cache.paths.contains_key(&did) {
Some(("../".repeat(loc.len())).to_strbuf())
} else {
match *cache.extern_locations.get(&did.krate) {

View file

@ -144,6 +144,8 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
extern fn block(ob: *mut hoedown_buffer, text: *hoedown_buffer,
lang: *hoedown_buffer, opaque: *mut libc::c_void) {
unsafe {
if text.is_null() { return }
let opaque = opaque as *mut hoedown_html_renderer_state;
let my_opaque: &MyOpaque = &*((*opaque).opaque as *MyOpaque);
slice::raw::buf_as_slice((*text).data, (*text).size as uint, |text| {

View file

@ -132,7 +132,7 @@ pub struct Cache {
///
/// The values of the map are a list of implementations and documentation
/// found on that implementation.
pub impls: HashMap<ast::NodeId, Vec<(clean::Impl, Option<String>)>>,
pub impls: HashMap<ast::DefId, Vec<(clean::Impl, Option<String>)>>,
/// Maintains a mapping of local crate node ids to the fully qualified name
/// and "short type description" of that node. This is used when generating
@ -141,6 +141,10 @@ pub struct Cache {
/// necessary.
pub paths: HashMap<ast::DefId, (Vec<String>, ItemType)>,
/// Similar to `paths`, but only holds external paths. This is only used for
/// generating explicit hyperlinks to other crates.
pub external_paths: HashMap<ast::DefId, Vec<String>>,
/// This map contains information about all known traits of this crate.
/// Implementations of a crate should inherit the documentation of the
/// parent trait if no extra documentation is specified, and default methods
@ -157,9 +161,9 @@ pub struct Cache {
// Private fields only used when initially crawling a crate to build a cache
stack: Vec<String> ,
parent_stack: Vec<ast::NodeId> ,
search_index: Vec<IndexItem> ,
stack: Vec<String>,
parent_stack: Vec<ast::DefId>,
search_index: Vec<IndexItem>,
privmod: bool,
public_items: NodeSet,
@ -198,7 +202,7 @@ struct IndexItem {
name: String,
path: String,
desc: String,
parent: Option<ast::NodeId>,
parent: Option<ast::DefId>,
}
// TLS keys used to carry information around during rendering.
@ -249,7 +253,8 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
let analysis = ::analysiskey.get();
let public_items = analysis.as_ref().map(|a| a.public_items.clone());
let public_items = public_items.unwrap_or(NodeSet::new());
let paths = analysis.as_ref().map(|a| {
let paths: HashMap<ast::DefId, (Vec<String>, ItemType)> =
analysis.as_ref().map(|a| {
let paths = a.external_paths.borrow_mut().take_unwrap();
paths.move_iter().map(|(k, (v, t))| {
(k, (v, match t {
@ -265,6 +270,8 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
}).unwrap_or(HashMap::new());
let mut cache = Cache {
impls: HashMap::new(),
external_paths: paths.iter().map(|(&k, &(ref v, _))| (k, v.clone()))
.collect(),
paths: paths,
implementors: HashMap::new(),
stack: Vec::new(),
@ -302,7 +309,7 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
path: fqp.slice_to(fqp.len() - 1).connect("::")
.to_strbuf(),
desc: shorter(item.doc_value()).to_strbuf(),
parent: Some(pid),
parent: Some(did),
});
},
None => {}
@ -360,9 +367,8 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
try!(write!(&mut w, r#"],"paths":["#));
for (i, &nodeid) in pathid_to_nodeid.iter().enumerate() {
let def = ast_util::local_def(nodeid);
let &(ref fqp, short) = cache.paths.find(&def).unwrap();
for (i, &did) in pathid_to_nodeid.iter().enumerate() {
let &(ref fqp, short) = cache.paths.find(&did).unwrap();
if i > 0 {
try!(write!(&mut w, ","));
}
@ -497,13 +503,15 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
seen: HashSet::new(),
cx: &mut cx,
};
// skip all invalid spans
folder.seen.insert("".to_strbuf());
krate = folder.fold_crate(krate);
}
for &(n, ref e) in krate.externs.iter() {
cache.extern_locations.insert(n, extern_location(e, &cx.dst));
let did = ast::DefId { krate: n, node: ast::CRATE_NODE_ID };
cache.paths.insert(did, (Vec::new(), item_type::Module));
cache.paths.insert(did, (vec![e.name.to_strbuf()], item_type::Module));
}
// And finally render the whole crate's documentation
@ -730,14 +738,13 @@ impl DocFolder for Cache {
clean::VariantItem(..) => {
(Some(*self.parent_stack.last().unwrap()),
Some(self.stack.slice_to(self.stack.len() - 1)))
}
clean::MethodItem(..) => {
if self.parent_stack.len() == 0 {
(None, None)
} else {
let last = self.parent_stack.last().unwrap();
let did = ast_util::local_def(*last);
let did = *last;
let path = match self.paths.find(&did) {
Some(&(_, item_type::Trait)) =>
Some(self.stack.slice_to(self.stack.len() - 1)),
@ -766,9 +773,11 @@ impl DocFolder for Cache {
});
}
(Some(parent), None) if !self.privmod => {
// We have a parent, but we don't know where they're
// defined yet. Wait for later to index this item.
self.orphan_methods.push((parent, item.clone()))
if ast_util::is_local(parent) {
// We have a parent, but we don't know where they're
// defined yet. Wait for later to index this item.
self.orphan_methods.push((parent.node, item.clone()))
}
}
_ => {}
}
@ -789,19 +798,18 @@ impl DocFolder for Cache {
clean::TypedefItem(..) | clean::TraitItem(..) |
clean::FunctionItem(..) | clean::ModuleItem(..) |
clean::ForeignFunctionItem(..) => {
if ast_util::is_local(item.def_id) {
// Reexported items mean that the same id can show up twice
// in the rustdoc ast that we're looking at. We know,
// however, that a reexported item doesn't show up in the
// `public_items` map, so we can skip inserting into the
// paths map if there was already an entry present and we're
// not a public item.
let id = item.def_id.node;
if !self.paths.contains_key(&item.def_id) ||
self.public_items.contains(&id) {
self.paths.insert(item.def_id,
(self.stack.clone(), shortty(&item)));
}
// Reexported items mean that the same id can show up twice
// in the rustdoc ast that we're looking at. We know,
// however, that a reexported item doesn't show up in the
// `public_items` map, so we can skip inserting into the
// paths map if there was already an entry present and we're
// not a public item.
let id = item.def_id.node;
if !self.paths.contains_key(&item.def_id) ||
!ast_util::is_local(item.def_id) ||
self.public_items.contains(&id) {
self.paths.insert(item.def_id,
(self.stack.clone(), shortty(&item)));
}
}
// link variants to their parent enum because pages aren't emitted
@ -817,20 +825,14 @@ impl DocFolder for Cache {
// Maintain the parent stack
let parent_pushed = match item.inner {
clean::TraitItem(..) | clean::EnumItem(..) | clean::StructItem(..) => {
if ast_util::is_local(item.def_id) {
self.parent_stack.push(item.def_id.node);
}
self.parent_stack.push(item.def_id);
true
}
clean::ImplItem(ref i) => {
match i.for_ {
clean::ResolvedPath{ did, .. } => {
if ast_util::is_local(did) {
self.parent_stack.push(did.node);
true
} else {
false
}
self.parent_stack.push(did);
true
}
_ => false
}
@ -845,10 +847,8 @@ impl DocFolder for Cache {
match item {
clean::Item{ attrs, inner: clean::ImplItem(i), .. } => {
match i.for_ {
clean::ResolvedPath { did, .. }
if ast_util::is_local(did) =>
{
let v = self.impls.find_or_insert_with(did.node, |_| {
clean::ResolvedPath { did, .. } => {
let v = self.impls.find_or_insert_with(did, |_| {
Vec::new()
});
// extract relevant documentation for this impl
@ -1041,23 +1041,62 @@ impl<'a> Item<'a> {
}
}
fn link(&self) -> String {
let mut path = Vec::new();
clean_srcpath(self.item.source.filename.as_bytes(), |component| {
path.push(component.to_owned());
});
let href = if self.item.source.loline == self.item.source.hiline {
format_strbuf!("{}", self.item.source.loline)
/// Generate a url appropriate for an `href` attribute back to the source of
/// this item.
///
/// The url generated, when clicked, will redirect the browser back to the
/// original source code.
///
/// If `None` is returned, then a source link couldn't be generated. This
/// may happen, for example, with externally inlined items where the source
/// of their crate documentation isn't known.
fn href(&self) -> Option<String> {
// If this item is part of the local crate, then we're guaranteed to
// know the span, so we plow forward and generate a proper url. The url
// has anchors for the line numbers that we're linking to.
if ast_util::is_local(self.item.def_id) {
let mut path = Vec::new();
clean_srcpath(self.item.source.filename.as_bytes(), |component| {
path.push(component.to_owned());
});
let href = if self.item.source.loline == self.item.source.hiline {
format!("{}", self.item.source.loline)
} else {
format!("{}-{}",
self.item.source.loline,
self.item.source.hiline)
};
Some(format!("{root}src/{krate}/{path}.html\\#{href}",
root = self.cx.root_path,
krate = self.cx.layout.krate,
path = path.connect("/"),
href = href))
// If this item is not part of the local crate, then things get a little
// trickier. We don't actually know the span of the external item, but
// we know that the documentation on the other end knows the span!
//
// In this case, we generate a link to the *documentation* for this type
// in the original crate. There's an extra URL parameter which says that
// we want to go somewhere else, and the JS on the destination page will
// pick it up and instantly redirect the browser to the source code.
//
// If we don't know where the external documentation for this crate is
// located, then we return `None`.
} else {
format_strbuf!("{}-{}",
self.item.source.loline,
self.item.source.hiline)
};
format_strbuf!("{root}src/{krate}/{path}.html\\#{href}",
root = self.cx.root_path,
krate = self.cx.layout.krate,
path = path.connect("/"),
href = href)
let cache = cache_key.get().unwrap();
let path = cache.external_paths.get(&self.item.def_id);
let root = match *cache.extern_locations.get(&self.item.def_id.krate) {
Remote(ref s) => s.to_strbuf(),
Local => format!("{}/..", self.cx.root_path),
Unknown => return None,
};
Some(format!("{root}/{path}/{file}?gotosrc={goto}",
root = root,
path = path.slice_to(path.len() - 1).connect("/"),
file = item_path(self.item),
goto = self.item.def_id.node))
}
}
}
@ -1105,9 +1144,21 @@ impl<'a> fmt::Show for Item<'a> {
}
// Write `src` tag
//
// When this item is part of a `pub use` in a downstream crate, the
// [src] link in the downstream documentation will actually come back to
// this page, and this link will be auto-clicked. The `id` attribute is
// used to find the link to auto-click.
if self.cx.include_sources {
try!(write!(fmt, "<a class='source' href='{}'>[src]</a>",
self.link()));
match self.href() {
Some(l) => {
try!(write!(fmt,
"<a class='source' id='src-{}' \
href='{}'>[src]</a>",
self.item.def_id.node, l));
}
None => {}
}
}
try!(write!(fmt, "</h1>\n"));
@ -1266,7 +1317,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
try!(write!(f, "<code> = </code>"));
if s.contains("\n") {
write!(f, "<a href='{}'>[definition]</a>",
item.link())
item.href())
} else {
write!(f, "<code>{}</code>", s.as_slice())
}
@ -1672,7 +1723,7 @@ fn render_struct(w: &mut fmt::Formatter, it: &clean::Item,
}
fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
match cache_key.get().unwrap().impls.find(&it.def_id.node) {
match cache_key.get().unwrap().impls.find(&it.def_id) {
Some(v) => {
let mut non_trait = v.iter().filter(|p| {
p.ref0().trait_.is_none()

View file

@ -675,4 +675,10 @@
if (window.pending_implementors) {
window.register_implementors(window.pending_implementors);
}
// See documentaiton in html/render.rs for what this is doing.
var query = getQueryStringParams();
if (query['gotosrc']) {
window.location = $('#src-' + query['gotosrc']).attr('href');
}
}());

View file

@ -152,7 +152,8 @@ impl<'a> fold::DocFolder for Stripper<'a> {
clean::ImplItem(clean::Impl{
for_: clean::ResolvedPath{ did, .. }, ..
}) => {
if !self.exported_items.contains(&did.node) {
if ast_util::is_local(did) &&
!self.exported_items.contains(&did.node) {
return None;
}
}

View file

@ -16,10 +16,10 @@
//!
//! ## Intrinsic types and operations
//!
//! The [`ptr`](../core/ptr/index.html) and [`mem`](../core/mem/index.html)
//! The [`ptr`](ptr/index.html) and [`mem`](mem/index.html)
//! modules deal with unsafe pointers and memory manipulation.
//! [`kinds`](../core/kinds/index.html) defines the special built-in traits,
//! and [`raw`](../core/raw/index.html) the runtime representation of Rust types.
//! [`kinds`](kinds/index.html) defines the special built-in traits,
//! and [`raw`](raw/index.html) the runtime representation of Rust types.
//! These are some of the lowest-level building blocks in Rust.
//!
//! ## Math on primitive types and math traits
@ -31,11 +31,11 @@
//!
//! ## Pervasive types
//!
//! The [`option`](option/index.html) and [`result`](../core/result/index.html)
//! The [`option`](option/index.html) and [`result`](result/index.html)
//! modules define optional and error-handling types, `Option` and `Result`.
//! [`iter`](../core/iter/index.html) defines Rust's iterator protocol
//! [`iter`](iter/index.html) defines Rust's iterator protocol
//! along with a wide variety of iterators.
//! [`Cell` and `RefCell`](../core/cell/index.html) are for creating types that
//! [`Cell` and `RefCell`](cell/index.html) are for creating types that
//! manage their own mutability.
//!
//! ## Vectors, slices and strings

View file

@ -38,55 +38,62 @@
//! `drop`, `spawn`, and `channel`.
// Reexported core operators
pub use kinds::{Copy, Send, Sized, Share};
pub use ops::{Add, Sub, Mul, Div, Rem, Neg, Not};
pub use ops::{BitAnd, BitOr, BitXor};
pub use ops::{Drop, Deref, DerefMut};
pub use ops::{Shl, Shr, Index};
pub use option::{Option, Some, None};
pub use result::{Result, Ok, Err};
#[doc(no_inline)] pub use kinds::{Copy, Send, Sized, Share};
#[doc(no_inline)] pub use ops::{Add, Sub, Mul, Div, Rem, Neg, Not};
#[doc(no_inline)] pub use ops::{BitAnd, BitOr, BitXor};
#[doc(no_inline)] pub use ops::{Drop, Deref, DerefMut};
#[doc(no_inline)] pub use ops::{Shl, Shr, Index};
#[doc(no_inline)] pub use option::{Option, Some, None};
#[doc(no_inline)] pub use result::{Result, Ok, Err};
// Reexported functions
pub use from_str::from_str;
pub use iter::range;
pub use mem::drop;
#[doc(no_inline)] pub use from_str::from_str;
#[doc(no_inline)] pub use iter::range;
#[doc(no_inline)] pub use mem::drop;
// Reexported types and traits
pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr, IntoBytes};
pub use c_str::ToCStr;
pub use char::Char;
pub use clone::Clone;
pub use cmp::{Eq, Ord, TotalEq, TotalOrd, Ordering, Less, Equal, Greater, Equiv};
pub use container::{Container, Mutable, Map, MutableMap, Set, MutableSet};
pub use iter::{FromIterator, Extendable};
pub use iter::{Iterator, DoubleEndedIterator, RandomAccessIterator, CloneableIterator};
pub use iter::{OrdIterator, MutableDoubleEndedIterator, ExactSize};
pub use num::{Num, NumCast, CheckedAdd, CheckedSub, CheckedMul};
pub use num::{Signed, Unsigned};
pub use num::{Primitive, Int, Float, FloatMath, ToPrimitive, FromPrimitive};
pub use option::Expect;
pub use owned::Box;
pub use path::{GenericPath, Path, PosixPath, WindowsPath};
pub use ptr::RawPtr;
pub use io::{Buffer, Writer, Reader, Seek};
pub use str::{Str, StrVector, StrSlice, OwnedStr, IntoMaybeOwned};
pub use str::{StrAllocating};
pub use to_str::{ToStr, IntoStr};
pub use tuple::{Tuple1, Tuple2, Tuple3, Tuple4};
pub use tuple::{Tuple5, Tuple6, Tuple7, Tuple8};
pub use tuple::{Tuple9, Tuple10, Tuple11, Tuple12};
pub use slice::{CloneableVector, ImmutableCloneableVector, MutableCloneableVector};
pub use slice::{ImmutableVector, MutableVector};
pub use slice::{ImmutableEqVector, ImmutableTotalOrdVector, MutableTotalOrdVector};
pub use slice::{Vector, VectorVector, OwnedVector, MutableVectorAllocating};
pub use string::String;
pub use vec::Vec;
#[doc(no_inline)] pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr};
#[doc(no_inline)] pub use ascii::IntoBytes;
#[doc(no_inline)] pub use c_str::ToCStr;
#[doc(no_inline)] pub use char::Char;
#[doc(no_inline)] pub use clone::Clone;
#[doc(no_inline)] pub use cmp::{Eq, Ord, TotalEq, TotalOrd};
#[doc(no_inline)] pub use cmp::{Ordering, Less, Equal, Greater, Equiv};
#[doc(no_inline)] pub use container::{Container, Mutable, Map, MutableMap};
#[doc(no_inline)] pub use container::{Set, MutableSet};
#[doc(no_inline)] pub use iter::{FromIterator, Extendable, ExactSize};
#[doc(no_inline)] pub use iter::{Iterator, DoubleEndedIterator};
#[doc(no_inline)] pub use iter::{RandomAccessIterator, CloneableIterator};
#[doc(no_inline)] pub use iter::{OrdIterator, MutableDoubleEndedIterator};
#[doc(no_inline)] pub use num::{Num, NumCast, CheckedAdd, CheckedSub, CheckedMul};
#[doc(no_inline)] pub use num::{Signed, Unsigned, Primitive, Int, Float};
#[doc(no_inline)] pub use num::{FloatMath, ToPrimitive, FromPrimitive};
#[doc(no_inline)] pub use option::Expect;
#[doc(no_inline)] pub use owned::Box;
#[doc(no_inline)] pub use path::{GenericPath, Path, PosixPath, WindowsPath};
#[doc(no_inline)] pub use ptr::RawPtr;
#[doc(no_inline)] pub use io::{Buffer, Writer, Reader, Seek};
#[doc(no_inline)] pub use str::{Str, StrVector, StrSlice, OwnedStr};
#[doc(no_inline)] pub use str::{IntoMaybeOwned, StrAllocating};
#[doc(no_inline)] pub use to_str::{ToStr, IntoStr};
#[doc(no_inline)] pub use tuple::{Tuple1, Tuple2, Tuple3, Tuple4};
#[doc(no_inline)] pub use tuple::{Tuple5, Tuple6, Tuple7, Tuple8};
#[doc(no_inline)] pub use tuple::{Tuple9, Tuple10, Tuple11, Tuple12};
#[doc(no_inline)] pub use slice::{CloneableVector, ImmutableCloneableVector};
#[doc(no_inline)] pub use slice::{MutableCloneableVector, MutableTotalOrdVector};
#[doc(no_inline)] pub use slice::{ImmutableVector, MutableVector};
#[doc(no_inline)] pub use slice::{ImmutableEqVector, ImmutableTotalOrdVector};
#[doc(no_inline)] pub use slice::{Vector, VectorVector, OwnedVector};
#[doc(no_inline)] pub use slice::MutableVectorAllocating;
#[doc(no_inline)] pub use string::String;
#[doc(no_inline)] pub use vec::Vec;
// Reexported runtime types
pub use comm::{sync_channel, channel, SyncSender, Sender, Receiver};
pub use task::spawn;
#[doc(no_inline)] pub use comm::{sync_channel, channel};
#[doc(no_inline)] pub use comm::{SyncSender, Sender, Receiver};
#[doc(no_inline)] pub use task::spawn;
// Reexported statics
#[cfg(not(test))]
pub use gc::GC;
#[doc(no_inline)] pub use gc::GC;