astsrv.rs 5.3 KB
Newer Older
1 2 3 4 5 6 7 8
/*!
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.
*/
9

B
Brian Anderson 已提交
10
use std::map::HashMap;
P
Patrick Walton 已提交
11 12
use rustc::driver::session;
use session::{basic_options, options};
13
use session::Session;
P
Patrick Walton 已提交
14 15 16 17 18 19
use rustc::driver::driver;
use syntax::diagnostic;
use syntax::diagnostic::handler;
use syntax::ast;
use syntax::codemap;
use syntax::ast_map;
20
use rustc::back::link;
P
Patrick Walton 已提交
21 22
use rustc::metadata::filesearch;
use rustc::front;
23

B
Brian Anderson 已提交
24 25 26
export Ctxt;
export CtxtHandler;
export Srv;
27 28
export from_str;
export from_file;
29
export exec;
30

B
Brian Anderson 已提交
31
type Ctxt = {
32
    ast: @ast::crate,
33
    ast_map: ast_map::map
34 35
};

B
Brian Anderson 已提交
36 37
type SrvOwner<T> = fn(srv: Srv) -> T;
type CtxtHandler<T> = fn~(ctxt: Ctxt) -> T;
38
type Parser = fn~(Session, ~str) -> @ast::crate;
39

B
Brian Anderson 已提交
40 41 42
enum Msg {
    HandleRequest(fn~(Ctxt)),
    Exit
43 44
}

B
Brian Anderson 已提交
45 46
enum Srv = {
    ch: comm::Chan<Msg>
47
};
48

B
Brian Anderson 已提交
49
fn from_str<T>(source: ~str, owner: SrvOwner<T>) -> T {
50
    run(owner, source, parse::from_str_sess)
51
}
52

B
Brian Anderson 已提交
53
fn from_file<T>(file: ~str, owner: SrvOwner<T>) -> T {
54
    run(owner, file, |sess, f| parse::from_file_sess(sess, &Path(f)))
55 56
}

B
Brian Anderson 已提交
57
fn run<T>(owner: SrvOwner<T>, source: ~str, +parse: Parser) -> T {
58

B
Brian Anderson 已提交
59
    let srv_ = Srv({
T
Tim Chevalier 已提交
60
        ch: do task::spawn_listener |move parse, po| {
61 62 63
            act(po, source, parse);
        }
    });
64

65
    let res = owner(srv_);
B
Brian Anderson 已提交
66
    comm::send(srv_.ch, Exit);
T
Tim Chevalier 已提交
67
    move res
68 69
}

B
Brian Anderson 已提交
70
fn act(po: comm::Port<Msg>, source: ~str, parse: Parser) {
71
    let sess = build_session();
72

73 74
    let ctxt = build_ctxt(
        sess,
75
        parse(sess, source)
76 77
    );

78
    let mut keep_going = true;
79
    while keep_going {
80
        match comm::recv(po) {
B
Brian Anderson 已提交
81
          HandleRequest(f) => {
82 83
            f(ctxt);
          }
B
Brian Anderson 已提交
84
          Exit => {
85 86 87 88 89
            keep_going = false;
          }
        }
    }
}
90

91
fn exec<T:Send>(
B
Brian Anderson 已提交
92 93
    srv: Srv,
    +f: fn~(ctxt: Ctxt) -> T
94
) -> T {
95
    let po = comm::Port();
T
Tim Chevalier 已提交
96
    let ch = comm::Chan(&po);
B
Brian Anderson 已提交
97
    let msg = HandleRequest(fn~(move f, ctxt: Ctxt) {
98 99
        comm::send(ch, f(ctxt))
    });
T
Tim Chevalier 已提交
100
    comm::send(srv.ch, move msg);
101
    comm::recv(po)
102 103
}

