rustdoc: Simplify attribute parsing

This commit is contained in:
Brian Anderson 2012-03-09 18:12:15 -08:00
parent effe4559d2
commit f9755c666d
6 changed files with 62 additions and 233 deletions

View File

@ -9,23 +9,14 @@ import rustc::syntax::ast;
import rustc::front::attr;
import core::tuple;
export crate_attrs, basic_attrs, variant_attrs;
export parse_crate, parse_basic, parse_variant;
export crate_attrs;
export parse_crate, parse_desc;
export parse_hidden;
type crate_attrs = {
name: option<str>
};
type basic_attrs = {
brief: option<str>,
desc: option<str>
};
type variant_attrs = {
desc: option<str>
};
#[cfg(test)]
mod test {
@ -102,170 +93,44 @@ fn should_not_extract_crate_name_if_no_name_value_in_link_attribute() {
assert attrs.name == none;
}
fn parse_basic(
attrs: [ast::attribute]
) -> {
brief: option<str>,
desc: option<str>
} {
parse_short_doc_or(
attrs,
{|desc|
{
brief: none,
desc: desc
}
},
{|_items, brief, desc|
{
brief: brief,
desc: desc
}
}
)
fn parse_desc(attrs: [ast::attribute]) -> option<str> {
alt doc_meta(attrs) {
some(meta) {
attr::get_meta_item_value_str(meta)
}
none { none }
}
}
#[test]
fn parse_basic_should_handle_undocumented_mods() {
fn parse_desc_should_handle_undocumented_mods() {
let source = "";
let attrs = test::parse_attributes(source);
let attrs = parse_basic(attrs);
assert attrs.brief == none;
assert attrs.desc == none;
let attrs = parse_desc(attrs);
assert attrs == none;
}
#[test]
fn parse_basic_should_parse_simple_doc_attributes() {
fn parse_desc_should_parse_simple_doc_attributes() {
let source = "#[doc = \"basic\"]";
let attrs = test::parse_attributes(source);
let attrs = parse_basic(attrs);
assert attrs.desc == some("basic");
}
#[test]
fn parse_basic_should_parse_the_brief_description() {
let source = "#[doc(brief = \"short\")]";
let attrs = test::parse_attributes(source);
let attrs = parse_basic(attrs);
assert attrs.brief == some("short");
}
#[test]
fn parse_basic_should_parse_the_long_description() {
let source = "#[doc(desc = \"description\")]";
let attrs = test::parse_attributes(source);
let attrs = parse_basic(attrs);
assert attrs.desc == some("description");
}
fn parse_short_doc_or<T>(
attrs: [ast::attribute],
handle_short: fn&(
short_desc: option<str>
) -> T,
parse_long: fn&(
doc_items: [@ast::meta_item],
brief: option<str>,
desc: option<str>
) -> T
) -> T {
alt doc_meta(attrs) {
some(meta) {
alt attr::get_meta_item_value_str(meta) {
some(desc) { handle_short(some(desc)) }
none {
alt attr::get_meta_item_list(meta) {
some(list) {
let brief = attr::meta_item_value_from_list(list, "brief");
let desc = attr::meta_item_value_from_list(list, "desc");
parse_long(list, brief, desc)
}
none {
handle_short(none)
}
}
}
}
}
none {
handle_short(none)
}
}
}
fn parse_long_doc<T>(
attrs: [ast::attribute],
parse_long: fn&(doc_items: [@ast::meta_item]) -> T
) -> T {
alt doc_meta(attrs) {
some(meta) {
alt attr::get_meta_item_list(meta) {
some(list) {
parse_long(list)
}
none {
parse_long([])
}
}
}
none { parse_long([]) }
}
}
fn parse_variant(attrs: [ast::attribute]) -> variant_attrs {
parse_short_doc_or(
attrs,
{|desc|
{
desc: desc
}
},
{|_items, brief, desc|
if option::is_some(brief) && option::is_some(desc) {
// FIXME: Warn about dropping brief description
}
{
// Prefer desc over brief
desc: option::maybe(brief, desc, {|s| some(s) })
}
}
)
}
#[test]
fn should_parse_variant_short_doc() {
let source = "#[doc = \"a\"]";
let attrs = test::parse_attributes(source);
let attrs = parse_variant(attrs);
assert attrs.desc == some("a");
}
#[test]
fn should_parse_variant_brief_doc() {
let source = "#[doc(brief = \"a\")]";
let attrs = test::parse_attributes(source);
let attrs = parse_variant(attrs);
assert attrs.desc == some("a");
}
#[test]
fn should_parse_variant_long_doc() {
let source = "#[doc(desc = \"a\")]";
let attrs = test::parse_attributes(source);
let attrs = parse_variant(attrs);
assert attrs.desc == some("a");
let attrs = parse_desc(attrs);
assert attrs == some("basic");
}
fn parse_hidden(attrs: [ast::attribute]) -> bool {
parse_short_doc_or(
attrs,
{|_desc| false },
{|metas, _brief, _desc|
alt doc_meta(attrs) {
some(meta) {
alt attr::get_meta_item_list(meta) {
some(metas) {
let hiddens = attr::find_meta_items_by_name(metas, "hidden");
vec::is_not_empty(hiddens)
}
none { false }
}
)
}
none { false }
}
}
#[test]

View File

@ -72,18 +72,17 @@ fn fold_item(
let srv = fold.ctxt;
let doc = fold::default_seq_fold_item(fold, doc);
let attrs = if doc.id == ast::crate_node_id {
let desc = if doc.id == ast::crate_node_id {
// This is the top-level mod, use the crate attributes
astsrv::exec(srv) {|ctxt|
attr_parser::parse_basic(ctxt.ast.node.attrs)
attr_parser::parse_desc(ctxt.ast.node.attrs)
}
} else {
parse_item_attrs(srv, doc.id, attr_parser::parse_basic)
parse_item_attrs(srv, doc.id, attr_parser::parse_desc)
};
{
brief: attrs.brief,
desc: attrs.desc
desc: desc
with doc
}
}
@ -134,14 +133,6 @@ fn should_extract_fn_attributes() {
assert doc.cratemod().fns()[0].desc() == some("test");
}
#[test]
fn should_extract_const_docs() {
let doc = test::mk_doc("#[doc(brief = \"foo\", desc = \"bar\")]\
const a: bool = true;");
assert doc.cratemod().consts()[0].brief() == some("foo");
assert doc.cratemod().consts()[0].desc() == some("bar");
}
fn fold_enum(
fold: fold::fold<astsrv::srv>,
doc: doc::enumdoc
@ -153,7 +144,7 @@ fn fold_enum(
{
variants: par::anymap(doc.variants) {|variant|
let attrs = astsrv::exec(srv) {|ctxt|
let desc = astsrv::exec(srv) {|ctxt|
alt check ctxt.ast_map.get(doc_id) {
ast_map::node_item(@{
node: ast::item_enum(ast_variants, _), _
@ -163,13 +154,13 @@ fn fold_enum(
v.node.name == variant.name
});
attr_parser::parse_variant(ast_variant.node.attrs)
attr_parser::parse_desc(ast_variant.node.attrs)
}
}
};
{
desc: attrs.desc
desc: desc
with variant
}
}
@ -179,9 +170,8 @@ fn fold_enum(
#[test]
fn should_extract_enum_docs() {
let doc = test::mk_doc("#[doc(brief = \"a\", desc = \"b\")]\
let doc = test::mk_doc("#[doc = \"b\"]\
enum a { v }");
assert doc.cratemod().enums()[0].brief() == some("a");
assert doc.cratemod().enums()[0].desc() == some("b");
}
@ -211,20 +201,20 @@ fn merge_method_attrs(
) -> [doc::methoddoc] {
// Create an assoc list from method name to attributes
let attrs: [(str, attr_parser::basic_attrs)] = astsrv::exec(srv) {|ctxt|
let attrs: [(str, option<str>)] = astsrv::exec(srv) {|ctxt|
alt ctxt.ast_map.get(item_id) {
ast_map::node_item(@{
node: ast::item_iface(_, methods), _
}, _) {
par::seqmap(methods) {|method|
(method.ident, attr_parser::parse_basic(method.attrs))
(method.ident, attr_parser::parse_desc(method.attrs))
}
}
ast_map::node_item(@{
node: ast::item_impl(_, _, _, methods), _
}, _) {
par::seqmap(methods) {|method|
(method.ident, attr_parser::parse_basic(method.attrs))
(method.ident, attr_parser::parse_desc(method.attrs))
}
}
_ { fail "unexpected item" }
@ -233,11 +223,10 @@ fn merge_method_attrs(
vec::map2(docs, attrs) {|doc, attrs|
assert doc.name == tuple::first(attrs);
let basic_attrs = tuple::second(attrs);
let desc = tuple::second(attrs);
{
brief: basic_attrs.brief,
desc: basic_attrs.desc
desc: desc
with doc
}
}
@ -290,15 +279,6 @@ fn should_extract_impl_method_docs() {
assert doc.cratemod().impls()[0].methods[0].desc == some("desc");
}
#[test]
fn should_extract_type_docs() {
let doc = test::mk_doc(
"#[doc(brief = \"brief\", desc = \"desc\")]\
type t = int;");
assert doc.cratemod().types()[0].brief() == some("brief");
assert doc.cratemod().types()[0].desc() == some("desc");
}
#[cfg(test)]
mod test {
fn mk_doc(source: str) -> doc::doc {

View File

@ -145,7 +145,7 @@ fn should_index_mod_contents_multi_page() {
fn should_add_brief_desc_to_index() {
let doc = test::mk_doc(
config::doc_per_mod,
"#[doc(brief = \"test\")] mod a { }"
"#[doc = \"test\"] mod a { }"
);
assert option::get(doc.cratemod().index).entries[0].brief == some("test");
}
@ -160,6 +160,7 @@ mod test {
};
let doc = extract::from_srv(srv, "");
let doc = attr_pass::mk_pass().f(srv, doc);
let doc = desc_to_brief_pass::mk_pass().f(srv, doc);
let doc = path_pass::mk_pass().f(srv, doc);
run(srv, doc, config)
}

View File

@ -347,7 +347,7 @@ fn should_write_index() {
#[test]
fn should_write_index_brief() {
let markdown = test::render("#[doc(brief = \"test\")] mod a { }");
let markdown = test::render("#[doc = \"test\"] mod a { }");
assert str::contains(markdown, "(#module-a) - test\n");
}
@ -617,7 +617,7 @@ fn should_write_iface_header() {
#[test]
fn should_write_iface_desc() {
let markdown = test::render(
"#[doc(desc = \"desc\")] iface i { fn a(); }");
"#[doc = \"desc\"] iface i { fn a(); }");
assert str::contains(markdown, "desc");
}
@ -656,7 +656,7 @@ fn should_write_impl_header_with_iface() {
#[test]
fn should_write_impl_desc() {
let markdown = test::render(
"#[doc(desc = \"desc\")] impl i for int { fn a() { } }");
"#[doc = \"desc\"] impl i for int { fn a() { } }");
assert str::contains(markdown, "desc");
}
@ -692,7 +692,7 @@ fn should_write_type_header() {
#[test]
fn should_write_type_desc() {
let markdown = test::render(
"#[doc(desc = \"desc\")] type t = int;");
"#[doc = \"desc\"] type t = int;");
assert str::contains(markdown, "\n\ndesc\n\n");
}
@ -727,6 +727,8 @@ mod test {
#debug("doc (path): %?", doc);
let doc = attr_pass::mk_pass().f(srv, doc);
#debug("doc (attr): %?", doc);
let doc = desc_to_brief_pass::mk_pass().f(srv, doc);
#debug("doc (desc_to_brief): %?", doc);
let doc = unindent_pass::mk_pass().f(srv, doc);
#debug("doc (unindent): %?", doc);
let doc = sectionalize_pass::mk_pass().f(srv, doc);

View File

@ -98,13 +98,13 @@ fn fold_impl(fold: fold::fold<op>, doc: doc::impldoc) -> doc::impldoc {
#[test]
fn should_execute_op_on_enum_brief() {
let doc = test::mk_doc("#[doc(brief = \" a \")] enum a { b }");
let doc = test::mk_doc("#[doc = \" a \"] enum a { b }");
assert doc.cratemod().enums()[0].brief() == some("a");
}
#[test]
fn should_execute_op_on_enum_desc() {
let doc = test::mk_doc("#[doc(desc = \" a \")] enum a { b }");
let doc = test::mk_doc("#[doc = \" a \"] enum a { b }");
assert doc.cratemod().enums()[0].desc() == some("a");
}
@ -116,83 +116,83 @@ fn should_execute_op_on_variant_desc() {
#[test]
fn should_execute_op_on_resource_brief() {
let doc = test::mk_doc("#[doc(brief = \" a \")] resource r(a: bool) { }");
let doc = test::mk_doc("#[doc = \" a \"] resource r(a: bool) { }");
assert doc.cratemod().resources()[0].brief() == some("a");
}
#[test]
fn should_execute_op_on_resource_desc() {
let doc = test::mk_doc("#[doc(desc = \" a \")] resource r(a: bool) { }");
let doc = test::mk_doc("#[doc = \" a \"] resource r(a: bool) { }");
assert doc.cratemod().resources()[0].desc() == some("a");
}
#[test]
fn should_execute_op_on_iface_brief() {
let doc = test::mk_doc(
"#[doc(brief = \" a \")] iface i { fn a(); }");
"#[doc = \" a \"] iface i { fn a(); }");
assert doc.cratemod().ifaces()[0].brief() == some("a");
}
#[test]
fn should_execute_op_on_iface_desc() {
let doc = test::mk_doc(
"#[doc(desc = \" a \")] iface i { fn a(); }");
"#[doc = \" a \"] iface i { fn a(); }");
assert doc.cratemod().ifaces()[0].desc() == some("a");
}
#[test]
fn should_execute_op_on_iface_method_brief() {
let doc = test::mk_doc(
"iface i { #[doc(brief = \" a \")] fn a(); }");
"iface i { #[doc = \" a \"] fn a(); }");
assert doc.cratemod().ifaces()[0].methods[0].brief == some("a");
}
#[test]
fn should_execute_op_on_iface_method_desc() {
let doc = test::mk_doc(
"iface i { #[doc(desc = \" a \")] fn a(); }");
"iface i { #[doc = \" a \"] fn a(); }");
assert doc.cratemod().ifaces()[0].methods[0].desc == some("a");
}
#[test]
fn should_execute_op_on_impl_brief() {
let doc = test::mk_doc(
"#[doc(brief = \" a \")] impl i for int { fn a() { } }");
"#[doc = \" a \"] impl i for int { fn a() { } }");
assert doc.cratemod().impls()[0].brief() == some("a");
}
#[test]
fn should_execute_op_on_impl_desc() {
let doc = test::mk_doc(
"#[doc(desc = \" a \")] impl i for int { fn a() { } }");
"#[doc = \" a \"] impl i for int { fn a() { } }");
assert doc.cratemod().impls()[0].desc() == some("a");
}
#[test]
fn should_execute_op_on_impl_method_brief() {
let doc = test::mk_doc(
"impl i for int { #[doc(brief = \" a \")] fn a() { } }");
"impl i for int { #[doc = \" a \"] fn a() { } }");
assert doc.cratemod().impls()[0].methods[0].brief == some("a");
}
#[test]
fn should_execute_op_on_impl_method_desc() {
let doc = test::mk_doc(
"impl i for int { #[doc(desc = \" a \")] fn a() { } }");
"impl i for int { #[doc = \" a \"] fn a() { } }");
assert doc.cratemod().impls()[0].methods[0].desc == some("a");
}
#[test]
fn should_execute_op_on_type_brief() {
let doc = test::mk_doc(
"#[doc(brief = \" a \")] type t = int;");
"#[doc = \" a \"] type t = int;");
assert doc.cratemod().types()[0].brief() == some("a");
}
#[test]
fn should_execute_op_on_type_desc() {
let doc = test::mk_doc(
"#[doc(desc = \" a \")] type t = int;");
"#[doc = \" a \"] type t = int;");
assert doc.cratemod().types()[0].desc() == some("a");
}
@ -268,6 +268,7 @@ mod test {
astsrv::from_str(source) {|srv|
let doc = extract::from_srv(srv, "");
let doc = attr_pass::mk_pass().f(srv, doc);
let doc = desc_to_brief_pass::mk_pass().f(srv, doc);
let doc = sectionalize_pass::mk_pass().f(srv, doc);
mk_pass("", {|s| str::trim(s)}).f(srv, doc)
}

View File

@ -14,32 +14,12 @@ fn mk_pass() -> pass {
}
#[test]
fn should_trim_mod() {
let doc = test::mk_doc("#[doc(brief = \"\nbrief\n\", \
desc = \"\ndesc\n\")] \
fn should_trim_text() {
let doc = test::mk_doc("#[doc = \" desc \"] \
mod m { }");
assert doc.cratemod().mods()[0].brief() == some("brief");
assert doc.cratemod().mods()[0].desc() == some("desc");
}
#[test]
fn should_trim_const() {
let doc = test::mk_doc("#[doc(brief = \"\nbrief\n\", \
desc = \"\ndesc\n\")] \
const a: bool = true;");
assert doc.cratemod().consts()[0].brief() == some("brief");
assert doc.cratemod().consts()[0].desc() == some("desc");
}
#[test]
fn should_trim_fn() {
let doc = test::mk_doc("#[doc(brief = \"\nbrief\n\", \
desc = \"\ndesc\n\")] \
fn a() { }");
assert doc.cratemod().fns()[0].brief() == some("brief");
assert doc.cratemod().fns()[0].desc() == some("desc");
}
#[cfg(test)]
mod test {
fn mk_doc(source: str) -> doc::doc {