rustdoc: Make rustdoc no longer resolve names.
It relies on being able to ignore errors, which doesn't make sense; resolve is meaningless if errors are ignored. It also doesn't perform macro expansion. Most of what is affected will be reexports, which are changing anyway.
This commit is contained in:
parent
30d94fe9a2
commit
df7ce33b3d
5 changed files with 11 additions and 852 deletions
|
@ -20,8 +20,6 @@ import syntax::ast_map;
|
|||
import rustc::back::link;
|
||||
import rustc::metadata::filesearch;
|
||||
import rustc::front;
|
||||
import rustc::middle::resolve;
|
||||
import rustc::middle::resolve3;
|
||||
|
||||
export ctxt;
|
||||
export ctxt_handler;
|
||||
|
@ -32,9 +30,7 @@ export exec;
|
|||
|
||||
type ctxt = {
|
||||
ast: @ast::crate,
|
||||
ast_map: ast_map::map,
|
||||
exp_map: resolve::exp_map,
|
||||
impl_map: resolve::impl_map
|
||||
ast_map: ast_map::map
|
||||
};
|
||||
|
||||
type srv_owner<T> = fn(srv: srv) -> T;
|
||||
|
@ -72,12 +68,11 @@ fn run<T>(owner: srv_owner<T>, source: ~str, +parse: parser) -> T {
|
|||
}
|
||||
|
||||
fn act(po: comm::port<msg>, source: ~str, parse: parser) {
|
||||
let (sess, ignore_errors) = build_session();
|
||||
let sess = build_session();
|
||||
|
||||
let ctxt = build_ctxt(
|
||||
sess,
|
||||
parse(sess, source),
|
||||
ignore_errors
|
||||
parse(sess, source)
|
||||
);
|
||||
|
||||
let mut keep_going = true;
|
||||
|
@ -107,41 +102,34 @@ fn exec<T:send>(
|
|||
}
|
||||
|
||||
fn build_ctxt(sess: session,
|
||||
ast: @ast::crate,
|
||||
ignore_errors: @mut bool) -> ctxt {
|
||||
ast: @ast::crate) -> ctxt {
|
||||
|
||||
import rustc::front::config;
|
||||
|
||||
let ast = config::strip_unconfigured_items(ast);
|
||||
let ast = front::test::modify_for_testing(sess, ast);
|
||||
let ast_map = ast_map::map_crate(sess.diagnostic(), *ast);
|
||||
*ignore_errors = true;
|
||||
let {exp_map, impl_map, _} = resolve3::resolve_crate(sess, ast_map, ast);
|
||||
*ignore_errors = false;
|
||||
|
||||
{
|
||||
ast: ast,
|
||||
ast_map: ast_map,
|
||||
exp_map: exp_map,
|
||||
impl_map: impl_map
|
||||
}
|
||||
}
|
||||
|
||||
fn build_session() -> (session, @mut bool) {
|
||||
fn build_session() -> session {
|
||||
let sopts: @options = basic_options();
|
||||
let codemap = codemap::new_codemap();
|
||||
let error_handlers = build_error_handlers(codemap);
|
||||
let {emitter, span_handler, ignore_errors} = error_handlers;
|
||||
let {emitter, span_handler} = error_handlers;
|
||||
|
||||
let session = driver::build_session_(sopts, codemap, emitter,
|
||||
span_handler);
|
||||
(session, ignore_errors)
|
||||
session
|
||||
}
|
||||
|
||||
type error_handlers = {
|
||||
emitter: diagnostic::emitter,
|
||||
span_handler: diagnostic::span_handler,
|
||||
ignore_errors: @mut bool
|
||||
span_handler: diagnostic::span_handler
|
||||
};
|
||||
|
||||
// Build a custom error handler that will allow us to ignore non-fatal
|
||||
|
@ -152,16 +140,13 @@ fn build_error_handlers(
|
|||
|
||||
type diagnostic_handler = {
|
||||
inner: diagnostic::handler,
|
||||
ignore_errors: @mut bool
|
||||
};
|
||||
|
||||
impl of diagnostic::handler for diagnostic_handler {
|
||||
fn fatal(msg: ~str) -> ! { self.inner.fatal(msg) }
|
||||
fn err(msg: ~str) { self.inner.err(msg) }
|
||||
fn bump_err_count() {
|
||||
if !(*self.ignore_errors) {
|
||||
self.inner.bump_err_count();
|
||||
}
|
||||
self.inner.bump_err_count();
|
||||
}
|
||||
fn has_errors() -> bool { self.inner.has_errors() }
|
||||
fn abort_if_errors() { self.inner.abort_if_errors() }
|
||||
|
@ -175,25 +160,20 @@ fn build_error_handlers(
|
|||
}
|
||||
}
|
||||
|
||||
let ignore_errors = @mut false;
|
||||
let emitter = fn@(cmsp: option<(codemap::codemap, codemap::span)>,
|
||||
msg: ~str, lvl: diagnostic::level) {
|
||||
if !(*ignore_errors) {
|
||||
diagnostic::emit(cmsp, msg, lvl);
|
||||
}
|
||||
diagnostic::emit(cmsp, msg, lvl);
|
||||
};
|
||||
let inner_handler = diagnostic::mk_handler(some(emitter));
|
||||
let handler = {
|
||||
inner: inner_handler,
|
||||
ignore_errors: ignore_errors
|
||||
};
|
||||
let span_handler = diagnostic::mk_span_handler(
|
||||
handler as diagnostic::handler, codemap);
|
||||
|
||||
{
|
||||
emitter: emitter,
|
||||
span_handler: span_handler,
|
||||
ignore_errors: ignore_errors
|
||||
span_handler: span_handler
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,48 +197,6 @@ fn srv_should_build_ast_map() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn srv_should_build_reexport_map() {
|
||||
let source = ~"import a::b; export b; mod a { mod b { } }";
|
||||
do from_str(source) |srv| {
|
||||
do exec(srv) |ctxt| {
|
||||
assert ctxt.exp_map.size() != 0u
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn srv_should_resolve_external_crates() {
|
||||
let source = ~"use std;\
|
||||
fn f() -> std::sha1::sha1 {\
|
||||
std::sha1::mk_sha1() }";
|
||||
// Just testing that resolve doesn't crash
|
||||
from_str(source, |_srv| { } )
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn srv_should_resolve_core_crate() {
|
||||
let source = ~"fn a() -> option { fail }";
|
||||
// Just testing that resolve doesn't crash
|
||||
from_str(source, |_srv| { } )
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn srv_should_resolve_non_existant_imports() {
|
||||
// XXX: XFAIL'd
|
||||
|
||||
// We want to ignore things we can't resolve. Shouldn't
|
||||
// need to be able to find external crates to create docs.
|
||||
//let source = ~"import wooboo; fn a() { }";
|
||||
//from_str(source, |_srv| { } )
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn srv_should_resolve_non_existant_uses() {
|
||||
let source = ~"use forble; fn a() { }";
|
||||
from_str(source, |_srv| { } )
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_ignore_external_import_paths_that_dont_exist() {
|
||||
let source = ~"use forble; import forble::bippy;";
|
||||
|
|
|
@ -1,247 +0,0 @@
|
|||
//! Prunes branches of the tree that are not exported
|
||||
|
||||
import doc::item_utils;
|
||||
import syntax::ast;
|
||||
import syntax::ast_util;
|
||||
import syntax::ast_map;
|
||||
import std::map::hashmap;
|
||||
|
||||
export mk_pass;
|
||||
|
||||
fn mk_pass() -> pass {
|
||||
{
|
||||
name: ~"prune_unexported",
|
||||
f: run
|
||||
}
|
||||
}
|
||||
|
||||
fn run(srv: astsrv::srv, doc: doc::doc) -> doc::doc {
|
||||
let fold = fold::fold({
|
||||
fold_mod: fold_mod
|
||||
with *fold::default_any_fold(srv)
|
||||
});
|
||||
fold.fold_doc(fold, doc)
|
||||
}
|
||||
|
||||
fn fold_mod(fold: fold::fold<astsrv::srv>, doc: doc::moddoc) -> doc::moddoc {
|
||||
let doc = fold::default_any_fold_mod(fold, doc);
|
||||
doc::moddoc_({
|
||||
items: exported_items(fold.ctxt, doc)
|
||||
with *doc
|
||||
})
|
||||
}
|
||||
|
||||
fn exported_items(srv: astsrv::srv, doc: doc::moddoc) -> ~[doc::itemtag] {
|
||||
exported_things(
|
||||
srv, doc,
|
||||
exported_items_from_crate,
|
||||
exported_items_from_mod
|
||||
)
|
||||
}
|
||||
|
||||
fn exported_things<T>(
|
||||
srv: astsrv::srv,
|
||||
doc: doc::moddoc,
|
||||
from_crate: fn(astsrv::srv, doc::moddoc) -> ~[T],
|
||||
from_mod: fn(astsrv::srv, doc::moddoc) -> ~[T]
|
||||
) -> ~[T] {
|
||||
if doc.id() == ast::crate_node_id {
|
||||
from_crate(srv, doc)
|
||||
} else {
|
||||
from_mod(srv, doc)
|
||||
}
|
||||
}
|
||||
|
||||
fn exported_items_from_crate(
|
||||
srv: astsrv::srv,
|
||||
doc: doc::moddoc
|
||||
) -> ~[doc::itemtag] {
|
||||
exported_items_from(srv, doc, is_exported_from_crate)
|
||||
}
|
||||
|
||||
fn exported_items_from_mod(
|
||||
srv: astsrv::srv,
|
||||
doc: doc::moddoc
|
||||
) -> ~[doc::itemtag] {
|
||||
exported_items_from(srv, doc, |a,b| {
|
||||
is_exported_from_mod(a, doc.id(), b)
|
||||
})
|
||||
}
|
||||
|
||||
fn exported_items_from(
|
||||
srv: astsrv::srv,
|
||||
doc: doc::moddoc,
|
||||
is_exported: fn(astsrv::srv, ~str) -> bool
|
||||
) -> ~[doc::itemtag] {
|
||||
do vec::filter_map(doc.items) |itemtag| {
|
||||
let itemtag = alt itemtag {
|
||||
doc::enumtag(enumdoc) {
|
||||
// Also need to check variant exportedness
|
||||
doc::enumtag({
|
||||
variants: exported_variants_from(srv, enumdoc, is_exported)
|
||||
with enumdoc
|
||||
})
|
||||
}
|
||||
_ { itemtag }
|
||||
};
|
||||
|
||||
if itemtag.item().reexport || is_exported(srv, itemtag.name()) {
|
||||
some(itemtag)
|
||||
} else {
|
||||
none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn exported_variants_from(
|
||||
srv: astsrv::srv,
|
||||
doc: doc::enumdoc,
|
||||
is_exported: fn(astsrv::srv, ~str) -> bool
|
||||
) -> ~[doc::variantdoc] {
|
||||
do vec::filter_map(doc.variants) |doc| {
|
||||
if is_exported(srv, doc.name) {
|
||||
some(doc)
|
||||
} else {
|
||||
none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_exported_from_mod(
|
||||
srv: astsrv::srv,
|
||||
mod_id: doc::ast_id,
|
||||
item_name: ~str
|
||||
) -> bool {
|
||||
do astsrv::exec(srv) |ctxt| {
|
||||
alt ctxt.ast_map.get(mod_id) {
|
||||
ast_map::node_item(item, _) {
|
||||
alt item.node {
|
||||
ast::item_mod(m) {
|
||||
ast_util::is_exported(@item_name, m)
|
||||
}
|
||||
_ {
|
||||
fail ~"is_exported_from_mod: not a mod";
|
||||
}
|
||||
}
|
||||
}
|
||||
_ { fail ~"is_exported_from_mod: not an item"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_exported_from_crate(
|
||||
srv: astsrv::srv,
|
||||
item_name: ~str
|
||||
) -> bool {
|
||||
do astsrv::exec(srv) |ctxt| {
|
||||
ast_util::is_exported(@item_name, ctxt.ast.node.module)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_unexported_fns() {
|
||||
let doc = test::mk_doc(~"mod b { export a; fn a() { } fn b() { } }");
|
||||
assert vec::len(doc.cratemod().mods()[0].fns()) == 1u;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_unexported_fns_from_top_mod() {
|
||||
let doc = test::mk_doc(~"export a; fn a() { } fn b() { }");
|
||||
assert vec::len(doc.cratemod().fns()) == 1u;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_unexported_modules() {
|
||||
let doc = test::mk_doc(~"mod a { export a; mod a { } mod b { } }");
|
||||
assert vec::len(doc.cratemod().mods()[0].mods()) == 1u;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_unexported_modules_from_top_mod() {
|
||||
let doc = test::mk_doc(~"export a; mod a { } mod b { }");
|
||||
assert vec::len(doc.cratemod().mods()) == 1u;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_unexported_consts() {
|
||||
let doc = test::mk_doc(
|
||||
~"mod a { export a; \
|
||||
const a: bool = true; \
|
||||
const b: bool = true; }");
|
||||
assert vec::len(doc.cratemod().mods()[0].consts()) == 1u;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_unexported_consts_from_top_mod() {
|
||||
let doc = test::mk_doc(
|
||||
~"export a; const a: bool = true; const b: bool = true;");
|
||||
assert vec::len(doc.cratemod().consts()) == 1u;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_unexported_enums_from_top_mod() {
|
||||
let doc = test::mk_doc(~"export a; mod a { } enum b { c }");
|
||||
assert vec::len(doc.cratemod().enums()) == 0u;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_unexported_enums() {
|
||||
let doc = test::mk_doc(~"mod a { export a; mod a { } enum b { c } }");
|
||||
assert vec::len(doc.cratemod().mods()[0].enums()) == 0u;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_unexported_variants_from_top_mod() {
|
||||
let doc = test::mk_doc(~"export b::{}; enum b { c }");
|
||||
assert vec::len(doc.cratemod().enums()[0].variants) == 0u;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_unexported_variants() {
|
||||
let doc = test::mk_doc(~"mod a { export b::{}; enum b { c } }");
|
||||
assert vec::len(doc.cratemod().mods()[0].enums()[0].variants) == 0u;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_unexported_traits_from_top_mod() {
|
||||
let doc = test::mk_doc(~"export a; mod a { } iface b { fn c(); }");
|
||||
assert vec::is_empty(doc.cratemod().traits());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_unexported_impls_from_top_mod() {
|
||||
let doc = test::mk_doc(
|
||||
~"export a; mod a { } impl b for int { fn c() { } }");
|
||||
assert vec::is_empty(doc.cratemod().impls())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_unexported_types() {
|
||||
let doc = test::mk_doc(~"export a; mod a { } type b = int;");
|
||||
assert vec::is_empty(doc.cratemod().types());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_prune_reexports() {
|
||||
fn mk_doc(source: ~str) -> doc::doc {
|
||||
do astsrv::from_str(source) |srv| {
|
||||
let doc = extract::from_srv(srv, ~"");
|
||||
let doc = reexport_pass::mk_pass().f(srv, doc);
|
||||
run(srv, doc)
|
||||
}
|
||||
}
|
||||
let doc = mk_doc(~"import a::b; \
|
||||
export b; \
|
||||
mod a { fn b() { } }");
|
||||
assert vec::is_not_empty(doc.cratemod().fns());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
fn mk_doc(source: ~str) -> doc::doc {
|
||||
do astsrv::from_str(source) |srv| {
|
||||
let doc = extract::from_srv(srv, ~"");
|
||||
run(srv, doc)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,528 +0,0 @@
|
|||
//! Finds docs for reexported items and duplicates them
|
||||
|
||||
import doc::item_utils;
|
||||
import std::map;
|
||||
import std::map::hashmap;
|
||||
import std::list;
|
||||
import syntax::ast;
|
||||
import syntax::ast_util;
|
||||
import syntax::ast_map;
|
||||
import syntax::visit;
|
||||
import syntax::codemap;
|
||||
import rustc::middle::resolve;
|
||||
|
||||
export mk_pass;
|
||||
|
||||
fn mk_pass() -> pass {
|
||||
{
|
||||
name: ~"reexport",
|
||||
f: run
|
||||
}
|
||||
}
|
||||
|
||||
type def_set = map::set<ast::def_id>;
|
||||
type def_map = map::hashmap<ast::def_id, doc::itemtag>;
|
||||
type path_map = map::hashmap<~str, ~[(~str, doc::itemtag)]>;
|
||||
|
||||
fn run(srv: astsrv::srv, doc: doc::doc) -> doc::doc {
|
||||
|
||||
// First gather the set of defs that are used as reexports
|
||||
let def_set = build_reexport_def_set(srv);
|
||||
|
||||
// Now find the docs that go with those defs
|
||||
let def_map = build_reexport_def_map(srv, doc, def_set);
|
||||
|
||||
// Now create a map that tells us where to insert the duplicated
|
||||
// docs into the existing doc tree
|
||||
let path_map = build_reexport_path_map(srv, def_map);
|
||||
|
||||
// Finally update the doc tree
|
||||
merge_reexports(doc, path_map)
|
||||
}
|
||||
|
||||
// Hash maps are not sendable so converting them back and forth
|
||||
// to association lists. Yuck.
|
||||
fn to_assoc_list<K:copy, V:copy>(
|
||||
map: map::hashmap<K, V>
|
||||
) -> ~[(K, V)] {
|
||||
|
||||
let mut vec = ~[];
|
||||
for map.each |k, v| {
|
||||
vec += ~[(k, v)];
|
||||
}
|
||||
ret vec;
|
||||
}
|
||||
|
||||
fn from_assoc_list<K:copy, V:copy>(
|
||||
list: ~[(K, V)],
|
||||
new_hash: fn() -> map::hashmap<K, V>
|
||||
) -> map::hashmap<K, V> {
|
||||
|
||||
let map = new_hash();
|
||||
do vec::iter(list) |elt| {
|
||||
let (k, v) = elt;
|
||||
map.insert(k, v);
|
||||
}
|
||||
ret map;
|
||||
}
|
||||
|
||||
fn from_def_assoc_list<V:copy>(
|
||||
list: ~[(ast::def_id, V)]
|
||||
) -> map::hashmap<ast::def_id, V> {
|
||||
from_assoc_list(list, ast_util::new_def_hash)
|
||||
}
|
||||
|
||||
fn from_str_assoc_list<V:copy>(
|
||||
list: ~[(~str, V)]
|
||||
) -> map::hashmap<~str, V> {
|
||||
from_assoc_list(list, map::str_hash)
|
||||
}
|
||||
|
||||
fn build_reexport_def_set(srv: astsrv::srv) -> def_set {
|
||||
let assoc_list = do astsrv::exec(srv) |ctxt| {
|
||||
let def_set = ast_util::new_def_hash();
|
||||
for ctxt.exp_map.each |_id, defs| {
|
||||
for defs.each |def| {
|
||||
if def.reexp {
|
||||
def_set.insert(def.id, ());
|
||||
}
|
||||
}
|
||||
}
|
||||
for find_reexport_impls(ctxt).each |def| {
|
||||
def_set.insert(def, ());
|
||||
}
|
||||
to_assoc_list(def_set)
|
||||
};
|
||||
|
||||
from_def_assoc_list(assoc_list)
|
||||
}
|
||||
|
||||
fn find_reexport_impls(ctxt: astsrv::ctxt) -> ~[ast::def_id] {
|
||||
let defs = @mut ~[];
|
||||
do for_each_reexported_impl(ctxt) |_mod_id, i| {
|
||||
*defs += ~[i.did]
|
||||
}
|
||||
ret *defs;
|
||||
}
|
||||
|
||||
fn build_reexport_def_map(
|
||||
srv: astsrv::srv,
|
||||
doc: doc::doc,
|
||||
def_set: def_set
|
||||
) -> def_map {
|
||||
|
||||
type ctxt = {
|
||||
srv: astsrv::srv,
|
||||
def_set: def_set,
|
||||
def_map: def_map
|
||||
};
|
||||
|
||||
let ctxt = {
|
||||
srv: srv,
|
||||
def_set: def_set,
|
||||
def_map: ast_util::new_def_hash()
|
||||
};
|
||||
|
||||
// FIXME: Do a parallel fold (#2597)
|
||||
let fold = fold::fold({
|
||||
fold_mod: fold_mod,
|
||||
fold_nmod: fold_nmod
|
||||
with *fold::default_seq_fold(ctxt)
|
||||
});
|
||||
|
||||
fold.fold_doc(fold, doc);
|
||||
|
||||
ret ctxt.def_map;
|
||||
|
||||
fn fold_mod(fold: fold::fold<ctxt>, doc: doc::moddoc) -> doc::moddoc {
|
||||
let doc = fold::default_seq_fold_mod(fold, doc);
|
||||
|
||||
for doc.items.each |item| {
|
||||
let def_id = ast_util::local_def(item.id());
|
||||
if fold.ctxt.def_set.contains_key(def_id) {
|
||||
fold.ctxt.def_map.insert(def_id, item);
|
||||
}
|
||||
}
|
||||
|
||||
ret doc;
|
||||
}
|
||||
|
||||
fn fold_nmod(fold: fold::fold<ctxt>, doc: doc::nmoddoc) -> doc::nmoddoc {
|
||||
let doc = fold::default_seq_fold_nmod(fold, doc);
|
||||
|
||||
for doc.fns.each |fndoc| {
|
||||
let def_id = ast_util::local_def(fndoc.id());
|
||||
if fold.ctxt.def_set.contains_key(def_id) {
|
||||
fold.ctxt.def_map.insert(def_id, doc::fntag(fndoc));
|
||||
}
|
||||
}
|
||||
|
||||
ret doc;
|
||||
}
|
||||
}
|
||||
|
||||
fn build_reexport_path_map(srv: astsrv::srv, -def_map: def_map) -> path_map {
|
||||
|
||||
// This is real unfortunate. Lots of copying going on here
|
||||
let def_assoc_list = to_assoc_list(def_map);
|
||||
#debug("def_map: %?", def_assoc_list);
|
||||
|
||||
let assoc_list = do astsrv::exec(srv) |ctxt| {
|
||||
|
||||
let def_map = from_def_assoc_list(def_assoc_list);
|
||||
let path_map = map::str_hash::<~[(~str,doc::itemtag)]>();
|
||||
|
||||
for ctxt.exp_map.each |exp_id, defs| {
|
||||
let path = alt check ctxt.ast_map.get(exp_id) {
|
||||
ast_map::node_export(_, path) { path }
|
||||
};
|
||||
// should be a constraint on the node_export constructor
|
||||
// that guarantees path is non-empty
|
||||
let name = alt check vec::last(*path) {
|
||||
ast_map::path_name(nm) { nm }
|
||||
};
|
||||
let modpath = ast_map::path_to_str(vec::init(*path));
|
||||
|
||||
let mut reexportdocs = ~[];
|
||||
for defs.each |def| {
|
||||
if !def.reexp { again; }
|
||||
alt def_map.find(def.id) {
|
||||
some(itemtag) {
|
||||
reexportdocs += ~[(*name, itemtag)];
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
}
|
||||
|
||||
if reexportdocs.len() > 0u {
|
||||
do option::iter(path_map.find(modpath)) |docs| {
|
||||
reexportdocs = docs + vec::filter(reexportdocs, |x| {
|
||||
!vec::contains(docs, x)
|
||||
});
|
||||
}
|
||||
path_map.insert(modpath, reexportdocs);
|
||||
#debug("path_map entry: %? - %?",
|
||||
modpath, (name, reexportdocs));
|
||||
}
|
||||
}
|
||||
|
||||
for find_reexport_impl_docs(ctxt, def_map).each |elt| {
|
||||
let (path, doc) = elt;
|
||||
let docs = alt path_map.find(path) {
|
||||
some(docs) { docs + ~[(doc)] }
|
||||
none { ~[doc] }
|
||||
};
|
||||
path_map.insert(path, docs);
|
||||
}
|
||||
|
||||
to_assoc_list(path_map)
|
||||
};
|
||||
|
||||
from_str_assoc_list(assoc_list)
|
||||
}
|
||||
|
||||
fn find_reexport_impl_docs(
|
||||
ctxt: astsrv::ctxt,
|
||||
def_map: def_map
|
||||
) -> ~[(~str, (~str, doc::itemtag))] {
|
||||
let docs = @mut ~[];
|
||||
|
||||
do for_each_reexported_impl(ctxt) |mod_id, i| {
|
||||
let path = alt ctxt.ast_map.find(mod_id) {
|
||||
some(ast_map::node_item(item, path)) {
|
||||
let path = ast_map::path_to_str(*path);
|
||||
if str::is_empty(path) {
|
||||
*item.ident
|
||||
} else {
|
||||
path + ~"::" + *item.ident
|
||||
}
|
||||
}
|
||||
_ {
|
||||
assert mod_id == ast::crate_node_id;
|
||||
~""
|
||||
}
|
||||
};
|
||||
let ident = *i.ident;
|
||||
let doc = alt check def_map.find(i.did) {
|
||||
some(doc) { doc }
|
||||
};
|
||||
*docs += ~[(path, (ident, doc))];
|
||||
}
|
||||
|
||||
ret *docs;
|
||||
}
|
||||
|
||||
fn for_each_reexported_impl(
|
||||
ctxt: astsrv::ctxt,
|
||||
f: fn@(ast::node_id, resolve::_impl)
|
||||
) {
|
||||
let visitor = @{
|
||||
visit_mod: |a,b,c| visit_mod(ctxt, f, a, b, c)
|
||||
with *visit::default_simple_visitor()
|
||||
};
|
||||
let visitor = visit::mk_simple_visitor(visitor);
|
||||
visit::visit_crate(*ctxt.ast, (), visitor);
|
||||
|
||||
fn visit_mod(
|
||||
ctxt: astsrv::ctxt,
|
||||
f: fn@(ast::node_id, resolve::_impl),
|
||||
m: ast::_mod,
|
||||
_sp: codemap::span,
|
||||
mod_id: ast::node_id
|
||||
) {
|
||||
let all_impls = all_impls(m);
|
||||
alt *ctxt.impl_map.get(mod_id) {
|
||||
list::cons(impls, _) {
|
||||
for vec::each(*impls) |i| {
|
||||
// This impl is not an item in the current mod
|
||||
if !all_impls.contains_key(i.did) {
|
||||
// Ignore external impls because I don't
|
||||
// know what to do with them yet
|
||||
if i.did.crate == ast::local_crate {
|
||||
f(mod_id, *i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
list::nil {
|
||||
// Do nothing -- mod has no impls
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn all_impls(m: ast::_mod) -> map::set<ast::def_id> {
|
||||
let all_impls = ast_util::new_def_hash();
|
||||
for m.items.each |item| {
|
||||
alt item.node {
|
||||
ast::item_impl(_, _, _, _) {
|
||||
all_impls.insert(ast_util::local_def(item.id), ());
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
}
|
||||
ret all_impls;
|
||||
}
|
||||
|
||||
fn merge_reexports(
|
||||
doc: doc::doc,
|
||||
path_map: path_map
|
||||
) -> doc::doc {
|
||||
|
||||
let fold = fold::fold({
|
||||
fold_mod: fold_mod
|
||||
with *fold::default_seq_fold(path_map)
|
||||
});
|
||||
|
||||
ret fold.fold_doc(fold, doc);
|
||||
|
||||
fn fold_mod(fold: fold::fold<path_map>, doc: doc::moddoc) -> doc::moddoc {
|
||||
let doc = fold::default_seq_fold_mod(fold, doc);
|
||||
|
||||
let is_topmod = doc.id() == ast::crate_node_id;
|
||||
|
||||
// In the case of the top mod, it really doesn't have a name;
|
||||
// the name we have here is actually the crate name
|
||||
let path = if is_topmod {
|
||||
doc.path()
|
||||
} else {
|
||||
doc.path() + ~[doc.name()]
|
||||
};
|
||||
|
||||
let new_items = get_new_items(path, fold.ctxt);
|
||||
#debug("merging into %?: %?", path, new_items);
|
||||
|
||||
doc::moddoc_({
|
||||
items: (doc.items + new_items)
|
||||
with *doc
|
||||
})
|
||||
}
|
||||
|
||||
fn get_new_items(path: ~[~str], path_map: path_map) -> ~[doc::itemtag] {
|
||||
#debug("looking for reexports in path %?", path);
|
||||
alt path_map.find(str::connect(path, ~"::")) {
|
||||
some(name_docs) {
|
||||
do vec::foldl(~[], name_docs) |v, name_doc| {
|
||||
let (name, doc) = name_doc;
|
||||
v + ~[reexport_doc(doc, name)]
|
||||
}
|
||||
}
|
||||
none { ~[] }
|
||||
}
|
||||
}
|
||||
|
||||
fn reexport_doc(doc: doc::itemtag, name: ~str) -> doc::itemtag {
|
||||
alt doc {
|
||||
doc::modtag(doc @ doc::moddoc_({item, _})) {
|
||||
doc::modtag(doc::moddoc_({
|
||||
item: reexport(item, name)
|
||||
with *doc
|
||||
}))
|
||||
}
|
||||
doc::nmodtag(_) { fail }
|
||||
doc::consttag(doc @ {item, _}) {
|
||||
doc::consttag({
|
||||
item: reexport(item, name)
|
||||
with doc
|
||||
})
|
||||
}
|
||||
doc::fntag(doc @ {item, _}) {
|
||||
doc::fntag({
|
||||
item: reexport(item, name)
|
||||
with doc
|
||||
})
|
||||
}
|
||||
doc::enumtag(doc @ {item, _}) {
|
||||
doc::enumtag({
|
||||
item: reexport(item, name)
|
||||
with doc
|
||||
})
|
||||
}
|
||||
doc::traittag(doc @ {item, _}) {
|
||||
doc::traittag({
|
||||
item: reexport(item, name)
|
||||
with doc
|
||||
})
|
||||
}
|
||||
doc::impltag(doc @ {item, _}) {
|
||||
doc::impltag({
|
||||
item: reexport(item, name)
|
||||
with doc
|
||||
})
|
||||
}
|
||||
doc::tytag(doc @ {item, _}) {
|
||||
doc::tytag({
|
||||
item: reexport(item, name)
|
||||
with doc
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reexport(doc: doc::itemdoc, name: ~str) -> doc::itemdoc {
|
||||
{
|
||||
name: name,
|
||||
reexport: true
|
||||
with doc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_duplicate_reexported_items() {
|
||||
let source = ~"mod a { export b; fn b() { } } \
|
||||
mod c { import a::b; export b; }";
|
||||
let doc = test::mk_doc(source);
|
||||
assert doc.cratemod().mods()[1].fns()[0].name() == ~"b";
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_mark_reepxorts_as_such() {
|
||||
let source = ~"mod a { export b; fn b() { } } \
|
||||
mod c { import a::b; export b; }";
|
||||
let doc = test::mk_doc(source);
|
||||
assert doc.cratemod().mods()[1].fns()[0].item.reexport == true;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_duplicate_reexported_impls() {
|
||||
let source = ~"mod a { impl b for int { fn c() { } } } \
|
||||
mod d { import a::b; export b; }";
|
||||
let doc = test::mk_doc(source);
|
||||
assert doc.cratemod().mods()[1].impls()[0].name() == ~"b";
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_duplicate_reexported_impls_deep() {
|
||||
let source = ~"mod a { impl b for int { fn c() { } } } \
|
||||
mod d { mod e { import a::b; export b; } }";
|
||||
let doc = test::mk_doc(source);
|
||||
assert doc.cratemod().mods()[1].mods()[0].impls()[0].name() == ~"b";
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_duplicate_reexported_impls_crate() {
|
||||
let source = ~"import a::b; export b; \
|
||||
mod a { impl b for int { fn c() { } } }";
|
||||
let doc = test::mk_doc(source);
|
||||
assert doc.cratemod().impls()[0].name() == ~"b";
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_duplicate_reexported_foreign_fns() {
|
||||
let source = ~"extern mod a { fn b(); } \
|
||||
mod c { import a::b; export b; }";
|
||||
let doc = test::mk_doc(source);
|
||||
assert doc.cratemod().mods()[0].fns()[0].name() == ~"b";
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_duplicate_multiple_reexported_items() {
|
||||
let source = ~"mod a { \
|
||||
export b; export c; \
|
||||
fn b() { } fn c() { } \
|
||||
} \
|
||||
mod d { \
|
||||
import a::b; import a::c; \
|
||||
export b; export c; \
|
||||
}";
|
||||
do astsrv::from_str(source) |srv| {
|
||||
let doc = extract::from_srv(srv, ~"");
|
||||
let doc = path_pass::mk_pass().f(srv, doc);
|
||||
let doc = run(srv, doc);
|
||||
// Reexports may not be in any specific order
|
||||
let doc = sort_item_name_pass::mk_pass().f(srv, doc);
|
||||
assert doc.cratemod().mods()[1].fns()[0].name() == ~"b";
|
||||
assert doc.cratemod().mods()[1].fns()[1].name() == ~"c";
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_rename_items_reexported_with_different_names() {
|
||||
let source = ~"mod a { export b; fn b() { } } \
|
||||
mod c { import x = a::b; export x; }";
|
||||
let doc = test::mk_doc(source);
|
||||
assert doc.cratemod().mods()[1].fns()[0].name() == ~"x";
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_reexport_in_topmod() {
|
||||
fn mk_doc(source: ~str) -> doc::doc {
|
||||
do astsrv::from_str(source) |srv| {
|
||||
let doc = extract::from_srv(srv, ~"core");
|
||||
let doc = path_pass::mk_pass().f(srv, doc);
|
||||
run(srv, doc)
|
||||
}
|
||||
}
|
||||
let source = ~"import option::{some, none}; \
|
||||
import option = option::t; \
|
||||
export option, some, none; \
|
||||
mod option { \
|
||||
enum t { some, none } \
|
||||
}";
|
||||
let doc = mk_doc(source);
|
||||
assert doc.cratemod().enums()[0].name() == ~"option";
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_reexport_multiple_times() {
|
||||
let source = ~"import option = option::t; \
|
||||
export option; \
|
||||
export option; \
|
||||
mod option { \
|
||||
enum t { none, some } \
|
||||
}";
|
||||
let doc = test::mk_doc(source);
|
||||
assert vec::len(doc.cratemod().enums()) == 1u;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
fn mk_doc(source: ~str) -> doc::doc {
|
||||
do astsrv::from_str(source) |srv| {
|
||||
let doc = extract::from_srv(srv, ~"");
|
||||
let doc = path_pass::mk_pass().f(srv, doc);
|
||||
run(srv, doc)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,7 +33,6 @@ mod fold;
|
|||
mod path_pass;
|
||||
mod attr_pass;
|
||||
mod tystr_pass;
|
||||
mod prune_unexported_pass;
|
||||
mod prune_hidden_pass;
|
||||
mod desc_to_brief_pass;
|
||||
mod text_pass;
|
||||
|
@ -44,7 +43,6 @@ mod demo;
|
|||
mod sort_pass;
|
||||
mod sort_item_name_pass;
|
||||
mod sort_item_type_pass;
|
||||
mod reexport_pass;
|
||||
mod page_pass;
|
||||
mod sectionalize_pass;
|
||||
mod escape_pass;
|
||||
|
|
|
@ -139,8 +139,6 @@ fn run(config: config::config) {
|
|||
extract::from_srv(srv, default_name)
|
||||
});
|
||||
run_passes(srv, doc, ~[
|
||||
reexport_pass::mk_pass(),
|
||||
prune_unexported_pass::mk_pass(),
|
||||
tystr_pass::mk_pass(),
|
||||
path_pass::mk_pass(),
|
||||
attr_pass::mk_pass(),
|
||||
|
|
Loading…
Reference in a new issue