Skip to content

Commit b976d9e

Browse files
committed
Implement JSON error emission
[breaking-change] syntax::errors::Handler::new has been renamed to with_tty_emitter Many functions which used to take a syntax::errors::ColorConfig, now take a rustc::session::config::ErrorOutputType. If you previously used ColorConfig::Auto as a default, you should now use ErrorOutputType::default().
1 parent fd46c78 commit b976d9e

File tree

4 files changed

+213
-30
lines changed

4 files changed

+213
-30
lines changed

src/librustc/session/config.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -940,8 +940,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
940940
None => ErrorOutputType::default(),
941941

942942
Some(arg) => {
943-
early_error(ErrorOutputType::default(), &format!("argument for --output must be tty or \
944-
json (instead was `{}`)",
943+
early_error(ErrorOutputType::default(), &format!("argument for --output must be \
944+
tty or json (instead was `{}`)",
945945
arg))
946946
}
947947
}

src/libsyntax/errors/json.rs

Lines changed: 192 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,45 +9,225 @@
99
// except according to those terms.
1010

1111
//! A JSON emitter for errors.
12+
//!
13+
//! This works by converting errors to a simplified structural format (see the
14+
//! structs at the start of the file) and then serialising them. These should
15+
//! contain as much information about the error as possible.
16+
//!
17+
//! The format of the JSON output should be considered *unstable*. For now the
18+
//! structs at the end of this file (Diagnostic*) specify the error format.
19+
20+
// FIXME spec the JSON output properly.
21+
1222

1323
use codemap::{Span, CodeMap};
1424
use diagnostics::registry::Registry;
15-
use errors::{Level, DiagnosticBuilder, RenderSpan};
25+
use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan};
1626
use errors::emitter::Emitter;
1727

1828
use std::rc::Rc;
29+
use std::io::{self, Write};
30+
31+
use rustc_serialize::json::as_json;
1932

2033
pub struct JsonEmitter {
21-
todo: i32
34+
dst: Box<Write + Send>,
35+
registry: Option<Registry>,
36+
cm: Rc<CodeMap>,
2237
}
2338

2439
impl JsonEmitter {
2540
pub fn basic() -> JsonEmitter {
26-
JsonEmitter {
27-
todo: 42,
28-
}
41+
JsonEmitter::stderr(None, Rc::new(CodeMap::new()))
2942
}
3043

3144
pub fn stderr(registry: Option<Registry>,
3245
code_map: Rc<CodeMap>) -> JsonEmitter {
3346
JsonEmitter {
34-
todo: 42,
47+
dst: Box::new(io::stderr()),
48+
registry: registry,
49+
cm: code_map,
3550
}
3651
}
3752
}
3853

3954
impl Emitter for JsonEmitter {
40-
fn emit(&mut self, span: Option<Span>, msg: &str, code: Option<&str>, lvl: Level) {
41-
unimplemented!();
55+
fn emit(&mut self, span: Option<Span>, msg: &str, code: Option<&str>, level: Level) {
56+
let data = Diagnostic::new(span, msg, code, level, self);
57+
if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
58+
panic!("failed to print diagnostics: {:?}", e);
59+
}
60+
}
61+
62+
fn custom_emit(&mut self, sp: RenderSpan, msg: &str, level: Level) {
63+
let data = Diagnostic::from_render_span(&sp, msg, level, self);
64+
if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
65+
panic!("failed to print diagnostics: {:?}", e);
66+
}
67+
}
68+
69+
fn emit_struct(&mut self, db: &DiagnosticBuilder) {
70+
let data = Diagnostic::from_diagnostic_builder(db, self);
71+
if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
72+
panic!("failed to print diagnostics: {:?}", e);
73+
}
74+
}
75+
}
76+
77+
// The following data types are provided just for serialisation.
78+
79+
#[derive(RustcEncodable)]
80+
struct Diagnostic<'a> {
81+
/// The primary error message.
82+
message: &'a str,
83+
code: Option<DiagnosticCode>,
84+
/// "error: internal compiler error", "error", "warning", "note", "help".
85+
level: &'static str,
86+
span: Option<DiagnosticSpan>,
87+
/// Assocaited diagnostic messages.
88+
children: Vec<Diagnostic<'a>>,
89+
}
90+
91+
#[derive(RustcEncodable)]
92+
struct DiagnosticSpan {
93+
file_name: String,
94+
byte_start: u32,
95+
byte_end: u32,
96+
/// 1-based.
97+
line_start: usize,
98+
line_end: usize,
99+
/// 1-based, character offset.
100+
column_start: usize,
101+
column_end: usize,
102+
}
42103

