rust/src/libsyntax/diagnostic.rs

358 lines
11 KiB
Rust
Raw Normal View History

// 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.
use codemap::{Pos, Span};
use codemap;
use std::rt::io;
use std::local_data;
use extra::term;
static BUG_REPORT_URL: &'static str =
"https://github.com/mozilla/rust/wiki/HOWTO-submit-a-Rust-bug-report";
pub trait Emitter {
fn emit(&self,
cmsp: Option<(@codemap::CodeMap, Span)>,
msg: &str,
lvl: level);
}
2013-02-11 15:36:24 -06:00
// a handler deals with errors; certain errors
// (fatal, bug, unimpl) may cause immediate exit,
// others log errors for later reporting.
pub trait handler {
fn fatal(@mut self, msg: &str) -> !;
fn err(@mut self, msg: &str);
fn bump_err_count(@mut self);
fn err_count(@mut self) -> uint;
fn has_errors(@mut self) -> bool;
fn abort_if_errors(@mut self);
fn warn(@mut self, msg: &str);
fn note(@mut self, msg: &str);
2013-02-11 15:36:24 -06:00
// used to indicate a bug in the compiler:
fn bug(@mut self, msg: &str) -> !;
fn unimpl(@mut self, msg: &str) -> !;
fn emit(@mut self,
cmsp: Option<(@codemap::CodeMap, Span)>,
msg: &str,
lvl: level);
}
2013-02-11 15:36:24 -06:00
// a span-handler is like a handler but also
// accepts span information for source-location
// reporting.
pub trait span_handler {
fn span_fatal(@mut self, sp: Span, msg: &str) -> !;
fn span_err(@mut self, sp: Span, msg: &str);
fn span_warn(@mut self, sp: Span, msg: &str);
fn span_note(@mut self, sp: Span, msg: &str);
fn span_bug(@mut self, sp: Span, msg: &str) -> !;
fn span_unimpl(@mut self, sp: Span, msg: &str) -> !;
fn handler(@mut self) -> @mut handler;
2013-02-11 15:36:24 -06:00
}
struct HandlerT {
err_count: uint,
emit: @Emitter,
}
struct CodemapT {
handler: @mut handler,
cm: @codemap::CodeMap,
}
impl span_handler for CodemapT {
fn span_fatal(@mut self, sp: Span, msg: &str) -> ! {
2012-08-20 14:23:37 -05:00
self.handler.emit(Some((self.cm, sp)), msg, fatal);
fail!();
}
fn span_err(@mut self, sp: Span, msg: &str) {
2012-08-20 14:23:37 -05:00
self.handler.emit(Some((self.cm, sp)), msg, error);
self.handler.bump_err_count();
}
fn span_warn(@mut self, sp: Span, msg: &str) {
self.handler.emit(Some((self.cm, sp)), msg, warning);
}
fn span_note(@mut self, sp: Span, msg: &str) {
2012-08-20 14:23:37 -05:00
self.handler.emit(Some((self.cm, sp)), msg, note);
}
fn span_bug(@mut self, sp: Span, msg: &str) -> ! {
self.span_fatal(sp, ice_msg(msg));
}
fn span_unimpl(@mut self, sp: Span, msg: &str) -> ! {
self.span_bug(sp, ~"unimplemented " + msg);
}
fn handler(@mut self) -> @mut handler {
self.handler
}
}
impl handler for HandlerT {
fn fatal(@mut self, msg: &str) -> ! {
self.emit.emit(None, msg, fatal);
fail!();
}
fn err(@mut self, msg: &str) {
self.emit.emit(None, msg, error);
self.bump_err_count();
}
fn bump_err_count(@mut self) {
self.err_count += 1u;
}
fn err_count(@mut self) -> uint {
self.err_count
}
fn has_errors(@mut self) -> bool {
self.err_count > 0u
}
fn abort_if_errors(@mut self) {
2012-06-06 16:28:59 -05:00
let s;
2012-08-06 14:34:08 -05:00
match self.err_count {
2012-08-03 21:59:04 -05:00
0u => return,
1u => s = ~"aborting due to previous error",
_ => {
2013-09-27 23:01:58 -05:00
s = format!("aborting due to {} previous errors",
2012-08-22 19:24:52 -05:00
self.err_count);
2012-08-03 21:59:04 -05:00
}
}
2012-06-06 16:28:59 -05:00
self.fatal(s);
}
fn warn(@mut self, msg: &str) {
self.emit.emit(None, msg, warning);
}
fn note(@mut self, msg: &str) {
self.emit.emit(None, msg, note);
}
fn bug(@mut self, msg: &str) -> ! {
self.fatal(ice_msg(msg));
}
fn unimpl(@mut self, msg: &str) -> ! {
self.bug(~"unimplemented " + msg);
}
fn emit(@mut self,
cmsp: Option<(@codemap::CodeMap, Span)>,
msg: &str,
lvl: level) {
self.emit.emit(cmsp, msg, lvl);
}
}
pub fn ice_msg(msg: &str) -> ~str {
format!("internal compiler error: {}\nThis message reflects a bug in the Rust compiler. \
\nWe would appreciate a bug report: {}", msg, BUG_REPORT_URL)
}
pub fn mk_span_handler(handler: @mut handler, cm: @codemap::CodeMap)
-> @mut span_handler {
@mut CodemapT {
handler: handler,
cm: cm,
} as @mut span_handler
}
pub fn mk_handler(emitter: Option<@Emitter>) -> @mut handler {
let emit: @Emitter = match emitter {
Some(e) => e,
None => @DefaultEmitter as @Emitter
};
@mut HandlerT {
err_count: 0,
emit: emit,
} as @mut handler
}
#[deriving(Eq)]
pub enum level {
2012-01-19 19:56:05 -06:00
fatal,
error,
warning,
note,
}
fn diagnosticstr(lvl: level) -> ~str {
2012-08-06 14:34:08 -05:00
match lvl {
fatal => ~"error",
error => ~"error",
warning => ~"warning",
note => ~"note"
}
}
fn diagnosticcolor(lvl: level) -> term::color::Color {
2012-08-06 14:34:08 -05:00
match lvl {
fatal => term::color::BRIGHT_RED,
error => term::color::BRIGHT_RED,
warning => term::color::BRIGHT_YELLOW,
note => term::color::BRIGHT_GREEN
}
}
fn print_maybe_styled(msg: &str, color: term::attr::Attr) {
2013-09-17 01:34:40 -05:00
local_data_key!(tls_terminal: @Option<term::Terminal>)
let stderr = @mut io::stderr() as @mut io::Writer;
fn is_stderr_screen() -> bool {
use std::libc;
unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
}
2013-05-30 01:13:35 -05:00
if is_stderr_screen() {
let t = match local_data::get(tls_terminal, |v| v.map(|k| *k)) {
None => {
let t = term::Terminal::new(stderr);
let tls = @match t {
Ok(t) => Some(t),
Err(_) => None
};
local_data::set(tls_terminal, tls);
&*tls
}
Some(tls) => &*tls
};
2013-05-30 01:13:35 -05:00
match t {
&Some(ref term) => {
term.attr(color);
write!(stderr, "{}", msg);
term.reset();
},
_ => write!(stderr, "{}", msg)
}
} else {
write!(stderr, "{}", msg);
}
}
fn print_diagnostic(topic: &str, lvl: level, msg: &str) {
let mut stderr = io::stderr();
if !topic.is_empty() {
write!(&mut stderr as &mut io::Writer, "{} ", topic);
}
2013-09-27 23:01:58 -05:00
print_maybe_styled(format!("{}: ", diagnosticstr(lvl)),
term::attr::ForegroundColor(diagnosticcolor(lvl)));
2013-09-27 23:01:58 -05:00
print_maybe_styled(format!("{}\n", msg), term::attr::Bold);
}
pub struct DefaultEmitter;
impl Emitter for DefaultEmitter {
fn emit(&self,
cmsp: Option<(@codemap::CodeMap, Span)>,
msg: &str,
lvl: level) {
match cmsp {
Some((cm, sp)) => {
let sp = cm.adjust_span(sp);
let ss = cm.span_to_str(sp);
let lines = cm.span_to_lines(sp);
print_diagnostic(ss, lvl, msg);
highlight_lines(cm, sp, lvl, lines);
print_macro_backtrace(cm, sp);
}
None => print_diagnostic("", lvl, msg),
}
}
}
fn highlight_lines(cm: @codemap::CodeMap,
sp: Span,
lvl: level,
lines: @codemap::FileLines) {
let fm = lines.file;
let mut err = io::stderr();
let err = &mut err as &mut io::Writer;
// arbitrarily only print up to six lines of the error
let max_lines = 6u;
let mut elided = false;
2013-07-02 14:47:32 -05:00
let mut display_lines = /* FIXME (#2543) */ lines.lines.clone();
2013-05-14 04:52:12 -05:00
if display_lines.len() > max_lines {
display_lines = display_lines.slice(0u, max_lines).to_owned();
elided = true;
}
// Print the offending lines
for line in display_lines.iter() {
write!(err, "{}:{} {}\n", fm.name, *line + 1, fm.get_line(*line as int));
}
if elided {
2013-05-14 04:52:12 -05:00
let last_line = display_lines[display_lines.len() - 1u];
2013-09-27 23:01:58 -05:00
let s = format!("{}:{} ", fm.name, last_line + 1u);
write!(err, "{0:1$}...\n", "", s.len());
}
2013-02-08 18:01:39 -06:00
// FIXME (#3260)
// If there's one line at fault we can easily point to the problem
2013-05-14 04:52:12 -05:00
if lines.lines.len() == 1u {
2012-11-12 20:24:56 -06:00
let lo = cm.lookup_char_pos(sp.lo);
let mut digits = 0u;
let mut num = (lines.lines[0] + 1u) / 10u;
// how many digits must be indent past?
while num > 0u { num /= 10u; digits += 1u; }
// indent past |name:## | and the 0-offset column location
let left = fm.name.len() + digits + lo.col.to_uint() + 3u;
let mut s = ~"";
// Skip is the number of characters we need to skip because they are
// part of the 'filename:line ' part of the previous line.
let skip = fm.name.len() + digits + 3u;
do skip.times() {
s.push_char(' ');
}
let orig = fm.get_line(lines.lines[0] as int);
for pos in range(0u, left-skip) {
let curChar = (orig[pos] as char);
// Whenever a tab occurs on the previous line, we insert one on
// the error-point-squiggly-line as well (instead of a space).
// That way the squiggly line will usually appear in the correct
// position.
match curChar {
'\t' => s.push_char('\t'),
_ => s.push_char(' '),
};
}
write!(err, "{}", s);
let mut s = ~"^";
2012-11-12 20:24:56 -06:00
let hi = cm.lookup_char_pos(sp.hi);
if hi.col != lo.col {
// the ^ already takes up one space
let num_squigglies = hi.col.to_uint()-lo.col.to_uint()-1u;
do num_squigglies.times() {
s.push_char('~')
}
}
print_maybe_styled(s + "\n", term::attr::ForegroundColor(diagnosticcolor(lvl)));
}
}
fn print_macro_backtrace(cm: @codemap::CodeMap, sp: Span) {
for ei in sp.expn_info.iter() {
let ss = ei.callee.span.as_ref().map_default(~"", |span| cm.span_to_str(*span));
print_diagnostic(ss, note,
2013-09-27 23:01:58 -05:00
format!("in expansion of {}!", ei.callee.name));
2012-11-12 20:24:56 -06:00
let ss = cm.span_to_str(ei.call_site);
print_diagnostic(ss, note, "expansion site");
print_macro_backtrace(cm, ei.call_site);
}
}
2012-05-22 16:55:39 -05:00
pub fn expect<T:Clone>(diag: @mut span_handler,
opt: Option<T>,
msg: &fn() -> ~str) -> T {
2012-08-06 14:34:08 -05:00
match opt {
2013-07-02 14:47:32 -05:00
Some(ref t) => (*t).clone(),
None => diag.handler().bug(msg()),
2012-05-22 16:55:39 -05:00
}
}