diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 6a7b411823c..01eb14474a2 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -940,8 +940,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { None => ErrorOutputType::default(), Some(arg) => { - early_error(ErrorOutputType::default(), &format!("argument for --output must be tty or \ - json (instead was `{}`)", + early_error(ErrorOutputType::default(), &format!("argument for --output must be \ + tty or json (instead was `{}`)", arg)) } } diff --git a/src/libsyntax/errors/json.rs b/src/libsyntax/errors/json.rs index feae71f55d3..0b8da7a09b1 100644 --- a/src/libsyntax/errors/json.rs +++ b/src/libsyntax/errors/json.rs @@ -9,45 +9,225 @@ // except according to those terms. //! A JSON emitter for errors. +//! +//! This works by converting errors to a simplified structural format (see the +//! structs at the start of the file) and then serialising them. These should +//! contain as much information about the error as possible. +//! +//! The format of the JSON output should be considered *unstable*. For now the +//! structs at the end of this file (Diagnostic*) specify the error format. + +// FIXME spec the JSON output properly. + use codemap::{Span, CodeMap}; use diagnostics::registry::Registry; -use errors::{Level, DiagnosticBuilder, RenderSpan}; +use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan}; use errors::emitter::Emitter; use std::rc::Rc; +use std::io::{self, Write}; + +use rustc_serialize::json::as_json; pub struct JsonEmitter { - todo: i32 + dst: Box, + registry: Option, + cm: Rc, } impl JsonEmitter { pub fn basic() -> JsonEmitter { - JsonEmitter { - todo: 42, - } + JsonEmitter::stderr(None, Rc::new(CodeMap::new())) } pub fn stderr(registry: Option, code_map: Rc) -> JsonEmitter { JsonEmitter { - todo: 42, + dst: Box::new(io::stderr()), + registry: registry, + cm: code_map, } } } impl Emitter for JsonEmitter { - fn emit(&mut self, span: Option, msg: &str, code: Option<&str>, lvl: Level) { - unimplemented!(); - + fn emit(&mut self, span: Option, msg: &str, code: Option<&str>, level: Level) { + let data = Diagnostic::new(span, msg, code, level, self); + if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) { + panic!("failed to print diagnostics: {:?}", e); + } } - fn custom_emit(&mut self, sp: RenderSpan, msg: &str, lvl: Level) { - unimplemented!(); - + fn custom_emit(&mut self, sp: RenderSpan, msg: &str, level: Level) { + let data = Diagnostic::from_render_span(&sp, msg, level, self); + if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) { + panic!("failed to print diagnostics: {:?}", e); + } } fn emit_struct(&mut self, db: &DiagnosticBuilder) { - unimplemented!(); + let data = Diagnostic::from_diagnostic_builder(db, self); + if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) { + panic!("failed to print diagnostics: {:?}", e); + } + } +} + +// The following data types are provided just for serialisation. + +#[derive(RustcEncodable)] +struct Diagnostic<'a> { + /// The primary error message. + message: &'a str, + code: Option, + /// "error: internal compiler error", "error", "warning", "note", "help". + level: &'static str, + span: Option, + /// Assocaited diagnostic messages. + children: Vec>, +} + +#[derive(RustcEncodable)] +struct DiagnosticSpan { + file_name: String, + byte_start: u32, + byte_end: u32, + /// 1-based. + line_start: usize, + line_end: usize, + /// 1-based, character offset. + column_start: usize, + column_end: usize, +} + +#[derive(RustcEncodable)] +struct DiagnosticCode { + /// The code itself. + code: String, + /// An explanation for the code. + explanation: Option<&'static str>, +} + +impl<'a> Diagnostic<'a> { + fn new(span: Option, + msg: &'a str, + code: Option<&str>, + level: Level, + je: &JsonEmitter) + -> Diagnostic<'a> { + Diagnostic { + message: msg, + code: DiagnosticCode::map_opt_string(code.map(|c| c.to_owned()), je), + level: level.to_str(), + span: span.map(|sp| DiagnosticSpan::from_span(sp, je)), + children: vec![], + } + } + + fn from_render_span(span: &RenderSpan, + msg: &'a str, + level: Level, + je: &JsonEmitter) + -> Diagnostic<'a> { + Diagnostic { + msg: msg, + code: None, + level: level.to_str(), + span: Some(DiagnosticSpan::from_render_span(span, je)), + children: vec![], + } + } + + fn from_diagnostic_builder<'c>(db: &'c DiagnosticBuilder, + je: &JsonEmitter) + -> Diagnostic<'c> { + Diagnostic { + message: &db.message, + code: DiagnosticCode::map_opt_string(db.code.clone(), je), + level: db.level.to_str(), + span: db.span.map(|sp| DiagnosticSpan::from_span(sp, je)), + children: db.children.iter().map(|c| { + Diagnostic::from_sub_diagnostic(c, je) + }).collect(), + } + } + + fn from_sub_diagnostic<'c>(db: &'c SubDiagnostic, je: &JsonEmitter) -> Diagnostic<'c> { + Diagnostic { + message: &db.message, + code: None, + level: db.level.to_str(), + span: db.render_span.as_ref() + .map(|sp| DiagnosticSpan::from_render_span(sp, je)) + .or_else(|| db.span.map(|sp| DiagnosticSpan::from_span(sp, je))), + children: vec![], + } + } +} + +impl DiagnosticSpan { + fn from_span(span: Span, je: &JsonEmitter) -> DiagnosticSpan { + let start = je.cm.lookup_char_pos(span.lo); + let end = je.cm.lookup_char_pos(span.hi); + DiagnosticSpan { + file_name: start.file.name.clone(), + byte_start: span.lo.0, + byte_end: span.hi.0, + line_start: start.line, + line_end: end.line, + column_start: start.col.0 + 1, + column_end: end.col.0 + 1, + } + } + + fn from_render_span(span: &RenderSpan, je: &JsonEmitter) -> DiagnosticSpan { + match *span { + // FIXME(#30701) handle Suggestion properly + RenderSpan::FullSpan(sp) | RenderSpan::Suggestion(sp, _) => { + DiagnosticSpan::from_span(sp, je) + } + RenderSpan::EndSpan(span) => { + let end = je.cm.lookup_char_pos(span.hi); + DiagnosticSpan { + file_name: end.file.name.clone(), + byte_start: span.lo.0, + byte_end: span.hi.0, + line_start: 0, + line_end: end.line, + column_start: 0, + column_end: end.col.0 + 1, + } + } + RenderSpan::FileLine(span) => { + let start = je.cm.lookup_char_pos(span.lo); + let end = je.cm.lookup_char_pos(span.hi); + DiagnosticSpan { + file_name: start.file.name.clone(), + byte_start: span.lo.0, + byte_end: span.hi.0, + line_start: start.line, + line_end: end.line, + column_start: 0, + column_end: 0, + } + } + } + } +} + +impl DiagnosticCode { + fn map_opt_string(s: Option, je: &JsonEmitter) -> Option { + s.map(|s| { + + let explanation = je.registry + .as_ref() + .and_then(|registry| registry.find_description(&s)); + + DiagnosticCode { + code: s, + explanation: explanation, + } + }) } } diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs index 68f8caf755a..f269dee31d9 100644 --- a/src/libsyntax/errors/mod.rs +++ b/src/libsyntax/errors/mod.rs @@ -276,13 +276,12 @@ pub struct Handler { } impl Handler { - // TODO remove - pub fn new(color_config: ColorConfig, - registry: Option, - can_emit_warnings: bool, - treat_err_as_bug: bool, - cm: Rc) - -> Handler { + pub fn with_tty_emitter(color_config: ColorConfig, + registry: Option, + can_emit_warnings: bool, + treat_err_as_bug: bool, + cm: Rc) + -> Handler { let emitter = Box::new(EmitterWriter::stderr(color_config, registry, cm)); Handler::with_emitter(can_emit_warnings, treat_err_as_bug, emitter) } @@ -549,14 +548,7 @@ impl fmt::Display for Level { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use std::fmt::Display; - match *self { - Bug => "error: internal compiler error".fmt(f), - Fatal | Error => "error".fmt(f), - Warning => "warning".fmt(f), - Note => "note".fmt(f), - Help => "help".fmt(f), - Cancelled => unreachable!(), - } + self.to_str().fmt(f) } } @@ -570,6 +562,17 @@ impl Level { Cancelled => unreachable!(), } } + + fn to_str(self) -> &'static str { + match self { + Bug => "error: internal compiler error", + Fatal | Error => "error", + Warning => "warning", + Note => "note", + Help => "help", + Cancelled => panic!("Shouldn't call on cancelled error"), + } + } } pub fn expect(diag: &Handler, opt: Option, msg: M) -> T where diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index a1224565501..090b070433f 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -49,7 +49,7 @@ pub struct ParseSess { impl ParseSess { pub fn new() -> ParseSess { let cm = Rc::new(CodeMap::new()); - let handler = Handler::new(ColorConfig::Auto, None, true, false, cm.clone()); + let handler = Handler::with_tty_emitter(ColorConfig::Auto, None, true, false, cm.clone()); ParseSess::with_span_handler(handler, cm) }