104+
#[derive(RustcEncodable)]
105+
struct DiagnosticCode {
106+
/// The code itself.
107+
code: String,
108+
/// An explanation for the code.
109+
explanation: Option<&'static str>,
110+
}
111+
112+
impl<'a> Diagnostic<'a> {
113+
fn new(span: Option<Span>,
114+
msg: &'a str,
115+
code: Option<&str>,
116+
level: Level,
117+
je: &JsonEmitter)
118+
-> Diagnostic<'a> {
119+
Diagnostic {
120+
message: msg,
121+
code: DiagnosticCode::map_opt_string(code.map(|c| c.to_owned()), je),
122+
level: level.to_str(),
123+
span: span.map(|sp| DiagnosticSpan::from_span(sp, je)),
124+
children: vec![],
125+
}
43126
}
44127

45-
fn custom_emit(&mut self, sp: RenderSpan, msg: &str, lvl: Level) {
46-
unimplemented!();
128+
fn from_render_span(span: &RenderSpan,
129+
msg: &'a str,
130+
level: Level,
131+
je: &JsonEmitter)
132+
-> Diagnostic<'a> {
133+
Diagnostic {
134+
msg: msg,
135+
code: None,
136+
level: level.to_str(),
137+
span: Some(DiagnosticSpan::from_render_span(span, je)),
138+
children: vec![],
139+
}
140+
}
47141

142+
fn from_diagnostic_builder<'c>(db: &'c DiagnosticBuilder,
143+
je: &JsonEmitter)
144+
-> Diagnostic<'c> {
145+
Diagnostic {
146+
message: &db.message,
147+
code: DiagnosticCode::map_opt_string(db.code.clone(), je),
148+
level: db.level.to_str(),
149+
span: db.span.map(|sp| DiagnosticSpan::from_span(sp, je)),
150+
children: db.children.iter().map(|c| {
151+
Diagnostic::from_sub_diagnostic(c, je)
152+
}).collect(),
153+
}
48154
}
49155

50-
fn emit_struct(&mut self, db: &DiagnosticBuilder) {
51-
unimplemented!();
156+
fn from_sub_diagnostic<'c>(db: &'c SubDiagnostic, je: &JsonEmitter) -> Diagnostic<'c> {
157+
Diagnostic {
158+
message: &db.message,
159+
code: None,
160+
level: db.level.to_str(),
161+
span: db.render_span.as_ref()
162+
.map(|sp| DiagnosticSpan::from_render_span(sp, je))
163+
.or_else(|| db.span.map(|sp| DiagnosticSpan::from_span(sp, je))),
164+
children: vec![],
165+
}
166+
}
167+
}
168+
169+
impl DiagnosticSpan {
170+
fn from_span(span: Span, je: &JsonEmitter) -> DiagnosticSpan {
171+
let start = je.cm.lookup_char_pos(span.lo);
172+
let end = je.cm.lookup_char_pos(span.hi);
173+
DiagnosticSpan {
174+
file_name: start.file.name.clone(),
175+
byte_start: span.lo.0,
176+
byte_end: span.hi.0,
177+
line_start: start.line,
178+
line_end: end.line,
179+
column_start: start.col.0 + 1,
180+
column_end: end.col.0 + 1,
181+
}
182+
}
183+
184+
fn from_render_span(span: &RenderSpan, je: &JsonEmitter) -> DiagnosticSpan {
185+
match *span {
186+
// FIXME(#30701) handle Suggestion properly
187+
RenderSpan::FullSpan(sp) | RenderSpan::Suggestion(sp, _) => {
188+
DiagnosticSpan::from_span(sp, je)
189+
}
190+
RenderSpan::EndSpan(span) => {
191+
let end = je.cm.lookup_char_pos(span.hi);
192+
DiagnosticSpan {
193+
file_name: end.file.name.clone(),
194+
byte_start: span.lo.0,
195+
byte_end: span.hi.0,
196+
line_start: 0,
197+
line_end: end.line,
198+
column_start: 0,
199+
column_end: end.col.0 + 1,
200+
}
201+
}
202+
RenderSpan::FileLine(span) => {
203+
let start = je.cm.lookup_char_pos(span.lo);
204+
let end = je.cm.lookup_char_pos(span.hi);
205+
DiagnosticSpan {
206+
file_name: start.file.name.clone(),
207+
byte_start: span.lo.0,
208+
byte_end: span.hi.0,
209+
line_start: start.line,
210+
line_end: end.line,
211+
column_start: 0,
212+
column_end: 0,
213+
}
214+
}
215+
}
216+
}
217+
}
218+
219+
impl DiagnosticCode {
220+
fn map_opt_string(s: Option<String>, je: &JsonEmitter) -> Option<DiagnosticCode> {
221+
s.map(|s| {
222+
223+
let explanation = je.registry
224+
.as_ref()
225+
.and_then(|registry| registry.find_description(&s));
226+
227+
DiagnosticCode {
228+
code: s,
229+
explanation: explanation,
230+
}
231+
})
52232
}
53233
}

