| //! Pulls type information out of the AST and attaches it to the document |
| |
| use doc::ItemUtils; |
| use syntax::ast; |
| use syntax::print::pprust; |
| use syntax::ast_map; |
| use std::map::HashMap; |
| use extract::to_str; |
| |
| export mk_pass; |
| |
| fn mk_pass() -> Pass { |
| { |
| name: ~"tystr", |
| f: run |
| } |
| } |
| |
| fn run( |
| srv: astsrv::Srv, |
| doc: doc::Doc |
| ) -> doc::Doc { |
| let fold = fold::Fold({ |
| fold_fn: fold_fn, |
| fold_const: fold_const, |
| fold_enum: fold_enum, |
| fold_trait: fold_trait, |
| fold_impl: fold_impl, |
| fold_type: fold_type, |
| fold_struct: fold_struct, |
| .. *fold::default_any_fold(srv) |
| }); |
| fold.fold_doc(fold, doc) |
| } |
| |
| fn fold_fn( |
| fold: fold::Fold<astsrv::Srv>, |
| doc: doc::FnDoc |
| ) -> doc::FnDoc { |
| |
| let srv = fold.ctxt; |
| |
| { |
| sig: get_fn_sig(srv, doc.id()), |
| .. doc |
| } |
| } |
| |
| fn get_fn_sig(srv: astsrv::Srv, fn_id: doc::AstId) -> Option<~str> { |
| do astsrv::exec(srv) |ctxt| { |
| match ctxt.ast_map.get(fn_id) { |
| ast_map::node_item(@{ |
| ident: ident, |
| node: ast::item_fn(decl, _, tys, _), _ |
| }, _) | |
| ast_map::node_foreign_item(@{ |
| ident: ident, |
| node: ast::foreign_item_fn(decl, _, tys), _ |
| }, _, _) => { |
| Some(pprust::fun_to_str(decl, ident, tys, extract::interner())) |
| } |
| _ => fail ~"get_fn_sig: fn_id not bound to a fn item" |
| } |
| } |
| } |
| |
| #[test] |
| fn should_add_fn_sig() { |
| let doc = test::mk_doc(~"fn a<T>() -> int { }"); |
| assert doc.cratemod().fns()[0].sig == Some(~"fn a<T>() -> int"); |
| } |
| |
| #[test] |
| fn should_add_foreign_fn_sig() { |
| let doc = test::mk_doc(~"extern mod a { fn a<T>() -> int; }"); |
| assert doc.cratemod().nmods()[0].fns[0].sig == Some(~"fn a<T>() -> int"); |
| } |
| |
| fn fold_const( |
| fold: fold::Fold<astsrv::Srv>, |
| doc: doc::ConstDoc |
| ) -> doc::ConstDoc { |
| let srv = fold.ctxt; |
| |
| { |
| sig: Some(do astsrv::exec(srv) |ctxt| { |
| match ctxt.ast_map.get(doc.id()) { |
| ast_map::node_item(@{ |
| node: ast::item_const(ty, _), _ |
| }, _) => { |
| pprust::ty_to_str(ty, extract::interner()) |
| } |
| _ => fail ~"fold_const: id not bound to a const item" |
| } |
| }), |
| .. doc |
| } |
| } |
| |
| #[test] |
| fn should_add_const_types() { |
| let doc = test::mk_doc(~"const a: bool = true;"); |
| assert doc.cratemod().consts()[0].sig == Some(~"bool"); |
| } |
| |
| fn fold_enum( |
| fold: fold::Fold<astsrv::Srv>, |
| doc: doc::EnumDoc |
| ) -> doc::EnumDoc { |
| let doc_id = doc.id(); |
| let srv = fold.ctxt; |
| |
| { |
| variants: do par::map(doc.variants) |variant| { |
| let variant = *variant; |
| let sig = do astsrv::exec(srv) |ctxt| { |
| match ctxt.ast_map.get(doc_id) { |
| ast_map::node_item(@{ |
| node: ast::item_enum(enum_definition, _), _ |
| }, _) => { |
| let ast_variant = |
| do vec::find(enum_definition.variants) |v| { |
| to_str(v.node.name) == variant.name |
| }.get(); |
| |
| pprust::variant_to_str(ast_variant, extract::interner()) |
| } |
| _ => fail ~"enum variant not bound to an enum item" |
| } |
| }; |
| |
| { |
| sig: Some(sig), |
| .. variant |
| } |
| }, |
| .. doc |
| } |
| } |
| |
| #[test] |
| fn should_add_variant_sigs() { |
| let doc = test::mk_doc(~"enum a { b(int) }"); |
| assert doc.cratemod().enums()[0].variants[0].sig == Some(~"b(int)"); |
| } |
| |
| fn fold_trait( |
| fold: fold::Fold<astsrv::Srv>, |
| doc: doc::TraitDoc |
| ) -> doc::TraitDoc { |
| { |
| methods: merge_methods(fold.ctxt, doc.id(), doc.methods), |
| .. doc |
| } |
| } |
| |
| fn merge_methods( |
| srv: astsrv::Srv, |
| item_id: doc::AstId, |
| docs: ~[doc::MethodDoc] |
| ) -> ~[doc::MethodDoc] { |
| do par::map(docs) |doc| { |
| { |
| sig: get_method_sig(srv, item_id, doc.name), |
| .. *doc |
| } |
| } |
| } |
| |
| fn get_method_sig( |
| srv: astsrv::Srv, |
| item_id: doc::AstId, |
| method_name: ~str |
| ) -> Option<~str> { |
| do astsrv::exec(srv) |ctxt| { |
| match ctxt.ast_map.get(item_id) { |
| ast_map::node_item(@{ |
| node: ast::item_trait(_, _, methods), _ |
| }, _) => { |
| match vec::find(methods, |method| { |
| match *method { |
| ast::required(ty_m) => to_str(ty_m.ident) == method_name, |
| ast::provided(m) => to_str(m.ident) == method_name, |
| } |
| }) { |
| Some(method) => { |
| match method { |
| ast::required(ty_m) => { |
| Some(pprust::fun_to_str( |
| ty_m.decl, |
| ty_m.ident, |
| ty_m.tps, |
| extract::interner() |
| )) |
| } |
| ast::provided(m) => { |
| Some(pprust::fun_to_str( |
| m.decl, |
| m.ident, |
| m.tps, |
| extract::interner() |
| )) |
| } |
| } |
| } |
| _ => fail ~"method not found" |
| } |
| } |
| ast_map::node_item(@{ |
| node: ast::item_impl(_, _, _, methods), _ |
| }, _) => { |
| match vec::find(methods, |method| { |
| to_str(method.ident) == method_name |
| }) { |
| Some(method) => { |
| Some(pprust::fun_to_str( |
| method.decl, |
| method.ident, |
| method.tps, |
| extract::interner() |
| )) |
| } |
| None => fail ~"method not found" |
| } |
| } |
| _ => fail ~"get_method_sig: item ID not bound to trait or impl" |
| } |
| } |
| } |
| |
| #[test] |
| fn should_add_trait_method_sigs() { |
| let doc = test::mk_doc(~"trait i { fn a<T>() -> int; }"); |
| assert doc.cratemod().traits()[0].methods[0].sig |
| == Some(~"fn a<T>() -> int"); |
| } |
| |
| fn fold_impl( |
| fold: fold::Fold<astsrv::Srv>, |
| doc: doc::ImplDoc |
| ) -> doc::ImplDoc { |
| |
| let srv = fold.ctxt; |
| |
| let (trait_types, self_ty) = do astsrv::exec(srv) |ctxt| { |
| match ctxt.ast_map.get(doc.id()) { |
| ast_map::node_item(@{ |
| node: ast::item_impl(_, opt_trait_type, self_ty, _), _ |
| }, _) => { |
| let trait_types = opt_trait_type.map_default(~[], |p| { |
| ~[pprust::path_to_str(p.path, extract::interner())] |
| }); |
| (trait_types, Some(pprust::ty_to_str(self_ty, |
| extract::interner()))) |
| } |
| _ => fail ~"expected impl" |
| } |
| }; |
| |
| { |
| trait_types: trait_types, |
| self_ty: self_ty, |
| methods: merge_methods(fold.ctxt, doc.id(), doc.methods), |
| .. doc |
| } |
| } |
| |
| #[test] |
| fn should_add_impl_trait_types() { |
| let doc = test::mk_doc(~"impl int: j { fn a<T>() { } }"); |
| assert doc.cratemod().impls()[0].trait_types[0] == ~"j"; |
| } |
| |
| #[test] |
| fn should_not_add_impl_trait_types_if_none() { |
| let doc = test::mk_doc(~"impl int { fn a() { } }"); |
| assert vec::len(doc.cratemod().impls()[0].trait_types) == 0; |
| } |
| |
| #[test] |
| fn should_add_impl_self_ty() { |
| let doc = test::mk_doc(~"impl int { fn a() { } }"); |
| assert doc.cratemod().impls()[0].self_ty == Some(~"int"); |
| } |
| |
| #[test] |
| fn should_add_impl_method_sigs() { |
| let doc = test::mk_doc(~"impl int { fn a<T>() -> int { fail } }"); |
| assert doc.cratemod().impls()[0].methods[0].sig |
| == Some(~"fn a<T>() -> int"); |
| } |
| |
| fn fold_type( |
| fold: fold::Fold<astsrv::Srv>, |
| doc: doc::TyDoc |
| ) -> doc::TyDoc { |
| |
| let srv = fold.ctxt; |
| |
| { |
| sig: do astsrv::exec(srv) |ctxt| { |
| match ctxt.ast_map.get(doc.id()) { |
| ast_map::node_item(@{ |
| ident: ident, |
| node: ast::item_ty(ty, params), _ |
| }, _) => { |
| Some(fmt!( |
| "type %s%s = %s", |
| to_str(ident), |
| pprust::typarams_to_str(params, extract::interner()), |
| pprust::ty_to_str(ty, extract::interner()) |
| )) |
| } |
| _ => fail ~"expected type" |
| } |
| }, |
| .. doc |
| } |
| } |
| |
| #[test] |
| fn should_add_type_signatures() { |
| let doc = test::mk_doc(~"type t<T> = int;"); |
| assert doc.cratemod().types()[0].sig == Some(~"type t<T> = int"); |
| } |
| |
| fn fold_struct( |
| fold: fold::Fold<astsrv::Srv>, |
| doc: doc::StructDoc |
| ) -> doc::StructDoc { |
| let srv = fold.ctxt; |
| |
| { |
| sig: do astsrv::exec(srv) |ctxt| { |
| match ctxt.ast_map.get(doc.id()) { |
| ast_map::node_item(item, _) => { |
| let item = strip_struct_extra_stuff(item); |
| Some(pprust::item_to_str(item, |
| extract::interner())) |
| } |
| _ => fail ~"not an item" |
| } |
| }, |
| .. doc |
| } |
| } |
| |
| /// Removes various things from the struct item definition that |
| /// shouldn't be displayed in the struct signature. Probably there |
| /// should be a simple pprust::struct_to_str function that does |
| /// what I actually want |
| fn strip_struct_extra_stuff(item: @ast::item) -> @ast::item { |
| let node = match item.node { |
| ast::item_class(def, tys) => { |
| let def = @{ |
| dtor: None, // Remove the drop { } block |
| .. *def |
| }; |
| ast::item_class(def, tys) |
| } |
| _ => fail ~"not a struct" |
| }; |
| |
| @{ |
| attrs: ~[], // Remove the attributes |
| node: node, |
| .. *item |
| } |
| } |
| |
| #[test] |
| fn should_add_struct_defs() { |
| let doc = test::mk_doc(~"struct S { field: () }"); |
| assert doc.cratemod().structs()[0].sig.get().contains("struct S {"); |
| } |
| |
| #[test] |
| fn should_not_serialize_struct_drop_blocks() { |
| // All we care about are the fields |
| let doc = test::mk_doc(~"struct S { field: (), drop { } }"); |
| assert !doc.cratemod().structs()[0].sig.get().contains("drop"); |
| } |
| |
| #[test] |
| fn should_not_serialize_struct_attrs() { |
| // All we care about are the fields |
| let doc = test::mk_doc(~"#[wut] struct S { field: () }"); |
| assert !doc.cratemod().structs()[0].sig.get().contains("wut"); |
| } |
| |
| #[cfg(test)] |
| mod test { |
| #[legacy_exports]; |
| fn mk_doc(source: ~str) -> doc::Doc { |
| do astsrv::from_str(source) |srv| { |
| let doc = extract::from_srv(srv, ~""); |
| run(srv, doc) |
| } |
| } |
| } |