diff options
| author | bors <bors@rust-lang.org> | 2016-01-15 01:52:01 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2016-01-15 01:52:01 +0000 |
| commit | d8869d3487a569b4a6b86c1b585cc15db48abc4a (patch) | |
| tree | f061c00a00e41e3518bf7f8b16579b5853ac6b3b /src/libsyntax/errors | |
| parent | 2fb0c5ebcf6d912224532265776fb96febea9797 (diff) | |
| parent | 82f8e5ce84c83b02fbfa720c6841f12db1a55603 (diff) | |
| download | rust-d8869d3487a569b4a6b86c1b585cc15db48abc4a.tar.gz rust-d8869d3487a569b4a6b86c1b585cc15db48abc4a.zip | |
Auto merge of #30711 - nrc:json-errs, r=huonw
The compiler can emit errors and warning in JSON format. This is a more easily machine readable form then the usual error output. Closes #10492, closes #14863.
Diffstat (limited to 'src/libsyntax/errors')
| -rw-r--r-- | src/libsyntax/errors/emitter.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/errors/json.rs | 233 | ||||
| -rw-r--r-- | src/libsyntax/errors/mod.rs | 33 |
3 files changed, 253 insertions, 15 deletions
diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs index a7bfdedf718..c21bf1e6a1f 100644 --- a/src/libsyntax/errors/emitter.rs +++ b/src/libsyntax/errors/emitter.rs @@ -43,7 +43,7 @@ pub trait Emitter { /// maximum number of lines we will print for each error; arbitrary. const MAX_LINES: usize = 6; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ColorConfig { Auto, Always, diff --git a/src/libsyntax/errors/json.rs b/src/libsyntax/errors/json.rs new file mode 100644 index 00000000000..713190ef419 --- /dev/null +++ b/src/libsyntax/errors/json.rs @@ -0,0 +1,233 @@ +// Copyright 2015 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. + +//! 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, SubDiagnostic, RenderSpan}; +use errors::emitter::Emitter; + +use std::rc::Rc; +use std::io::{self, Write}; + +use rustc_serialize::json::as_json; + +pub struct JsonEmitter { + dst: Box<Write + Send>, + registry: Option<Registry>, + cm: Rc<CodeMap>, +} + +impl JsonEmitter { + pub fn basic() -> JsonEmitter { + JsonEmitter::stderr(None, Rc::new(CodeMap::new())) + } + + pub fn stderr(registry: Option<Registry>, + code_map: Rc<CodeMap>) -> JsonEmitter { + JsonEmitter { + dst: Box::new(io::stderr()), + registry: registry, + cm: code_map, + } + } +} + +impl Emitter for JsonEmitter { + fn emit(&mut self, span: Option<Span>, 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, 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) { + 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<DiagnosticCode>, + /// "error: internal compiler error", "error", "warning", "note", "help". + level: &'static str, + span: Option<DiagnosticSpan>, + /// Assocaited diagnostic messages. + children: Vec<Diagnostic<'a>>, +} + +#[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<Span>, + 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 { + message: 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<String>, je: &JsonEmitter) -> Option<DiagnosticCode> { + 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 a2fae975148..f269dee31d9 100644 --- a/src/libsyntax/errors/mod.rs +++ b/src/libsyntax/errors/mod.rs @@ -24,6 +24,7 @@ use std::rc::Rc; use term; pub mod emitter; +pub mod json; #[derive(Clone)] pub enum RenderSpan { @@ -275,12 +276,12 @@ pub struct Handler { } impl Handler { - pub fn new(color_config: ColorConfig, - registry: Option<diagnostics::registry::Registry>, - can_emit_warnings: bool, - treat_err_as_bug: bool, - cm: Rc<codemap::CodeMap>) - -> Handler { + pub fn with_tty_emitter(color_config: ColorConfig, + registry: Option<diagnostics::registry::Registry>, + can_emit_warnings: bool, + treat_err_as_bug: bool, + cm: Rc<codemap::CodeMap>) + -> Handler { let emitter = Box::new(EmitterWriter::stderr(color_config, registry, cm)); Handler::with_emitter(can_emit_warnings, treat_err_as_bug, emitter) } @@ -547,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) } } @@ -568,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<T, M>(diag: &Handler, opt: Option<T>, msg: M) -> T where |
