|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
11 | 11 | //! 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 | + |
12 | 22 |
|
13 | 23 | use codemap::{Span, CodeMap};
|
14 | 24 | use diagnostics::registry::Registry;
|
15 |
| -use errors::{Level, DiagnosticBuilder, RenderSpan}; |
| 25 | +use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan}; |
16 | 26 | use errors::emitter::Emitter;
|
17 | 27 |
|
18 | 28 | use std::rc::Rc;
|
| 29 | +use std::io::{self, Write}; |
| 30 | + |
| 31 | +use rustc_serialize::json::as_json; |
19 | 32 |
|
20 | 33 | pub struct JsonEmitter {
|
21 |
| - todo: i32 |
| 34 | + dst: Box<Write + Send>, |
| 35 | + registry: Option<Registry>, |
| 36 | + cm: Rc<CodeMap>, |
22 | 37 | }
|
23 | 38 |
|
24 | 39 | impl JsonEmitter {
|
25 | 40 | pub fn basic() -> JsonEmitter {
|
26 |
| - JsonEmitter { |
27 |
| - todo: 42, |
28 |
| - } |
| 41 | + JsonEmitter::stderr(None, Rc::new(CodeMap::new())) |
29 | 42 | }
|
30 | 43 |
|
31 | 44 | pub fn stderr(registry: Option<Registry>,
|
32 | 45 | code_map: Rc<CodeMap>) -> JsonEmitter {
|
33 | 46 | JsonEmitter {
|
34 |
| - todo: 42, |
| 47 | + dst: Box::new(io::stderr()), |
| 48 | + registry: registry, |
| 49 | + cm: code_map, |
35 | 50 | }
|
36 | 51 | }
|
37 | 52 | }
|
38 | 53 |
|
39 | 54 | 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 | +} |
42 | 103 |
|
| 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 | + } |
43 | 126 | }
|
44 | 127 |
|
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 | + } |
47 | 141 |
|
| 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 | + } |
48 | 154 | }
|
49 | 155 |
|
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 | + }) |
52 | 232 | }
|
53 | 233 | }
|
0 commit comments