提交 27843133 编写于 作者: A Alex Crichton

rustc: Clean up error reporting

This commit re-works how the monitor() function works and how it both receives
and transmits errors. There are a few cases in which the compiler can abort:

1. A normal compiler error. In this case, the compiler raises a FatalError as
   the failure value of the task. If this happens, then the monitor task does
   nothing. It ignores all stderr output of the child task and it also
   suppresses the failure message of the main task itself. This means that on a
   normal compiler error just the error message itself is printed.

2. A normal internal compiler error. These are invoked from sess.span_bug() and
   friends. In these cases, they follow the same path (raising a FatalError),
   but they will also print an ICE message which has a URL to go report a bug.

3. An actual compiler bug. This happens whenever anything calls fail!() instead
   of going through the session itself. In this case, we print out stuff about
   RUST_LOG=2 and we by default capture all stderr and print via warn!() so it's
   only printed out with the RUST_LOG var set.
上级 f3f2e697
...@@ -38,13 +38,9 @@ ...@@ -38,13 +38,9 @@
use d = driver::driver; use d = driver::driver;
use std::cast;
use std::comm;
use std::io; use std::io;
use std::io::Reader;
use std::num; use std::num;
use std::os; use std::os;
use std::result;
use std::str; use std::str;
use std::task; use std::task;
use std::vec; use std::vec;
...@@ -52,7 +48,6 @@ ...@@ -52,7 +48,6 @@
use extra::getopts; use extra::getopts;
use syntax::ast; use syntax::ast;
use syntax::attr; use syntax::attr;
use syntax::codemap;
use syntax::diagnostic::Emitter; use syntax::diagnostic::Emitter;
use syntax::diagnostic; use syntax::diagnostic;
use syntax::parse; use syntax::parse;
...@@ -126,21 +121,6 @@ pub mod lib { ...@@ -126,21 +121,6 @@ pub mod lib {
pub mod llvmdeps; pub mod llvmdeps;
} }
// A curious inner module that allows ::std::foo to be available in here for
// macros.
/*
mod std {
pub use std::clone;
pub use std::cmp;
pub use std::os;
pub use std::str;
pub use std::sys;
pub use std::to_bytes;
pub use std::unstable;
pub use extra::serialize;
}
*/
pub fn version(argv0: &str) { pub fn version(argv0: &str) {
let vers = match option_env!("CFG_VERSION") { let vers = match option_env!("CFG_VERSION") {
Some(vers) => vers, Some(vers) => vers,
...@@ -344,45 +324,12 @@ fn parse_crate_attrs(sess: session::Session, ...@@ -344,45 +324,12 @@ fn parse_crate_attrs(sess: session::Session,
} }
} }
#[deriving(Eq)] /// Run a procedure which will detect failures in the compiler and print nicer
pub enum monitor_msg { /// error messages rather than just failing the test.
fatal, ///
done, /// The diagnostic emitter yielded to the procedure should be used for reporting
} /// errors of the compiler.
struct RustcEmitter {
ch_capture: comm::SharedChan<monitor_msg>
}
impl diagnostic::Emitter for RustcEmitter {
fn emit(&self,
cmsp: Option<(&codemap::CodeMap, codemap::Span)>,
msg: &str,
lvl: diagnostic::Level) {
if lvl == diagnostic::Fatal {
let this = unsafe { cast::transmute_mut(self) };
this.ch_capture.send(fatal)
}
diagnostic::DefaultEmitter.emit(cmsp, msg, lvl)
}
}
/*
This is a sanity check that any failure of the compiler is performed
through the diagnostic module and reported properly - we shouldn't be calling
plain-old-fail on any execution path that might be taken. Since we have
console logging off by default, hitting a plain fail statement would make the
compiler silently exit, which would be terrible.
This method wraps the compiler in a subtask and injects a function into the
diagnostic emitter which records when we hit a fatal error. If the task
fails without recording a fatal error then we've encountered a compiler
bug and need to present an error.
*/
pub fn monitor(f: proc(@diagnostic::Emitter)) { pub fn monitor(f: proc(@diagnostic::Emitter)) {
use std::comm::*;
// XXX: This is a hack for newsched since it doesn't support split stacks. // XXX: This is a hack for newsched since it doesn't support split stacks.
// rustc needs a lot of stack! When optimizations are disabled, it needs // rustc needs a lot of stack! When optimizations are disabled, it needs
// even *more* stack than usual as well. // even *more* stack than usual as well.
...@@ -391,8 +338,6 @@ pub fn monitor(f: proc(@diagnostic::Emitter)) { ...@@ -391,8 +338,6 @@ pub fn monitor(f: proc(@diagnostic::Emitter)) {
#[cfg(not(rtopt))] #[cfg(not(rtopt))]
static STACK_SIZE: uint = 20000000; // 20MB static STACK_SIZE: uint = 20000000; // 20MB
let (p, ch) = SharedChan::new();
let ch_capture = ch.clone();
let mut task_builder = task::task(); let mut task_builder = task::task();
task_builder.name("rustc"); task_builder.name("rustc");
...@@ -402,30 +347,18 @@ pub fn monitor(f: proc(@diagnostic::Emitter)) { ...@@ -402,30 +347,18 @@ pub fn monitor(f: proc(@diagnostic::Emitter)) {
task_builder.opts.stack_size = Some(STACK_SIZE); task_builder.opts.stack_size = Some(STACK_SIZE);
} }
match task_builder.try(proc() { let (p, c) = Chan::new();
let ch = ch_capture.clone(); let w = io::ChanWriter::new(c);
// The 'diagnostics emitter'. Every error, warning, etc. should let mut r = io::PortReader::new(p);
// go through this function.
let demitter = @RustcEmitter {
ch_capture: ch.clone(),
} as @diagnostic::Emitter;
struct finally {
ch: SharedChan<monitor_msg>,
}
impl Drop for finally { match task_builder.try(proc() {
fn drop(&mut self) { self.ch.send(done); } io::stdio::set_stderr(~w as ~io::Writer);
} f(@diagnostic::DefaultEmitter)
let _finally = finally { ch: ch };
f(demitter);
}) { }) {
result::Ok(_) => { /* fallthrough */ } Ok(()) => { /* fallthrough */ }
result::Err(_) => { Err(value) => {
// Task failed without emitting a fatal diagnostic // Task failed without emitting a fatal diagnostic
if p.recv() == done { if !value.is::<diagnostic::FatalError>() {
diagnostic::DefaultEmitter.emit( diagnostic::DefaultEmitter.emit(
None, None,
diagnostic::ice_msg("unexpected failure"), diagnostic::ice_msg("unexpected failure"),
...@@ -434,17 +367,20 @@ impl Drop for finally { ...@@ -434,17 +367,20 @@ impl Drop for finally {
let xs = [ let xs = [
~"the compiler hit an unexpected failure path. \ ~"the compiler hit an unexpected failure path. \
this is a bug", this is a bug",
~"try running with RUST_LOG=rustc=1 \
to get further details and report the results \
to github.com/mozilla/rust/issues"
]; ];
for note in xs.iter() { for note in xs.iter() {
diagnostic::DefaultEmitter.emit(None, diagnostic::DefaultEmitter.emit(None,
*note, *note,
diagnostic::Note) diagnostic::Note)
} }
println!("{}", r.read_to_str());
} }
// Fail so the process returns a failure code
// Fail so the process returns a failure code, but don't pollute the
// output with some unnecessary failure messages, we've already
// printed everything that we needed to.
io::stdio::set_stderr(~io::util::NullWriter as ~io::Writer);
fail!(); fail!();
} }
} }
......
...@@ -27,6 +27,11 @@ fn emit(&self, ...@@ -27,6 +27,11 @@ fn emit(&self,
lvl: Level); lvl: Level);
} }
/// This structure is used to signify that a task has failed with a fatal error
/// from the diagnostics. You can use this with the `Any` trait to figure out
/// how a rustc task died (if so desired).
pub struct FatalError;
// a span-handler is like a handler but also // a span-handler is like a handler but also
// accepts span information for source-location // accepts span information for source-location
// reporting. // reporting.
...@@ -38,7 +43,7 @@ pub struct SpanHandler { ...@@ -38,7 +43,7 @@ pub struct SpanHandler {
impl SpanHandler { impl SpanHandler {
pub fn span_fatal(@self, sp: Span, msg: &str) -> ! { pub fn span_fatal(@self, sp: Span, msg: &str) -> ! {
self.handler.emit(Some((&*self.cm, sp)), msg, Fatal); self.handler.emit(Some((&*self.cm, sp)), msg, Fatal);
fail!(); fail!(FatalError);
} }
pub fn span_err(@self, sp: Span, msg: &str) { pub fn span_err(@self, sp: Span, msg: &str) {
self.handler.emit(Some((&*self.cm, sp)), msg, Error); self.handler.emit(Some((&*self.cm, sp)), msg, Error);
...@@ -72,7 +77,7 @@ pub struct Handler { ...@@ -72,7 +77,7 @@ pub struct Handler {
impl Handler { impl Handler {
pub fn fatal(@self, msg: &str) -> ! { pub fn fatal(@self, msg: &str) -> ! {
self.emit.emit(None, msg, Fatal); self.emit.emit(None, msg, Fatal);
fail!(); fail!(FatalError);
} }
pub fn err(@self, msg: &str) { pub fn err(@self, msg: &str) {
self.emit.emit(None, msg, Error); self.emit.emit(None, msg, Error);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册