astsrv.rs 5.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

11 12 13 14 15 16 17 18
/*!
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.
*/
19

B
Brian Anderson 已提交
20
use std::map::HashMap;
P
Patrick Walton 已提交
21 22
use rustc::driver::session;
use session::{basic_options, options};
23
use session::Session;
P
Patrick Walton 已提交
24 25 26 27 28 29
use rustc::driver::driver;
use syntax::diagnostic;
use syntax::diagnostic::handler;
use syntax::ast;
use syntax::codemap;
use syntax::ast_map;
30
use rustc::back::link;
P
Patrick Walton 已提交
31 32
use rustc::metadata::filesearch;
use rustc::front;
33

B
Brian Anderson 已提交
34
pub type Ctxt = {
35
    ast: @ast::crate,
36
    ast_map: ast_map::map
37 38
};

B
Brian Anderson 已提交
39
type SrvOwner<T> = fn(srv: Srv) -> T;
B
Brian Anderson 已提交
40
pub type CtxtHandler<T> = fn~(ctxt: Ctxt) -> T;
41
type Parser = fn~(Session, +s: ~str) -> @ast::crate;
42

B
Brian Anderson 已提交
43 44 45
enum Msg {
    HandleRequest(fn~(Ctxt)),
    Exit
46 47
}

B
Brian Anderson 已提交
48
pub enum Srv = {
B
Brian Anderson 已提交
49
    ch: comm::Chan<Msg>
50
};
51

52 53 54 55
impl Srv: Clone {
    fn clone(&self) -> Srv { copy *self }
}

B
Brian Anderson 已提交
56
pub fn from_str<T>(source: ~str, owner: SrvOwner<T>) -> T {
57
    run(owner, source, parse::from_str_sess)
58
}
59

B
Brian Anderson 已提交
60
pub fn from_file<T>(file: ~str, owner: SrvOwner<T>) -> T {
61
    run(owner, file, |sess, f| parse::from_file_sess(sess, &Path(f)))
62 63
}

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

B
Brian Anderson 已提交
66
    let srv_ = Srv({
T
Tim Chevalier 已提交
67
        ch: do task::spawn_listener |move parse, po| {
68 69 70
            act(po, source, parse);
        }
    });
71

72
    let res = owner(srv_);
B
Brian Anderson 已提交
73
    comm::send(srv_.ch, Exit);
T
Tim Chevalier 已提交
74
    move res
75 76
}

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

80 81
    let ctxt = build_ctxt(
        sess,
82
        parse(sess, source)
83 84
    );

85
    let mut keep_going = true;
86
    while keep_going {
87
        match comm::recv(po) {
B
Brian Anderson 已提交
88
          HandleRequest(f) => {
89 90
            f(ctxt);
          }
B
Brian Anderson 已提交
91
          Exit => {
92 93 94 95 96
            keep_going = false;
          }
        }
    }
}
97

B
Brian Anderson 已提交
98
pub fn exec<T:Send>(
B
Brian Anderson 已提交
99 100
    srv: Srv,
    +f: fn~(ctxt: Ctxt) -> T
101
) -> T {
102
    let po = comm::Port();
T
Tim Chevalier 已提交
103
    let ch = comm::Chan(&po);
B
Brian Anderson 已提交
104
    let msg = HandleRequest(fn~(move f, ctxt: Ctxt) {
105 106
        comm::send(ch, f(ctxt))
    });
T
Tim Chevalier 已提交
107
    comm::send(srv.ch, move msg);
108
    comm::recv(po)
109 110
}

111
fn build_ctxt(sess: Session,
B
Brian Anderson 已提交
112
              ast: @ast::crate) -> Ctxt {
113

114
    use rustc::front::config;
115 116

    let ast = config::strip_unconfigured_items(ast);
117 118
    let ast = syntax::ext::expand::expand_crate(sess.parse_sess,
                                                sess.opts.cfg, ast);
119
    let ast = front::test::modify_for_testing(sess, ast);
120
    let ast_map = ast_map::map_crate(sess.diagnostic(), *ast);
121

122 123
    {
        ast: ast,
124
        ast_map: ast_map,
125 126 127
    }
}

128
fn build_session() -> Session {
129
    let sopts: @options = basic_options();
B
Brian Anderson 已提交
130
    let codemap = @codemap::CodeMap::new();
131
    let error_handlers = build_error_handlers(codemap);
132
    let {emitter, span_handler} = error_handlers;
133

134
    let session = driver::build_session_(sopts, codemap, emitter,
135
                                         span_handler);
136
    session
137 138
}

B
Brian Anderson 已提交
139
type ErrorHandlers = {
140
    emitter: diagnostic::emitter,
141
    span_handler: diagnostic::span_handler
142 143 144 145 146
};

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

B
Brian Anderson 已提交
150
    type DiagnosticHandler = {
151 152 153
        inner: diagnostic::handler,
    };

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

172
    let emitter = fn@(cmsp: Option<(@codemap::CodeMap, codemap::span)>,
T
Tim Chevalier 已提交
173
                       msg: &str, lvl: diagnostic::level) {
174
        diagnostic::emit(cmsp, msg, lvl);
175
    };
B
Brian Anderson 已提交
176
    let inner_handler = diagnostic::mk_handler(Some(emitter));
177 178 179 180 181 182 183 184
    let handler = {
        inner: inner_handler,
    };
    let span_handler = diagnostic::mk_span_handler(
        handler as diagnostic::handler, codemap);

    {
        emitter: emitter,
185
        span_handler: span_handler
186
    }
187 188
}

189 190
#[test]
fn should_prune_unconfigured_items() {
191
    let source = ~"#[cfg(shut_up_and_leave_me_alone)]fn a() { }";
B
Brian Anderson 已提交
192 193
    do from_str(source) |srv| {
        do exec(srv) |ctxt| {
194 195
            assert vec::is_empty(ctxt.ast.node.module.items);
        }
196 197 198
    }
}

199 200
#[test]
fn srv_should_build_ast_map() {
201
    let source = ~"fn a() { }";
B
Brian Anderson 已提交
202 203
    do from_str(source) |srv| {
        do exec(srv) |ctxt| {
204 205 206
            assert ctxt.ast_map.size() != 0u
        };
    }
207 208
}

209 210
#[test]
fn should_ignore_external_import_paths_that_dont_exist() {
211
    let source = ~"use forble; use forble::bippy;";
B
Brian Anderson 已提交
212
    from_str(source, |_srv| { } )
213 214
}

215 216
#[test]
fn srv_should_return_request_result() {
217
    let source = ~"fn a() { }";
B
Brian Anderson 已提交
218 219
    do from_str(source) |srv| {
        let result = exec(srv, |_ctxt| 1000 );
220 221
        assert result == 1000;
    }
222
}