src/libsyntax/errors/mod.rs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -276,13 +276,12 @@ pub struct Handler {
276276
}
277277

278278
impl Handler {
279-
// TODO remove
280-
pub fn new(color_config: ColorConfig,
281-
registry: Option<diagnostics::registry::Registry>,
282-
can_emit_warnings: bool,
283-
treat_err_as_bug: bool,
284-
cm: Rc<codemap::CodeMap>)
285-
-> Handler {
279+
pub fn with_tty_emitter(color_config: ColorConfig,
280+
registry: Option<diagnostics::registry::Registry>,
281+
can_emit_warnings: bool,
282+
treat_err_as_bug: bool,
283+
cm: Rc<codemap::CodeMap>)
284+
-> Handler {
286285
let emitter = Box::new(EmitterWriter::stderr(color_config, registry, cm));
287286
Handler::with_emitter(can_emit_warnings, treat_err_as_bug, emitter)
288287
}
@@ -549,14 +548,7 @@ impl fmt::Display for Level {
549548
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
550549
use std::fmt::Display;
551550

552-
match *self {
553-
Bug => "error: internal compiler error".fmt(f),
554-
Fatal | Error => "error".fmt(f),
555-
Warning => "warning".fmt(f),
556-
Note => "note".fmt(f),
557-
Help => "help".fmt(f),
558-
Cancelled => unreachable!(),
559-
}
551+
self.to_str().fmt(f)
560552
}
561553
}
562554

@@ -570,6 +562,17 @@ impl Level {
570562
Cancelled => unreachable!(),
571563
}
572564
}
565+
566+
fn to_str(self) -> &'static str {
567+
match self {
568+
Bug => "error: internal compiler error",
569+
Fatal | Error => "error",
570+
Warning => "warning",
571+
Note => "note",
572+
Help => "help",
573+
Cancelled => panic!("Shouldn't call on cancelled error"),
574+
}
575+
}
573576
}
574577

575578
pub fn expect<T, M>(diag: &Handler, opt: Option<T>, msg: M) -> T where

src/libsyntax/parse/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub struct ParseSess {
4949
impl ParseSess {
5050
pub fn new() -> ParseSess {
5151
let cm = Rc::new(CodeMap::new());
52-
let handler = Handler::new(ColorConfig::Auto, None, true, false, cm.clone());
52+
let handler = Handler::with_tty_emitter(ColorConfig::Auto, None, true, false, cm.clone());
5353
ParseSess::with_span_handler(handler, cm)
5454
}
5555

0 commit comments

Comments
 (0)