2012-01-17 17:44:32 -08:00
|
|
|
#[doc(
|
|
|
|
brief = "Provides all access to AST-related, non-sendable info",
|
|
|
|
desc =
|
|
|
|
"Rustdoc is intended to be parallel, and the rustc AST is filled \
|
|
|
|
with shared boxes. The AST service attempts to provide a single \
|
|
|
|
place to query AST-related information, shielding the rest of \
|
|
|
|
Rustdoc from its non-sendableness."
|
|
|
|
)];
|
2012-01-16 16:55:26 -08:00
|
|
|
|
2012-03-07 16:48:57 -08:00
|
|
|
import std::map::hashmap;
|
2012-01-26 00:35:07 -08:00
|
|
|
import rustc::driver::session;
|
|
|
|
import rustc::driver::driver;
|
|
|
|
import rustc::driver::diagnostic;
|
2012-02-17 14:15:09 -08:00
|
|
|
import rustc::driver::diagnostic::handler;
|
2012-01-16 16:55:26 -08:00
|
|
|
import rustc::syntax::ast;
|
2012-02-17 14:15:09 -08:00
|
|
|
import rustc::syntax::codemap;
|
2012-01-16 16:55:26 -08:00
|
|
|
import rustc::middle::ast_map;
|
2012-01-26 00:35:07 -08:00
|
|
|
import rustc::back::link;
|
|
|
|
import rustc::util::filesearch;
|
|
|
|
import rustc::front;
|
2012-02-17 14:15:09 -08:00
|
|
|
import rustc::middle::resolve;
|
2012-01-16 16:55:26 -08:00
|
|
|
|
|
|
|
export ctxt;
|
|
|
|
export ctxt_handler;
|
2012-02-20 21:41:24 -08:00
|
|
|
export srv::{};
|
2012-02-20 20:44:33 -08:00
|
|
|
export from_str;
|
|
|
|
export from_file;
|
2012-01-17 16:12:31 -08:00
|
|
|
export exec;
|
2012-01-16 16:55:26 -08:00
|
|
|
|
|
|
|
type ctxt = {
|
|
|
|
ast: @ast::crate,
|
2012-02-17 14:15:09 -08:00
|
|
|
ast_map: ast_map::map,
|
2012-03-20 11:19:59 -07:00
|
|
|
exp_map: resolve::exp_map,
|
|
|
|
impl_map: resolve::impl_map
|
2012-01-16 16:55:26 -08:00
|
|
|
};
|
|
|
|
|
2012-02-20 21:08:19 -08:00
|
|
|
type srv_owner<T> = fn(srv: srv) -> T;
|
2012-01-16 16:55:26 -08:00
|
|
|
type ctxt_handler<T> = fn~(ctxt: ctxt) -> T;
|
2012-02-20 21:41:24 -08:00
|
|
|
type parser = fn~(session::session, str) -> @ast::crate;
|
2012-01-16 16:55:26 -08:00
|
|
|
|
2012-02-20 21:41:24 -08:00
|
|
|
enum msg {
|
|
|
|
handle_request(fn~(ctxt)),
|
|
|
|
exit
|
|
|
|
}
|
|
|
|
|
|
|
|
enum srv = {
|
|
|
|
ch: comm::chan<msg>
|
2012-01-17 16:12:31 -08:00
|
|
|
};
|
2012-01-16 16:55:26 -08:00
|
|
|
|
2012-02-20 21:08:19 -08:00
|
|
|
fn from_str<T>(source: str, owner: srv_owner<T>) -> T {
|
2012-02-20 21:41:24 -08:00
|
|
|
run(owner, source, parse::from_str_sess)
|
|
|
|
}
|
2012-02-20 21:08:19 -08:00
|
|
|
|
2012-02-20 21:41:24 -08:00
|
|
|
fn from_file<T>(file: str, owner: srv_owner<T>) -> T {
|
|
|
|
run(owner, file, parse::from_file_sess)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run<T>(owner: srv_owner<T>, source: str, parse: parser) -> T {
|
|
|
|
|
|
|
|
let srv_ = srv({
|
|
|
|
ch: task::spawn_listener {|po|
|
|
|
|
act(po, source, parse);
|
|
|
|
}
|
|
|
|
});
|
2012-02-20 21:08:19 -08:00
|
|
|
|
2012-02-20 21:41:24 -08:00
|
|
|
let res = owner(srv_);
|
|
|
|
comm::send(srv_.ch, exit);
|
|
|
|
ret res;
|
2012-01-16 16:55:26 -08:00
|
|
|
}
|
|
|
|
|
2012-02-20 21:41:24 -08:00
|
|
|
fn act(po: comm::port<msg>, source: str, parse: parser) {
|
2012-02-17 14:15:09 -08:00
|
|
|
let (sess, ignore_errors) = build_session();
|
2012-02-20 21:08:19 -08:00
|
|
|
|
2012-02-20 21:41:24 -08:00
|
|
|
let ctxt = build_ctxt(
|
|
|
|
sess,
|
|
|
|
parse(sess, source),
|
|
|
|
ignore_errors
|
|
|
|
);
|
|
|
|
|
|
|
|
let keep_going = true;
|
|
|
|
while keep_going {
|
|
|
|
alt comm::recv(po) {
|
|
|
|
handle_request(f) {
|
|
|
|
f(ctxt);
|
|
|
|
}
|
|
|
|
exit {
|
|
|
|
keep_going = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-02-20 21:08:19 -08:00
|
|
|
|
2012-02-20 21:41:24 -08:00
|
|
|
fn exec<T:send>(
|
|
|
|
srv: srv,
|
|
|
|
+f: fn~(ctxt: ctxt) -> T
|
|
|
|
) -> T {
|
|
|
|
let po = comm::port();
|
|
|
|
let ch = comm::chan(po);
|
|
|
|
let msg = handle_request(fn~[move f](ctxt: ctxt) {
|
|
|
|
comm::send(ch, f(ctxt))
|
|
|
|
});
|
|
|
|
comm::send(srv.ch, msg);
|
|
|
|
comm::recv(po)
|
2012-01-16 16:55:26 -08:00
|
|
|
}
|
|
|
|
|
2012-02-17 14:15:09 -08:00
|
|
|
fn build_ctxt(sess: session::session, ast: @ast::crate,
|
|
|
|
ignore_errors: @mutable bool) -> ctxt {
|
2012-01-18 15:48:09 -08:00
|
|
|
|
|
|
|
import rustc::front::config;
|
|
|
|
|
|
|
|
let ast = config::strip_unconfigured_items(ast);
|
2012-01-26 00:35:07 -08:00
|
|
|
let ast = front::test::modify_for_testing(sess, ast);
|
2012-03-07 12:21:08 +01:00
|
|
|
let ast_map = ast_map::map_crate(sess, *ast);
|
2012-02-17 14:15:09 -08:00
|
|
|
*ignore_errors = true;
|
2012-03-20 11:19:59 -07:00
|
|
|
let {exp_map, impl_map, _} = resolve::resolve_crate(sess, ast_map, ast);
|
2012-02-17 14:15:09 -08:00
|
|
|
*ignore_errors = false;
|
2012-01-18 15:48:09 -08:00
|
|
|
|
2012-01-16 16:55:26 -08:00
|
|
|
{
|
|
|
|
ast: ast,
|
2012-01-26 00:35:07 -08:00
|
|
|
ast_map: ast_map,
|
2012-03-20 11:19:59 -07:00
|
|
|
exp_map: exp_map,
|
|
|
|
impl_map: impl_map
|
2012-01-16 16:55:26 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-02 12:37:17 +01:00
|
|
|
// FIXME: this whole structure should not be duplicated here. makes it
|
|
|
|
// painful to add or remove options.
|
2012-02-17 14:15:09 -08:00
|
|
|
fn build_session() -> (session::session, @mutable bool) {
|
2012-01-26 00:35:07 -08:00
|
|
|
let sopts: @session::options = @{
|
|
|
|
crate_type: session::lib_crate,
|
|
|
|
static: false,
|
|
|
|
optimize: 0u,
|
|
|
|
debuginfo: false,
|
|
|
|
extra_debuginfo: false,
|
|
|
|
verify: false,
|
|
|
|
lint_opts: [],
|
|
|
|
save_temps: false,
|
|
|
|
stats: false,
|
|
|
|
time_passes: false,
|
|
|
|
time_llvm_passes: false,
|
|
|
|
output_type: link::output_type_exe,
|
|
|
|
addl_lib_search_paths: [],
|
|
|
|
maybe_sysroot: none,
|
|
|
|
target_triple: driver::host_triple(),
|
|
|
|
cfg: [],
|
|
|
|
test: false,
|
|
|
|
parse_only: false,
|
|
|
|
no_trans: false,
|
|
|
|
no_asm_comments: false,
|
2012-02-28 19:28:29 -08:00
|
|
|
warn_unused_imports: false,
|
|
|
|
enforce_mut_vars: false
|
2012-01-26 00:35:07 -08:00
|
|
|
};
|
2012-02-17 14:15:09 -08:00
|
|
|
|
|
|
|
let codemap = codemap::new_codemap();
|
|
|
|
let error_handlers = build_error_handlers(codemap);
|
|
|
|
let {emitter, span_handler, ignore_errors} = error_handlers;
|
|
|
|
|
2012-03-21 18:56:20 -04:00
|
|
|
let session = driver::build_session_(sopts, codemap, emitter,
|
2012-02-17 14:15:09 -08:00
|
|
|
span_handler);
|
|
|
|
(session, ignore_errors)
|
|
|
|
}
|
|
|
|
|
|
|
|
type error_handlers = {
|
|
|
|
emitter: diagnostic::emitter,
|
|
|
|
span_handler: diagnostic::span_handler,
|
|
|
|
ignore_errors: @mutable bool
|
|
|
|
};
|
|
|
|
|
|
|
|
// Build a custom error handler that will allow us to ignore non-fatal
|
|
|
|
// errors
|
|
|
|
fn build_error_handlers(
|
|
|
|
codemap: codemap::codemap
|
|
|
|
) -> error_handlers {
|
|
|
|
|
|
|
|
type diagnostic_handler = {
|
|
|
|
inner: diagnostic::handler,
|
|
|
|
ignore_errors: @mutable 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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn has_errors() -> bool { self.inner.has_errors() }
|
|
|
|
fn abort_if_errors() { self.inner.abort_if_errors() }
|
|
|
|
fn warn(msg: str) { self.inner.warn(msg) }
|
|
|
|
fn note(msg: str) { self.inner.note(msg) }
|
|
|
|
fn bug(msg: str) -> ! { self.inner.bug(msg) }
|
|
|
|
fn unimpl(msg: str) -> ! { self.inner.unimpl(msg) }
|
|
|
|
fn emit(cmsp: option<(codemap::codemap, codemap::span)>,
|
|
|
|
msg: str, lvl: diagnostic::level) {
|
|
|
|
self.inner.emit(cmsp, msg, lvl)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let ignore_errors = @mutable false;
|
|
|
|
let emitter = fn@(cmsp: option<(codemap::codemap, codemap::span)>,
|
|
|
|
msg: str, lvl: diagnostic::level) {
|
|
|
|
if !(*ignore_errors) {
|
|
|
|
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
|
|
|
|
}
|
2012-01-26 00:35:07 -08:00
|
|
|
}
|
|
|
|
|
2012-01-18 15:48:09 -08:00
|
|
|
#[test]
|
|
|
|
fn should_prune_unconfigured_items() {
|
|
|
|
let source = "#[cfg(shut_up_and_leave_me_alone)]fn a() { }";
|
2012-02-20 21:08:19 -08:00
|
|
|
from_str(source) {|srv|
|
|
|
|
exec(srv) {|ctxt|
|
|
|
|
assert vec::is_empty(ctxt.ast.node.module.items);
|
|
|
|
}
|
2012-01-18 15:48:09 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-26 00:35:07 -08:00
|
|
|
#[test]
|
|
|
|
fn srv_should_build_ast_map() {
|
2012-01-30 21:02:03 -08:00
|
|
|
let source = "fn a() { }";
|
2012-02-20 21:08:19 -08:00
|
|
|
from_str(source) {|srv|
|
|
|
|
exec(srv) {|ctxt|
|
|
|
|
assert ctxt.ast_map.size() != 0u
|
|
|
|
};
|
|
|
|
}
|
2012-01-26 00:35:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn srv_should_build_reexport_map() {
|
2012-02-17 14:15:09 -08:00
|
|
|
let source = "import a::b; export b; mod a { mod b { } }";
|
2012-02-20 21:08:19 -08:00
|
|
|
from_str(source) {|srv|
|
|
|
|
exec(srv) {|ctxt|
|
|
|
|
assert ctxt.exp_map.size() != 0u
|
|
|
|
};
|
|
|
|
}
|
2012-01-26 00:35:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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
|
2012-02-20 21:08:19 -08:00
|
|
|
from_str(source) {|_srv| }
|
2012-01-26 00:35:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn srv_should_resolve_core_crate() {
|
|
|
|
let source = "fn a() -> option { fail }";
|
|
|
|
// Just testing that resolve doesn't crash
|
2012-02-20 21:08:19 -08:00
|
|
|
from_str(source) {|_srv| }
|
2012-01-26 00:35:07 -08:00
|
|
|
}
|
|
|
|
|
2012-02-17 14:15:09 -08:00
|
|
|
#[test]
|
|
|
|
fn srv_should_resolve_non_existant_imports() {
|
|
|
|
// 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() { }";
|
2012-02-20 21:08:19 -08:00
|
|
|
from_str(source) {|_srv| }
|
2012-02-17 14:15:09 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2012-02-17 17:34:44 -08:00
|
|
|
fn srv_should_resolve_non_existant_uses() {
|
2012-02-17 14:15:09 -08:00
|
|
|
let source = "use forble; fn a() { }";
|
2012-02-20 21:08:19 -08:00
|
|
|
from_str(source) {|_srv| }
|
2012-02-17 14:15:09 -08:00
|
|
|
}
|
|
|
|
|
2012-02-17 17:34:44 -08:00
|
|
|
#[test]
|
|
|
|
fn should_ignore_external_import_paths_that_dont_exist() {
|
|
|
|
let source = "use forble; import forble::bippy;";
|
2012-02-20 21:08:19 -08:00
|
|
|
from_str(source) {|_srv| }
|
2012-02-17 17:34:44 -08:00
|
|
|
}
|
|
|
|
|
2012-01-26 00:35:07 -08:00
|
|
|
#[test]
|
|
|
|
fn srv_should_return_request_result() {
|
|
|
|
let source = "fn a() { }";
|
2012-02-20 21:08:19 -08:00
|
|
|
from_str(source) {|srv|
|
|
|
|
let result = exec(srv) {|_ctxt| 1000};
|
|
|
|
assert result == 1000;
|
|
|
|
}
|
2012-01-26 00:35:07 -08:00
|
|
|
}
|