syntax: Refactor diagnostics to focus on Writers

This commit alters the diagnostic emission machinery to be focused around a
Writer for emitting errors. This allows it to not hard-code emission of errors
to stderr (useful for other applications).
This commit is contained in:
Alex Crichton 2014-02-28 11:37:04 -08:00
parent 9b1be3d182
commit 324547140e
5 changed files with 122 additions and 111 deletions

View File

@ -931,7 +931,7 @@ pub fn build_session(sopts: @session::Options,
-> Session { -> Session {
let codemap = @codemap::CodeMap::new(); let codemap = @codemap::CodeMap::new();
let diagnostic_handler = let diagnostic_handler =
diagnostic::mk_handler(); diagnostic::default_handler();
let span_diagnostic_handler = let span_diagnostic_handler =
diagnostic::mk_span_handler(diagnostic_handler, codemap); diagnostic::mk_span_handler(diagnostic_handler, codemap);
@ -1149,7 +1149,8 @@ pub fn build_output_filenames(input: &Input,
} }
pub fn early_error(msg: &str) -> ! { pub fn early_error(msg: &str) -> ! {
diagnostic::DefaultEmitter.emit(None, msg, diagnostic::Fatal); let mut emitter = diagnostic::EmitterWriter::stderr();
emitter.emit(None, msg, diagnostic::Fatal);
fail!(diagnostic::FatalError); fail!(diagnostic::FatalError);
} }

View File

@ -394,7 +394,8 @@ pub fn monitor(f: proc()) {
Err(value) => { Err(value) => {
// Task failed without emitting a fatal diagnostic // Task failed without emitting a fatal diagnostic
if !value.is::<diagnostic::FatalError>() { if !value.is::<diagnostic::FatalError>() {
diagnostic::DefaultEmitter.emit( let mut emitter = diagnostic::EmitterWriter::stderr();
emitter.emit(
None, None,
diagnostic::ice_msg("unexpected failure"), diagnostic::ice_msg("unexpected failure"),
diagnostic::Error); diagnostic::Error);
@ -404,9 +405,7 @@ pub fn monitor(f: proc()) {
this is a bug", this is a bug",
]; ];
for note in xs.iter() { for note in xs.iter() {
diagnostic::DefaultEmitter.emit(None, emitter.emit(None, *note, diagnostic::Note)
*note,
diagnostic::Note)
} }
println!("{}", r.read_to_str()); println!("{}", r.read_to_str());

View File

@ -25,6 +25,7 @@ use rustc::metadata::creader::Loader;
use getopts; use getopts;
use syntax::diagnostic; use syntax::diagnostic;
use syntax::parse; use syntax::parse;
use syntax::codemap::CodeMap;
use core; use core;
use clean; use clean;
@ -35,7 +36,6 @@ use passes;
use visit_ast::RustdocVisitor; use visit_ast::RustdocVisitor;
pub fn run(input: &str, matches: &getopts::Matches) -> int { pub fn run(input: &str, matches: &getopts::Matches) -> int {
let parsesess = parse::new_parse_sess();
let input_path = Path::new(input); let input_path = Path::new(input);
let input = driver::FileInput(input_path.clone()); let input = driver::FileInput(input_path.clone());
let libs = matches.opt_strs("L").map(|s| Path::new(s.as_slice())); let libs = matches.opt_strs("L").map(|s| Path::new(s.as_slice()));
@ -49,9 +49,12 @@ pub fn run(input: &str, matches: &getopts::Matches) -> int {
}; };
let cm = @CodeMap::new();
let diagnostic_handler = diagnostic::mk_handler(); let diagnostic_handler = diagnostic::mk_handler();
let span_diagnostic_handler = let span_diagnostic_handler =
diagnostic::mk_span_handler(diagnostic_handler, parsesess.cm); diagnostic::mk_span_handler(diagnostic_handler, cm);
let parsesess = parse::new_parse_sess_special_handler(span_diagnostic_handler,
cm);
let sess = driver::build_session_(sessopts, let sess = driver::build_session_(sessopts,
Some(input_path), Some(input_path),

View File

@ -11,12 +11,10 @@
use codemap::{Pos, Span}; use codemap::{Pos, Span};
use codemap; use codemap;
use std::cell::Cell; use std::cell::{RefCell, Cell};
use std::fmt; use std::fmt;
use std::io::stdio::StdWriter;
use std::io; use std::io;
use std::iter::range; use std::iter::range;
use std::local_data;
use term; use term;
static BUG_REPORT_URL: &'static str = static BUG_REPORT_URL: &'static str =
@ -25,9 +23,9 @@ static BUG_REPORT_URL: &'static str =
static MAX_LINES: uint = 6u; static MAX_LINES: uint = 6u;
pub trait Emitter { pub trait Emitter {
fn emit(&self, cmsp: Option<(&codemap::CodeMap, Span)>, fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, Span)>,
msg: &str, lvl: Level); msg: &str, lvl: Level);
fn custom_emit(&self, cm: &codemap::CodeMap, fn custom_emit(&mut self, cm: &codemap::CodeMap,
sp: Span, msg: &str, lvl: Level); sp: Span, msg: &str, lvl: Level);
} }
@ -78,16 +76,16 @@ impl SpanHandler {
// others log errors for later reporting. // others log errors for later reporting.
pub struct Handler { pub struct Handler {
err_count: Cell<uint>, err_count: Cell<uint>,
emit: DefaultEmitter, emit: RefCell<~Emitter>,
} }
impl Handler { impl Handler {
pub fn fatal(&self, msg: &str) -> ! { pub fn fatal(&self, msg: &str) -> ! {
self.emit.emit(None, msg, Fatal); self.emit.borrow_mut().get().emit(None, msg, Fatal);
fail!(FatalError); fail!(FatalError);
} }
pub fn err(&self, msg: &str) { pub fn err(&self, msg: &str) {
self.emit.emit(None, msg, Error); self.emit.borrow_mut().get().emit(None, msg, Error);
self.bump_err_count(); self.bump_err_count();
} }
pub fn bump_err_count(&self) { pub fn bump_err_count(&self) {
@ -112,10 +110,10 @@ impl Handler {
self.fatal(s); self.fatal(s);
} }
pub fn warn(&self, msg: &str) { pub fn warn(&self, msg: &str) {
self.emit.emit(None, msg, Warning); self.emit.borrow_mut().get().emit(None, msg, Warning);
} }
pub fn note(&self, msg: &str) { pub fn note(&self, msg: &str) {
self.emit.emit(None, msg, Note); self.emit.borrow_mut().get().emit(None, msg, Note);
} }
pub fn bug(&self, msg: &str) -> ! { pub fn bug(&self, msg: &str) -> ! {
self.fatal(ice_msg(msg)); self.fatal(ice_msg(msg));
@ -127,11 +125,11 @@ impl Handler {
cmsp: Option<(&codemap::CodeMap, Span)>, cmsp: Option<(&codemap::CodeMap, Span)>,
msg: &str, msg: &str,
lvl: Level) { lvl: Level) {
self.emit.emit(cmsp, msg, lvl); self.emit.borrow_mut().get().emit(cmsp, msg, lvl);
} }
pub fn custom_emit(&self, cm: &codemap::CodeMap, pub fn custom_emit(&self, cm: &codemap::CodeMap,
sp: Span, msg: &str, lvl: Level) { sp: Span, msg: &str, lvl: Level) {
self.emit.custom_emit(cm, sp, msg, lvl); self.emit.borrow_mut().get().custom_emit(cm, sp, msg, lvl);
} }
} }
@ -148,10 +146,14 @@ pub fn mk_span_handler(handler: @Handler, cm: @codemap::CodeMap)
} }
} }
pub fn mk_handler() -> @Handler { pub fn default_handler() -> @Handler {
mk_handler(~EmitterWriter::stderr())
}
pub fn mk_handler(e: ~Emitter) -> @Handler {
@Handler { @Handler {
err_count: Cell::new(0), err_count: Cell::new(0),
emit: DefaultEmitter, emit: RefCell::new(e),
} }
} }
@ -185,73 +187,79 @@ impl Level {
} }
} }
fn print_maybe_styled(msg: &str, color: term::attr::Attr) -> io::IoResult<()> { fn print_maybe_styled(w: &mut EmitterWriter,
local_data_key!(tls_terminal: Option<term::Terminal<StdWriter>>) msg: &str,
color: term::attr::Attr) -> io::IoResult<()> {
match w.dst {
fn is_stderr_screen() -> bool { Terminal(ref mut t) => {
use std::libc; try!(t.attr(color));
unsafe { libc::isatty(libc::STDERR_FILENO) != 0 } try!(t.write_str(msg));
} try!(t.reset());
fn write_pretty<T: Writer>(term: &mut term::Terminal<T>, s: &str, Ok(())
c: term::attr::Attr) -> io::IoResult<()> { }
try!(term.attr(c)); Raw(ref mut w) => {
try!(term.write(s.as_bytes())); w.write_str(msg)
try!(term.reset()); }
Ok(())
}
if is_stderr_screen() {
local_data::get_mut(tls_terminal, |term| {
match term {
Some(term) => {
match *term {
Some(ref mut term) => write_pretty(term, msg, color),
None => io::stderr().write(msg.as_bytes())
}
}
None => {
let (t, ret) = match term::Terminal::new(io::stderr()) {
Ok(mut term) => {
let r = write_pretty(&mut term, msg, color);
(Some(term), r)
}
Err(_) => {
(None, io::stderr().write(msg.as_bytes()))
}
};
local_data::set(tls_terminal, t);
ret
}
}
})
} else {
io::stderr().write(msg.as_bytes())
} }
} }
fn print_diagnostic(topic: &str, lvl: Level, msg: &str) -> io::IoResult<()> { fn print_diagnostic(dst: &mut EmitterWriter,
topic: &str, lvl: Level, msg: &str) -> io::IoResult<()> {
if !topic.is_empty() { if !topic.is_empty() {
let mut stderr = io::stderr(); try!(write!(&mut dst.dst, "{} ", topic));
try!(write!(&mut stderr as &mut io::Writer, "{} ", topic));
} }
try!(print_maybe_styled(format!("{}: ", lvl.to_str()), try!(print_maybe_styled(dst, format!("{}: ", lvl.to_str()),
term::attr::ForegroundColor(lvl.color()))); term::attr::ForegroundColor(lvl.color())));
try!(print_maybe_styled(format!("{}\n", msg), term::attr::Bold)); try!(print_maybe_styled(dst, format!("{}\n", msg), term::attr::Bold));
Ok(()) Ok(())
} }
pub struct DefaultEmitter; pub struct EmitterWriter {
priv dst: Destination,
}
impl Emitter for DefaultEmitter { enum Destination {
fn emit(&self, Terminal(term::Terminal<io::stdio::StdWriter>),
Raw(~Writer),
}
impl EmitterWriter {
pub fn stderr() -> EmitterWriter {
let stderr = io::stderr();
if stderr.isatty() {
let dst = match term::Terminal::new(stderr) {
Ok(t) => Terminal(t),
Err(..) => Raw(~io::stderr()),
};
EmitterWriter { dst: dst }
} else {
EmitterWriter { dst: Raw(~stderr) }
}
}
pub fn new(dst: ~Writer) -> EmitterWriter {
EmitterWriter { dst: Raw(dst) }
}
}
impl Writer for Destination {
fn write(&mut self, bytes: &[u8]) -> io::IoResult<()> {
match *self {
Terminal(ref mut t) => t.write(bytes),
Raw(ref mut w) => w.write(bytes),
}
}
}
impl Emitter for EmitterWriter {
fn emit(&mut self,
cmsp: Option<(&codemap::CodeMap, Span)>, cmsp: Option<(&codemap::CodeMap, Span)>,
msg: &str, msg: &str,
lvl: Level) { lvl: Level) {
let error = match cmsp { let error = match cmsp {
Some((cm, sp)) => emit(cm, sp, msg, lvl, false), Some((cm, sp)) => emit(self, cm, sp, msg, lvl, false),
None => print_diagnostic("", lvl, msg), None => print_diagnostic(self, "", lvl, msg),
}; };
match error { match error {
@ -260,16 +268,16 @@ impl Emitter for DefaultEmitter {
} }
} }
fn custom_emit(&self, cm: &codemap::CodeMap, fn custom_emit(&mut self, cm: &codemap::CodeMap,
sp: Span, msg: &str, lvl: Level) { sp: Span, msg: &str, lvl: Level) {
match emit(cm, sp, msg, lvl, true) { match emit(self, cm, sp, msg, lvl, true) {
Ok(()) => {} Ok(()) => {}
Err(e) => fail!("failed to print diagnostics: {}", e), Err(e) => fail!("failed to print diagnostics: {}", e),
} }
} }
} }
fn emit(cm: &codemap::CodeMap, sp: Span, fn emit(dst: &mut EmitterWriter, cm: &codemap::CodeMap, sp: Span,
msg: &str, lvl: Level, custom: bool) -> io::IoResult<()> { msg: &str, lvl: Level, custom: bool) -> io::IoResult<()> {
let ss = cm.span_to_str(sp); let ss = cm.span_to_str(sp);
let lines = cm.span_to_lines(sp); let lines = cm.span_to_lines(sp);
@ -279,22 +287,21 @@ fn emit(cm: &codemap::CodeMap, sp: Span,
// the span) // the span)
let span_end = Span { lo: sp.hi, hi: sp.hi, expn_info: sp.expn_info}; let span_end = Span { lo: sp.hi, hi: sp.hi, expn_info: sp.expn_info};
let ses = cm.span_to_str(span_end); let ses = cm.span_to_str(span_end);
try!(print_diagnostic(ses, lvl, msg)); try!(print_diagnostic(dst, ses, lvl, msg));
try!(custom_highlight_lines(cm, sp, lvl, lines)); try!(custom_highlight_lines(dst, cm, sp, lvl, lines));
} else { } else {
try!(print_diagnostic(ss, lvl, msg)); try!(print_diagnostic(dst, ss, lvl, msg));
try!(highlight_lines(cm, sp, lvl, lines)); try!(highlight_lines(dst, cm, sp, lvl, lines));
} }
print_macro_backtrace(cm, sp) print_macro_backtrace(dst, cm, sp)
} }
fn highlight_lines(cm: &codemap::CodeMap, fn highlight_lines(err: &mut EmitterWriter,
cm: &codemap::CodeMap,
sp: Span, sp: Span,
lvl: Level, lvl: Level,
lines: &codemap::FileLines) -> io::IoResult<()> { lines: &codemap::FileLines) -> io::IoResult<()> {
let fm = lines.file; let fm = lines.file;
let mut err = io::stderr();
let err = &mut err as &mut io::Writer;
let mut elided = false; let mut elided = false;
let mut display_lines = lines.lines.as_slice(); let mut display_lines = lines.lines.as_slice();
@ -304,13 +311,13 @@ fn highlight_lines(cm: &codemap::CodeMap,
} }
// Print the offending lines // Print the offending lines
for line in display_lines.iter() { for line in display_lines.iter() {
try!(write!(err, "{}:{} {}\n", fm.name, *line + 1, try!(write!(&mut err.dst, "{}:{} {}\n", fm.name, *line + 1,
fm.get_line(*line as int))); fm.get_line(*line as int)));
} }
if elided { if elided {
let last_line = display_lines[display_lines.len() - 1u]; let last_line = display_lines[display_lines.len() - 1u];
let s = format!("{}:{} ", fm.name, last_line + 1u); let s = format!("{}:{} ", fm.name, last_line + 1u);
try!(write!(err, "{0:1$}...\n", "", s.len())); try!(write!(&mut err.dst, "{0:1$}...\n", "", s.len()));
} }
// FIXME (#3260) // FIXME (#3260)
@ -342,7 +349,7 @@ fn highlight_lines(cm: &codemap::CodeMap,
_ => s.push_char(' '), _ => s.push_char(' '),
}; };
} }
try!(write!(err, "{}", s)); try!(write!(&mut err.dst, "{}", s));
let mut s = ~"^"; let mut s = ~"^";
let hi = cm.lookup_char_pos(sp.hi); let hi = cm.lookup_char_pos(sp.hi);
if hi.col != lo.col { if hi.col != lo.col {
@ -350,8 +357,8 @@ fn highlight_lines(cm: &codemap::CodeMap,
let num_squigglies = hi.col.to_uint()-lo.col.to_uint()-1u; let num_squigglies = hi.col.to_uint()-lo.col.to_uint()-1u;
for _ in range(0, num_squigglies) { s.push_char('~'); } for _ in range(0, num_squigglies) { s.push_char('~'); }
} }
try!(print_maybe_styled(s + "\n", try!(print_maybe_styled(err, s + "\n",
term::attr::ForegroundColor(lvl.color()))); term::attr::ForegroundColor(lvl.color())));
} }
Ok(()) Ok(())
} }
@ -362,26 +369,25 @@ fn highlight_lines(cm: &codemap::CodeMap,
// than 6 lines), `custom_highlight_lines` will print the first line, then // than 6 lines), `custom_highlight_lines` will print the first line, then
// dot dot dot, then last line, whereas `highlight_lines` prints the first // dot dot dot, then last line, whereas `highlight_lines` prints the first
// six lines. // six lines.
fn custom_highlight_lines(cm: &codemap::CodeMap, fn custom_highlight_lines(w: &mut EmitterWriter,
cm: &codemap::CodeMap,
sp: Span, sp: Span,
lvl: Level, lvl: Level,
lines: &codemap::FileLines) -> io::IoResult<()> { lines: &codemap::FileLines) -> io::IoResult<()> {
let fm = lines.file; let fm = lines.file;
let mut err = io::stderr();
let err = &mut err as &mut io::Writer;
let lines = lines.lines.as_slice(); let lines = lines.lines.as_slice();
if lines.len() > MAX_LINES { if lines.len() > MAX_LINES {
try!(write!(err, "{}:{} {}\n", fm.name, try!(write!(&mut w.dst, "{}:{} {}\n", fm.name,
lines[0] + 1, fm.get_line(lines[0] as int))); lines[0] + 1, fm.get_line(lines[0] as int)));
try!(write!(err, "...\n")); try!(write!(&mut w.dst, "...\n"));
let last_line = lines[lines.len()-1]; let last_line = lines[lines.len()-1];
try!(write!(err, "{}:{} {}\n", fm.name, try!(write!(&mut w.dst, "{}:{} {}\n", fm.name,
last_line + 1, fm.get_line(last_line as int))); last_line + 1, fm.get_line(last_line as int)));
} else { } else {
for line in lines.iter() { for line in lines.iter() {
try!(write!(err, "{}:{} {}\n", fm.name, try!(write!(&mut w.dst, "{}:{} {}\n", fm.name,
*line + 1, fm.get_line(*line as int))); *line + 1, fm.get_line(*line as int)));
} }
} }
let last_line_start = format!("{}:{} ", fm.name, lines[lines.len()-1]+1); let last_line_start = format!("{}:{} ", fm.name, lines[lines.len()-1]+1);
@ -391,22 +397,24 @@ fn custom_highlight_lines(cm: &codemap::CodeMap,
let mut s = ~""; let mut s = ~"";
for _ in range(0, skip) { s.push_char(' '); } for _ in range(0, skip) { s.push_char(' '); }
s.push_char('^'); s.push_char('^');
print_maybe_styled(s + "\n", term::attr::ForegroundColor(lvl.color())) print_maybe_styled(w, s + "\n", term::attr::ForegroundColor(lvl.color()))
} }
fn print_macro_backtrace(cm: &codemap::CodeMap, sp: Span) -> io::IoResult<()> { fn print_macro_backtrace(w: &mut EmitterWriter,
cm: &codemap::CodeMap,
sp: Span) -> io::IoResult<()> {
for ei in sp.expn_info.iter() { for ei in sp.expn_info.iter() {
let ss = ei.callee.span.as_ref().map_or(~"", |span| cm.span_to_str(*span)); let ss = ei.callee.span.as_ref().map_or(~"", |span| cm.span_to_str(*span));
let (pre, post) = match ei.callee.format { let (pre, post) = match ei.callee.format {
codemap::MacroAttribute => ("#[", "]"), codemap::MacroAttribute => ("#[", "]"),
codemap::MacroBang => ("", "!") codemap::MacroBang => ("", "!")
}; };
try!(print_diagnostic(ss, Note, try!(print_diagnostic(w, ss, Note,
format!("in expansion of {}{}{}", pre, format!("in expansion of {}{}{}", pre,
ei.callee.name, post))); ei.callee.name, post)));
let ss = cm.span_to_str(ei.call_site); let ss = cm.span_to_str(ei.call_site);
try!(print_diagnostic(ss, Note, "expansion site")); try!(print_diagnostic(w, ss, Note, "expansion site"));
try!(print_macro_backtrace(cm, ei.call_site)); try!(print_macro_backtrace(w, cm, ei.call_site));
} }
Ok(()) Ok(())
} }

View File

@ -14,7 +14,7 @@
use ast; use ast;
use codemap::{Span, CodeMap, FileMap}; use codemap::{Span, CodeMap, FileMap};
use codemap; use codemap;
use diagnostic::{SpanHandler, mk_span_handler, mk_handler}; use diagnostic::{SpanHandler, mk_span_handler, default_handler};
use parse::attr::ParserAttr; use parse::attr::ParserAttr;
use parse::parser::Parser; use parse::parser::Parser;
@ -49,7 +49,7 @@ pub fn new_parse_sess() -> @ParseSess {
let cm = @CodeMap::new(); let cm = @CodeMap::new();
@ParseSess { @ParseSess {
cm: cm, cm: cm,
span_diagnostic: mk_span_handler(mk_handler(), cm), span_diagnostic: mk_span_handler(default_handler(), cm),
included_mod_stack: RefCell::new(~[]), included_mod_stack: RefCell::new(~[]),
} }
} }