1
Fork 0
rust/src/librustdoc/extract.rs

431 lines
11 KiB
Rust
Raw Normal View History

// Copyright 2012 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.
//! Converts the Rust AST to the rustdoc document model
2012-01-17 17:44:32 -08:00
use core::prelude::*;
use astsrv;
2012-09-18 16:48:40 -07:00
use doc::ItemUtils;
use doc;
use core::cast;
use core::task::local_data::local_data_get;
use core::vec;
use syntax::ast;
use syntax;
/* can't import macros yet, so this is copied from token.rs. See its comment
* there. */
macro_rules! interner_key (
2012-09-18 17:34:08 -07:00
() => (cast::transmute::<(uint, uint),
2012-09-23 05:39:39 -07:00
&fn(+v: @@syntax::parse::token::ident_interner)>((-3 as uint, 0u)))
)
2012-07-18 16:18:02 -07:00
// Hack; rather than thread an interner through everywhere, rely on
// thread-local data
2012-11-19 18:00:12 -08:00
pub fn to_str(id: ast::ident) -> ~str {
2012-09-19 17:29:54 -07:00
let intr = unsafe{ local_data_get(interner_key!()) };
2012-07-18 16:18:02 -07:00
2013-01-30 13:14:35 -08:00
return copy *(*intr.get()).get(id);
2012-07-18 16:18:02 -07:00
}
2012-11-19 18:00:12 -08:00
pub fn interner() -> @syntax::parse::token::ident_interner {
2012-09-19 17:29:54 -07:00
return *(unsafe{ local_data_get(interner_key!()) }).get();
2012-07-18 16:18:02 -07:00
}
2012-11-19 18:00:12 -08:00
pub fn from_srv(
2012-09-18 16:48:40 -07:00
srv: astsrv::Srv,
2013-01-30 19:32:36 -08:00
default_name: ~str
2012-09-18 16:48:40 -07:00
) -> doc::Doc {
2012-01-18 14:06:22 -08:00
//! Use the AST service to create a document tree
2012-01-18 14:06:22 -08:00
2012-06-30 16:19:07 -07:00
do astsrv::exec(srv) |ctxt| {
2013-01-30 13:14:35 -08:00
extract(ctxt.ast, copy default_name)
}
}
2012-11-19 18:00:12 -08:00
pub fn extract(
crate: @ast::crate,
2013-01-30 19:32:36 -08:00
default_name: ~str
2012-09-18 16:48:40 -07:00
) -> doc::Doc {
doc::Doc {
pages: ~[
doc::CratePage(doc::CrateDoc {
topmod: top_moddoc_from_crate(crate, default_name),
})
]
}
}
fn top_moddoc_from_crate(
crate: @ast::crate,
2013-01-30 19:32:36 -08:00
default_name: ~str
2012-09-18 16:48:40 -07:00
) -> doc::ModDoc {
moddoc_from_mod(mk_itemdoc(ast::crate_node_id, default_name),
2013-01-30 18:52:31 -08:00
copy crate.node.module)
}
2013-01-30 19:32:36 -08:00
fn mk_itemdoc(id: ast::node_id, name: ~str) -> doc::ItemDoc {
doc::ItemDoc {
2012-02-17 15:39:05 -08:00
id: id,
2012-07-18 16:18:02 -07:00
name: name,
path: ~[],
2012-08-20 12:23:37 -07:00
brief: None,
desc: None,
sections: ~[],
2012-02-24 15:43:57 -08:00
reexport: false
2012-02-17 15:39:05 -08:00
}
}
fn moddoc_from_mod(
2013-01-30 19:32:36 -08:00
itemdoc: doc::ItemDoc,
module_: ast::_mod
2012-09-18 16:48:40 -07:00
) -> doc::ModDoc {
doc::ModDoc {
item: itemdoc,
items: do vec::filter_mapped(module_.items) |item| {
let ItemDoc = mk_itemdoc(item.id, to_str(item.ident));
2013-01-30 13:14:35 -08:00
match copy item.node {
2012-08-03 19:59:04 -07:00
ast::item_mod(m) => {
2012-09-18 16:48:40 -07:00
Some(doc::ModTag(
moddoc_from_mod(ItemDoc, m)
))
}
2012-08-03 19:59:04 -07:00
ast::item_foreign_mod(nm) => {
2012-09-18 16:48:40 -07:00
Some(doc::NmodTag(
nmoddoc_from_mod(ItemDoc, nm)
))
}
ast::item_fn(*) => {
2012-09-18 16:48:40 -07:00
Some(doc::FnTag(
fndoc_from_fn(ItemDoc)
))
}
2012-08-03 19:59:04 -07:00
ast::item_const(_, _) => {
2012-09-18 16:48:40 -07:00
Some(doc::ConstTag(
constdoc_from_const(ItemDoc)
))
}
ast::item_enum(enum_definition, _) => {
2012-09-18 16:48:40 -07:00
Some(doc::EnumTag(
2013-01-30 13:14:35 -08:00
enumdoc_from_enum(ItemDoc, copy enum_definition.variants)
))
}
2012-08-03 19:59:04 -07:00
ast::item_trait(_, _, methods) => {
2012-09-18 16:48:40 -07:00
Some(doc::TraitTag(
traitdoc_from_trait(ItemDoc, methods)
))
}
2012-08-03 19:59:04 -07:00
ast::item_impl(_, _, _, methods) => {
2012-09-18 16:48:40 -07:00
Some(doc::ImplTag(
impldoc_from_impl(ItemDoc, methods)
))
}
2012-08-03 19:59:04 -07:00
ast::item_ty(_, _) => {
2012-09-18 16:48:40 -07:00
Some(doc::TyTag(
tydoc_from_ty(ItemDoc)
2012-02-01 22:41:41 -08:00
))
}
ast::item_struct(def, _) => {
2012-09-19 14:37:43 -07:00
Some(doc::StructTag(
structdoc_from_struct(ItemDoc, def)
))
}
2012-08-20 12:23:37 -07:00
_ => None
}
},
2012-08-20 12:23:37 -07:00
index: None
}
}
fn nmoddoc_from_mod(
2013-01-30 19:32:36 -08:00
itemdoc: doc::ItemDoc,
module_: ast::foreign_mod
2012-09-18 16:48:40 -07:00
) -> doc::NmodDoc {
let mut fns = ~[];
for module_.items.each |item| {
let ItemDoc = mk_itemdoc(item.id, to_str(item.ident));
match item.node {
ast::foreign_item_fn(*) => {
fns.push(fndoc_from_fn(ItemDoc));
}
ast::foreign_item_const(*) => {} // XXX: Not implemented.
}
}
doc:: NmodDoc {
item: itemdoc,
fns: fns,
2012-08-20 12:23:37 -07:00
index: None
}
}
2013-01-30 19:32:36 -08:00
fn fndoc_from_fn(itemdoc: doc::ItemDoc) -> doc::FnDoc {
doc::SimpleItemDoc {
item: itemdoc,
2012-08-20 12:23:37 -07:00
sig: None
}
}
2013-01-30 19:32:36 -08:00
fn constdoc_from_const(itemdoc: doc::ItemDoc) -> doc::ConstDoc {
doc::SimpleItemDoc {
item: itemdoc,
2012-08-20 12:23:37 -07:00
sig: None
}
}
#[test]
fn should_extract_const_name_and_id() {
let doc = test::mk_doc(~"const a: int = 0;");
assert doc.cratemod().consts()[0].id() != 0;
assert doc.cratemod().consts()[0].name() == ~"a";
}
fn enumdoc_from_enum(
2013-01-30 19:32:36 -08:00
itemdoc: doc::ItemDoc,
variants: ~[ast::variant]
2012-09-18 16:48:40 -07:00
) -> doc::EnumDoc {
doc::EnumDoc {
item: itemdoc,
variants: variantdocs_from_variants(variants)
}
}
fn variantdocs_from_variants(
2013-01-30 19:32:36 -08:00
variants: ~[ast::variant]
2012-09-18 16:48:40 -07:00
) -> ~[doc::VariantDoc] {
vec::map(variants, variantdoc_from_variant)
}
fn variantdoc_from_variant(variant: &ast::variant) -> doc::VariantDoc {
doc::VariantDoc {
2012-07-18 16:18:02 -07:00
name: to_str(variant.node.name),
2012-08-20 12:23:37 -07:00
desc: None,
sig: None
}
}
#[test]
fn should_extract_enums() {
let doc = test::mk_doc(~"enum e { v }");
assert doc.cratemod().enums()[0].id() != 0;
assert doc.cratemod().enums()[0].name() == ~"e";
}
#[test]
fn should_extract_enum_variants() {
let doc = test::mk_doc(~"enum e { v }");
assert doc.cratemod().enums()[0].variants[0].name == ~"v";
}
fn traitdoc_from_trait(
2013-01-30 19:32:36 -08:00
itemdoc: doc::ItemDoc,
methods: ~[ast::trait_method]
2012-09-18 16:48:40 -07:00
) -> doc::TraitDoc {
doc::TraitDoc {
item: itemdoc,
methods: do vec::map(methods) |method| {
2013-01-30 13:14:35 -08:00
match copy *method {
2012-08-03 19:59:04 -07:00
ast::required(ty_m) => {
doc::MethodDoc {
2012-07-18 16:18:02 -07:00
name: to_str(ty_m.ident),
2012-08-20 12:23:37 -07:00
brief: None,
desc: None,
sections: ~[],
2012-08-20 12:23:37 -07:00
sig: None,
2012-09-18 16:48:40 -07:00
implementation: doc::Required,
}
}
2012-08-03 19:59:04 -07:00
ast::provided(m) => {
doc::MethodDoc {
2012-07-18 16:18:02 -07:00
name: to_str(m.ident),
2012-08-20 12:23:37 -07:00
brief: None,
desc: None,
sections: ~[],
2012-08-20 12:23:37 -07:00
sig: None,
2012-09-18 16:48:40 -07:00
implementation: doc::Provided,
}
}
}
}
}
}
#[test]
fn should_extract_traits() {
let doc = test::mk_doc(~"trait i { fn f(); }");
assert doc.cratemod().traits()[0].name() == ~"i";
}
#[test]
fn should_extract_trait_methods() {
let doc = test::mk_doc(~"trait i { fn f(); }");
assert doc.cratemod().traits()[0].methods[0].name == ~"f";
}
fn impldoc_from_impl(
2013-01-30 19:32:36 -08:00
itemdoc: doc::ItemDoc,
methods: ~[@ast::method]
2012-09-18 16:48:40 -07:00
) -> doc::ImplDoc {
doc::ImplDoc {
item: itemdoc,
trait_types: ~[],
2012-08-20 12:23:37 -07:00
self_ty: None,
methods: do vec::map(methods) |method| {
doc::MethodDoc {
name: to_str(method.ident),
brief: None,
desc: None,
sections: ~[],
sig: None,
implementation: doc::Provided,
}
}
}
}
#[test]
fn should_extract_impl_methods() {
2012-08-08 17:19:06 -07:00
let doc = test::mk_doc(~"impl int { fn f() { } }");
assert doc.cratemod().impls()[0].methods[0].name == ~"f";
}
fn tydoc_from_ty(
2013-01-30 19:32:36 -08:00
itemdoc: doc::ItemDoc
2012-09-18 16:48:40 -07:00
) -> doc::TyDoc {
doc::SimpleItemDoc {
item: itemdoc,
2012-08-20 12:23:37 -07:00
sig: None
2012-02-01 22:41:41 -08:00
}
}
#[test]
fn should_extract_tys() {
let doc = test::mk_doc(~"type a = int;");
assert doc.cratemod().types()[0].name() == ~"a";
2012-02-01 22:41:41 -08:00
}
2012-09-19 14:37:43 -07:00
fn structdoc_from_struct(
2013-01-30 19:32:36 -08:00
itemdoc: doc::ItemDoc,
2012-09-19 14:37:43 -07:00
struct_def: @ast::struct_def
) -> doc::StructDoc {
doc::StructDoc {
2012-09-19 14:37:43 -07:00
item: itemdoc,
fields: do struct_def.fields.map |field| {
match field.node.kind {
ast::named_field(ident, _, _) => to_str(ident),
ast::unnamed_field => fail!(
~"what is an unnamed struct field?")
2012-09-19 14:37:43 -07:00
}
},
sig: None
}
}
#[test]
fn should_extract_structs() {
let doc = test::mk_doc(~"struct Foo { field: () }");
assert doc.cratemod().structs()[0].name() == ~"Foo";
}
#[test]
fn should_extract_struct_fields() {
let doc = test::mk_doc(~"struct Foo { field: () }");
assert doc.cratemod().structs()[0].fields[0] == ~"field";
}
#[cfg(test)]
2012-01-31 18:32:37 -08:00
mod test {
use astsrv;
use doc;
use extract::{extract, from_srv};
use parse;
use core::vec;
2013-01-30 19:32:36 -08:00
pub fn mk_doc(source: ~str) -> doc::Doc {
let ast = parse::from_str(source);
extract(ast, ~"")
2012-01-31 18:32:37 -08:00
}
#[test]
pub fn extract_empty_crate() {
let doc = mk_doc(~"");
assert vec::is_empty(doc.cratemod().mods());
assert vec::is_empty(doc.cratemod().fns());
}
#[test]
pub fn extract_mods() {
let doc = mk_doc(~"mod a { mod b { } mod c { } }");
assert doc.cratemod().mods()[0].name() == ~"a";
assert doc.cratemod().mods()[0].mods()[0].name() == ~"b";
assert doc.cratemod().mods()[0].mods()[1].name() == ~"c";
}
#[test]
pub fn extract_foreign_mods() {
let doc = mk_doc(~"extern mod a { }");
assert doc.cratemod().nmods()[0].name() == ~"a";
}
#[test]
pub fn extract_fns_from_foreign_mods() {
let doc = mk_doc(~"extern mod a { fn a(); }");
assert doc.cratemod().nmods()[0].fns[0].name() == ~"a";
}
#[test]
pub fn extract_mods_deep() {
let doc = mk_doc(~"mod a { mod b { mod c { } } }");
assert doc.cratemod().mods()[0].mods()[0].mods()[0].name() == ~"c";
}
2012-01-18 18:35:55 -08:00
#[test]
pub fn extract_should_set_mod_ast_id() {
let doc = mk_doc(~"mod a { }");
assert doc.cratemod().mods()[0].id() != 0;
2012-01-18 18:35:55 -08:00
}
#[test]
pub fn extract_fns() {
2012-01-31 18:32:37 -08:00
let doc = mk_doc(
~"fn a() { } \
2013-01-30 13:14:35 -08:00
mod b { fn c() {
} }");
assert doc.cratemod().fns()[0].name() == ~"a";
assert doc.cratemod().mods()[0].fns()[0].name() == ~"c";
}
#[test]
pub fn extract_should_set_fn_ast_id() {
let doc = mk_doc(~"fn a() { }");
assert doc.cratemod().fns()[0].id() != 0;
}
#[test]
pub fn extract_should_use_default_crate_name() {
let source = ~"";
let ast = parse::from_str(source);
let doc = extract(ast, ~"burp");
assert doc.cratemod().name() == ~"burp";
}
#[test]
pub fn extract_from_seq_srv() {
let source = ~"";
2012-06-30 16:19:07 -07:00
do astsrv::from_str(source) |srv| {
let doc = from_srv(srv, ~"name");
assert doc.cratemod().name() == ~"name";
}
}
}