104
fn build_ctxt(sess: Session,
B
Brian Anderson 已提交
105
              ast: @ast::crate) -> Ctxt {
106

107
    use rustc::front::config;
108 109

    let ast = config::strip_unconfigured_items(ast);
110 111
    let ast = syntax::ext::expand::expand_crate(sess.parse_sess,
                                                sess.opts.cfg, ast);
112
    let ast = front::test::modify_for_testing(sess, ast);
113
    let ast_map = ast_map::map_crate(sess.diagnostic(), *ast);
114

115 116
    {
        ast: ast,
117
        ast_map: ast_map,
118 119 120
    }
}

121
fn build_session() -> Session {
122
    let sopts: @options = basic_options();
B
Brian Anderson 已提交
123
    let codemap = @codemap::CodeMap::new();
124
    let error_handlers = build_error_handlers(codemap);
125
    let {emitter, span_handler} = error_handlers;
126

127
    let session = driver::build_session_(sopts, codemap, emitter,
128
                                         span_handler);
129
    session
130 131
}

B
Brian Anderson 已提交
132
type ErrorHandlers = {
133
    emitter: diagnostic::emitter,
134
    span_handler: diagnostic::span_handler
135 136 137 138 139
};

// Build a custom error handler that will allow us to ignore non-fatal
// errors
fn build_error_handlers(
140
    codemap: @codemap::CodeMap
B
Brian Anderson 已提交
141
) -> ErrorHandlers {
142

B
Brian Anderson 已提交
143
    type DiagnosticHandler = {
144 145 146
        inner: diagnostic::handler,
    };

B
Brian Anderson 已提交
147
    impl DiagnosticHandler: diagnostic::handler {
T
Tim Chevalier 已提交
148 149
        fn fatal(msg: &str) -> ! { self.inner.fatal(msg) }
        fn err(msg: &str) { self.inner.err(msg) }
150
        fn bump_err_count() {
151
            self.inner.bump_err_count();
152 153 154
        }
        fn has_errors() -> bool { self.inner.has_errors() }
        fn abort_if_errors() { self.inner.abort_if_errors() }
T
Tim Chevalier 已提交
155 156 157 158
        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) }
159
        fn emit(cmsp: Option<(@codemap::CodeMap, codemap::span)>,
T
Tim Chevalier 已提交
160
                msg: &str, lvl: diagnostic::level) {
161 162 163 164
            self.inner.emit(cmsp, msg, lvl)
        }
    }

165
    let emitter = fn@(cmsp: Option<(@codemap::CodeMap, codemap::span)>,
T
Tim Chevalier 已提交
166
                       msg: &str, lvl: diagnostic::level) {
167
        diagnostic::emit(cmsp, msg, lvl);
168
    };
B
Brian Anderson 已提交
169
    let inner_handler = diagnostic::mk_handler(Some(emitter));
170 171 172 173 174 175 176 177
    let handler = {
        inner: inner_handler,
    };
    let span_handler = diagnostic::mk_span_handler(
        handler as diagnostic::handler, codemap);

    {
        emitter: emitter,
178
        span_handler: span_handler
179
    }
180 181
}

182 183
#[test]
fn should_prune_unconfigured_items() {
184
    let source = ~"#[cfg(shut_up_and_leave_me_alone)]fn a() { }";
B
Brian Anderson 已提交
185 186
    do from_str(source) |srv| {
        do exec(srv) |ctxt| {
187 188
            assert vec::is_empty(ctxt.ast.node.module.items);
        }
189 190 191
    }
}

192 193
#[test]
fn srv_should_build_ast_map() {
194
    let source = ~"fn a() { }";
B
Brian Anderson 已提交
195 196
    do from_str(source) |srv| {
        do exec(srv) |ctxt| {
197 198 199
            assert ctxt.ast_map.size() != 0u
        };
    }
200 201
}

202 203
#[test]
fn should_ignore_external_import_paths_that_dont_exist() {
204
    let source = ~"use forble; use forble::bippy;";
B
Brian Anderson 已提交
205
    from_str(source, |_srv| { } )
206 207
}

208 209
#[test]
fn srv_should_return_request_result() {
210
    let source = ~"fn a() { }";
B
Brian Anderson 已提交
211 212
    do from_str(source) |srv| {
        let result = exec(srv, |_ctxt| 1000 );
213 214
        assert result == 1000;
    }
215
}