rust/src/librustdoc/astsrv.rs

209 lines
5.2 KiB
Rust
Raw Normal View History

/*!
Provides all access to AST-related, non-sendable info
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-09-10 15:38:28 -07:00
use std::map::HashMap;
2012-09-05 10:41:47 -07:00
use rustc::driver::session;
use session::{basic_options, options};
use session::Session;
2012-09-05 10:41:47 -07:00
use rustc::driver::driver;
use syntax::diagnostic;
use syntax::diagnostic::handler;
use syntax::ast;
use syntax::codemap;
use syntax::ast_map;
use rustc::back::link;
2012-09-05 10:41:47 -07:00
use rustc::metadata::filesearch;
use rustc::front;
2012-11-19 18:00:12 -08:00
pub type Ctxt = {
ast: @ast::crate,
ast_map: ast_map::map
};
2012-09-18 16:48:40 -07:00
type SrvOwner<T> = fn(srv: Srv) -> T;
2012-11-19 18:00:12 -08:00
pub type CtxtHandler<T> = fn~(ctxt: Ctxt) -> T;
2012-11-19 18:48:46 -08:00
type Parser = fn~(Session, +s: ~str) -> @ast::crate;
2012-09-18 16:48:40 -07:00
enum Msg {
HandleRequest(fn~(Ctxt)),
Exit
}
2012-11-19 18:00:12 -08:00
pub enum Srv = {
2012-09-18 16:48:40 -07:00
ch: comm::Chan<Msg>
};
2012-11-19 18:00:12 -08:00
pub fn from_str<T>(source: ~str, owner: SrvOwner<T>) -> T {
run(owner, source, parse::from_str_sess)
}
2012-11-19 18:00:12 -08:00
pub fn from_file<T>(file: ~str, owner: SrvOwner<T>) -> T {
run(owner, file, |sess, f| parse::from_file_sess(sess, &Path(f)))
}
2012-09-18 16:48:40 -07:00
fn run<T>(owner: SrvOwner<T>, source: ~str, +parse: Parser) -> T {
2012-09-18 16:48:40 -07:00
let srv_ = Srv({
2012-09-18 22:43:54 -07:00
ch: do task::spawn_listener |move parse, po| {
act(po, source, parse);
}
});
let res = owner(srv_);
2012-09-18 16:48:40 -07:00
comm::send(srv_.ch, Exit);
2012-09-18 22:43:54 -07:00
move res
}
2012-09-18 16:48:40 -07:00
fn act(po: comm::Port<Msg>, source: ~str, parse: Parser) {
let sess = build_session();
let ctxt = build_ctxt(
sess,
parse(sess, source)
);
let mut keep_going = true;
while keep_going {
2012-08-06 12:34:08 -07:00
match comm::recv(po) {
2012-09-18 16:48:40 -07:00
HandleRequest(f) => {
f(ctxt);
}
2012-09-18 16:48:40 -07:00
Exit => {
keep_going = false;
}
}
}
}
2012-11-19 18:00:12 -08:00
pub fn exec<T:Send>(
2012-09-18 16:48:40 -07:00
srv: Srv,
+f: fn~(ctxt: Ctxt) -> T
) -> T {
2012-08-27 14:22:25 -07:00
let po = comm::Port();
2012-10-03 14:38:01 -07:00
let ch = comm::Chan(&po);
2012-09-18 16:48:40 -07:00
let msg = HandleRequest(fn~(move f, ctxt: Ctxt) {
comm::send(ch, f(ctxt))
});
2012-09-18 22:43:54 -07:00
comm::send(srv.ch, move msg);
comm::recv(po)
}
fn build_ctxt(sess: Session,
2012-09-18 16:48:40 -07:00
ast: @ast::crate) -> Ctxt {
use rustc::front::config;
let ast = config::strip_unconfigured_items(ast);
let ast = syntax::ext::expand::expand_crate(sess.parse_sess,
sess.opts.cfg, ast);
let ast = front::test::modify_for_testing(sess, ast);
let ast_map = ast_map::map_crate(sess.diagnostic(), *ast);
{
ast: ast,
ast_map: ast_map,
}
}
fn build_session() -> Session {
let sopts: @options = basic_options();
2012-11-12 18:24:56 -08:00
let codemap = @codemap::CodeMap::new();
2012-02-17 14:15:09 -08:00
let error_handlers = build_error_handlers(codemap);
let {emitter, span_handler} = error_handlers;
2012-02-17 14:15:09 -08:00
let session = driver::build_session_(sopts, codemap, emitter,
2012-02-17 14:15:09 -08:00
span_handler);
session
2012-02-17 14:15:09 -08:00
}
2012-09-18 16:48:40 -07:00
type ErrorHandlers = {
2012-02-17 14:15:09 -08:00
emitter: diagnostic::emitter,
span_handler: diagnostic::span_handler
2012-02-17 14:15:09 -08:00
};
// Build a custom error handler that will allow us to ignore non-fatal
// errors
fn build_error_handlers(
codemap: @codemap::CodeMap
2012-09-18 16:48:40 -07:00
) -> ErrorHandlers {
2012-02-17 14:15:09 -08:00
2012-09-18 16:48:40 -07:00
type DiagnosticHandler = {
2012-02-17 14:15:09 -08:00
inner: diagnostic::handler,
};
2012-09-18 16:48:40 -07:00
impl DiagnosticHandler: diagnostic::handler {
fn fatal(msg: &str) -> ! { self.inner.fatal(msg) }
fn err(msg: &str) { self.inner.err(msg) }
2012-02-17 14:15:09 -08:00
fn bump_err_count() {
self.inner.bump_err_count();
2012-02-17 14:15:09 -08:00
}
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) {
2012-02-17 14:15:09 -08:00
self.inner.emit(cmsp, msg, lvl)
}
}
let emitter = fn@(cmsp: Option<(@codemap::CodeMap, codemap::span)>,
msg: &str, lvl: diagnostic::level) {
diagnostic::emit(cmsp, msg, lvl);
2012-02-17 14:15:09 -08:00
};
2012-08-20 12:23:37 -07:00
let inner_handler = diagnostic::mk_handler(Some(emitter));
2012-02-17 14:15:09 -08:00
let handler = {
inner: inner_handler,
};
let span_handler = diagnostic::mk_span_handler(
handler as diagnostic::handler, codemap);
{
emitter: emitter,
span_handler: span_handler
2012-02-17 14:15:09 -08:00
}
}
#[test]
fn should_prune_unconfigured_items() {
let source = ~"#[cfg(shut_up_and_leave_me_alone)]fn a() { }";
2012-06-30 16:19:07 -07:00
do from_str(source) |srv| {
do exec(srv) |ctxt| {
assert vec::is_empty(ctxt.ast.node.module.items);
}
}
}
#[test]
fn srv_should_build_ast_map() {
let source = ~"fn a() { }";
2012-06-30 16:19:07 -07:00
do from_str(source) |srv| {
do exec(srv) |ctxt| {
assert ctxt.ast_map.size() != 0u
};
}
}
#[test]
fn should_ignore_external_import_paths_that_dont_exist() {
let source = ~"use forble; use forble::bippy;";
2012-06-30 16:19:07 -07:00
from_str(source, |_srv| { } )
}
#[test]
fn srv_should_return_request_result() {
let source = ~"fn a() { }";
2012-06-30 16:19:07 -07:00
do from_str(source) |srv| {
let result = exec(srv, |_ctxt| 1000 );
assert result == 1000;
}
}