]> git.proxmox.com Git - rustc.git/blame - src/librustc_errors/json.rs
New upstream version 1.46.0~beta.2+dfsg1
[rustc.git] / src / librustc_errors / json.rs
CommitLineData
9cc50fc6
SL
1//! A JSON emitter for errors.
2//!
3//! This works by converting errors to a simplified structural format (see the
3b2f2976 4//! structs at the start of the file) and then serializing them. These should
9cc50fc6
SL
5//! contain as much information about the error as possible.
6//!
7//! The format of the JSON output should be considered *unstable*. For now the
8//! structs at the end of this file (Diagnostic*) specify the error format.
9
9fa01778
XL
10// FIXME: spec the JSON output properly.
11
dfeec247 12use rustc_span::source_map::{FilePathMapping, SourceMap};
9cc50fc6 13
60c5eb7d 14use crate::emitter::{Emitter, HumanReadableErrorType};
dfeec247
XL
15use crate::registry::Registry;
16use crate::{Applicability, DiagnosticId};
17use crate::{CodeSuggestion, SubDiagnostic};
9cc50fc6 18
60c5eb7d 19use rustc_data_structures::sync::Lrc;
dfeec247
XL
20use rustc_span::hygiene::ExpnData;
21use rustc_span::{MultiSpan, Span, SpanLabel};
9cc50fc6 22use std::io::{self, Write};
48663c56 23use std::path::Path;
ff7c6d11 24use std::sync::{Arc, Mutex};
dfeec247 25use std::vec;
9cc50fc6 26
abe05a73 27use rustc_serialize::json::{as_json, as_pretty_json};
9cc50fc6 28
e74abb32
XL
29#[cfg(test)]
30mod tests;
31
9cc50fc6 32pub struct JsonEmitter {
8faf50e0 33 dst: Box<dyn Write + Send>,
9cc50fc6 34 registry: Option<Registry>,
60c5eb7d 35 sm: Lrc<SourceMap>,
abe05a73 36 pretty: bool,
0531ce1d 37 ui_testing: bool,
48663c56 38 json_rendered: HumanReadableErrorType,
f035d41b 39 terminal_width: Option<usize>,
74b04a01 40 macro_backtrace: bool,
9cc50fc6
SL
41}
42
43impl JsonEmitter {
48663c56
XL
44 pub fn stderr(
45 registry: Option<Registry>,
46 source_map: Lrc<SourceMap>,
47 pretty: bool,
48 json_rendered: HumanReadableErrorType,
f035d41b 49 terminal_width: Option<usize>,
74b04a01 50 macro_backtrace: bool,
48663c56 51 ) -> JsonEmitter {
c30ab7b3 52 JsonEmitter {
74b04a01 53 dst: Box::new(io::BufWriter::new(io::stderr())),
3b2f2976 54 registry,
a1dfa0c6 55 sm: source_map,
abe05a73 56 pretty,
0531ce1d 57 ui_testing: false,
48663c56 58 json_rendered,
f035d41b 59 terminal_width,
74b04a01 60 macro_backtrace,
c30ab7b3
SL
61 }
62 }
63
e1599b0c
XL
64 pub fn basic(
65 pretty: bool,
66 json_rendered: HumanReadableErrorType,
f035d41b 67 terminal_width: Option<usize>,
74b04a01 68 macro_backtrace: bool,
e1599b0c 69 ) -> JsonEmitter {
7cac9316 70 let file_path_mapping = FilePathMapping::empty();
dfeec247
XL
71 JsonEmitter::stderr(
72 None,
73 Lrc::new(SourceMap::new(file_path_mapping)),
74 pretty,
75 json_rendered,
f035d41b 76 terminal_width,
74b04a01 77 macro_backtrace,
dfeec247 78 )
9cc50fc6
SL
79 }
80
48663c56
XL
81 pub fn new(
82 dst: Box<dyn Write + Send>,
83 registry: Option<Registry>,
84 source_map: Lrc<SourceMap>,
85 pretty: bool,
86 json_rendered: HumanReadableErrorType,
f035d41b 87 terminal_width: Option<usize>,
74b04a01 88 macro_backtrace: bool,
48663c56 89 ) -> JsonEmitter {
9cc50fc6 90 JsonEmitter {
3b2f2976
XL
91 dst,
92 registry,
a1dfa0c6 93 sm: source_map,
abe05a73 94 pretty,
0531ce1d 95 ui_testing: false,
48663c56 96 json_rendered,
f035d41b 97 terminal_width,
74b04a01 98 macro_backtrace,
9cc50fc6
SL
99 }
100 }
0531ce1d
XL
101
102 pub fn ui_testing(self, ui_testing: bool) -> Self {
103 Self { ui_testing, ..self }
104 }
9cc50fc6
SL
105}
106
107impl Emitter for JsonEmitter {
60c5eb7d 108 fn emit_diagnostic(&mut self, diag: &crate::Diagnostic) {
e74abb32 109 let data = Diagnostic::from_errors_diagnostic(diag, self);
abe05a73
XL
110 let result = if self.pretty {
111 writeln!(&mut self.dst, "{}", as_pretty_json(&data))
112 } else {
113 writeln!(&mut self.dst, "{}", as_json(&data))
74b04a01
XL
114 }
115 .and_then(|_| self.dst.flush());
abe05a73 116 if let Err(e) = result {
9cc50fc6
SL
117 panic!("failed to print diagnostics: {:?}", e);
118 }
119 }
48663c56 120
dc9dc135
XL
121 fn emit_artifact_notification(&mut self, path: &Path, artifact_type: &str) {
122 let data = ArtifactNotification { artifact: path, emit: artifact_type };
48663c56
XL
123 let result = if self.pretty {
124 writeln!(&mut self.dst, "{}", as_pretty_json(&data))
125 } else {
126 writeln!(&mut self.dst, "{}", as_json(&data))
74b04a01
XL
127 }
128 .and_then(|_| self.dst.flush());
48663c56
XL
129 if let Err(e) = result {
130 panic!("failed to print notification: {:?}", e);
131 }
132 }
e74abb32 133
60c5eb7d 134 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
e74abb32
XL
135 Some(&self.sm)
136 }
137
138 fn should_show_explain(&self) -> bool {
139 match self.json_rendered {
140 HumanReadableErrorType::Short(_) => false,
141 _ => true,
142 }
143 }
9cc50fc6
SL
144}
145
146// The following data types are provided just for serialisation.
147
148#[derive(RustcEncodable)]
32a655c1 149struct Diagnostic {
9cc50fc6 150 /// The primary error message.
32a655c1 151 message: String,
9cc50fc6
SL
152 code: Option<DiagnosticCode>,
153 /// "error: internal compiler error", "error", "warning", "note", "help".
154 level: &'static str,
7453a54e 155 spans: Vec<DiagnosticSpan>,
a7813a04 156 /// Associated diagnostic messages.
32a655c1 157 children: Vec<Diagnostic>,
ff7c6d11 158 /// The message as rustc would render it.
a7813a04 159 rendered: Option<String>,
9cc50fc6
SL
160}
161
162#[derive(RustcEncodable)]
163struct DiagnosticSpan {
164 file_name: String,
165 byte_start: u32,
166 byte_end: u32,
167 /// 1-based.
168 line_start: usize,
169 line_end: usize,
170 /// 1-based, character offset.
171 column_start: usize,
172 column_end: usize,
a7813a04
XL
173 /// Is this a "primary" span -- meaning the point, or one of the points,
174 /// where the error occurred?
175 is_primary: bool,
54a0048b
SL
176 /// Source text from the start of line_start to the end of line_end.
177 text: Vec<DiagnosticSpanLine>,
a7813a04
XL
178 /// Label that should be placed at this location (if any)
179 label: Option<String>,
180 /// If we are suggesting a replacement, this will contain text
abe05a73 181 /// that should be sliced in atop this span.
a7813a04 182 suggested_replacement: Option<String>,
2c00a5a8 183 /// If the suggestion is approximate
83c7162d 184 suggestion_applicability: Option<Applicability>,
a7813a04
XL
185 /// Macro invocations that created the code at this span, if any.
186 expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
54a0048b
SL
187}
188
189#[derive(RustcEncodable)]
190struct DiagnosticSpanLine {
191 text: String,
a7813a04 192
54a0048b
SL
193 /// 1-based, character offset in self.text.
194 highlight_start: usize,
a7813a04 195
54a0048b 196 highlight_end: usize,
9cc50fc6
SL
197}
198
a7813a04
XL
199#[derive(RustcEncodable)]
200struct DiagnosticSpanMacroExpansion {
201 /// span where macro was applied to generate this code; note that
202 /// this may itself derive from a macro (if
203 /// `span.expansion.is_some()`)
204 span: DiagnosticSpan,
205
206 /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
207 macro_decl_name: String,
208
209 /// span where macro was defined (if known)
416331ca 210 def_site_span: DiagnosticSpan,
a7813a04
XL
211}
212
9cc50fc6
SL
213#[derive(RustcEncodable)]
214struct DiagnosticCode {
215 /// The code itself.
216 code: String,
217 /// An explanation for the code.
218 explanation: Option<&'static str>,
219}
220
48663c56
XL
221#[derive(RustcEncodable)]
222struct ArtifactNotification<'a> {
223 /// The path of the artifact.
224 artifact: &'a Path,
dc9dc135
XL
225 /// What kind of artifact we're emitting.
226 emit: &'a str,
48663c56
XL
227}
228
32a655c1 229impl Diagnostic {
dfeec247
XL
230 fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
231 let sugg = diag.suggestions.iter().map(|sugg| Diagnostic {
232 message: sugg.msg.clone(),
233 code: None,
234 level: "help",
235 spans: DiagnosticSpan::from_suggestion(sugg, je),
236 children: vec![],
237 rendered: None,
7cac9316 238 });
ff7c6d11
XL
239
240 // generate regular command line output and store it in the json
241
242 // A threadsafe buffer for writing.
243 #[derive(Default, Clone)]
244 struct BufWriter(Arc<Mutex<Vec<u8>>>);
245
246 impl Write for BufWriter {
247 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
248 self.0.lock().unwrap().write(buf)
249 }
250 fn flush(&mut self) -> io::Result<()> {
251 self.0.lock().unwrap().flush()
252 }
253 }
254 let buf = BufWriter::default();
255 let output = buf.clone();
dfeec247 256 je.json_rendered
f035d41b
XL
257 .new_emitter(
258 Box::new(buf),
259 Some(je.sm.clone()),
260 false,
261 je.terminal_width,
262 je.macro_backtrace,
263 )
dfeec247
XL
264 .ui_testing(je.ui_testing)
265 .emit_diagnostic(diag);
ff7c6d11
XL
266 let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
267 let output = String::from_utf8(output).unwrap();
268
9cc50fc6 269 Diagnostic {
e74abb32
XL
270 message: diag.message(),
271 code: DiagnosticCode::map_opt_string(diag.code.clone(), je),
272 level: diag.level.to_str(),
273 spans: DiagnosticSpan::from_multispan(&diag.span, je),
dfeec247
XL
274 children: diag
275 .children
276 .iter()
277 .map(|c| Diagnostic::from_sub_diagnostic(c, je))
278 .chain(sugg)
279 .collect(),
ff7c6d11 280 rendered: Some(output),
9cc50fc6
SL
281 }
282 }
283
e74abb32 284 fn from_sub_diagnostic(diag: &SubDiagnostic, je: &JsonEmitter) -> Diagnostic {
9cc50fc6 285 Diagnostic {
e74abb32 286 message: diag.message(),
9cc50fc6 287 code: None,
e74abb32 288 level: diag.level.to_str(),
dfeec247
XL
289 spans: diag
290 .render_span
291 .as_ref()
292 .map(|sp| DiagnosticSpan::from_multispan(sp, je))
293 .unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, je)),
9cc50fc6 294 children: vec![],
7cac9316 295 rendered: None,
9cc50fc6
SL
296 }
297 }
298}
299
300impl DiagnosticSpan {
dfeec247
XL
301 fn from_span_label(
302 span: SpanLabel,
303 suggestion: Option<(&String, Applicability)>,
304 je: &JsonEmitter,
305 ) -> DiagnosticSpan {
306 Self::from_span_etc(span.span, span.is_primary, span.label, suggestion, je)
9cc50fc6
SL
307 }
308
dfeec247
XL
309 fn from_span_etc(
310 span: Span,
311 is_primary: bool,
312 label: Option<String>,
313 suggestion: Option<(&String, Applicability)>,
314 je: &JsonEmitter,
315 ) -> DiagnosticSpan {
a7813a04
XL
316 // obtain the full backtrace from the `macro_backtrace`
317 // helper; in some ways, it'd be better to expand the
318 // backtrace ourselves, but the `macro_backtrace` helper makes
319 // some decision, such as dropping some frames, and I don't
320 // want to duplicate that logic here.
dfeec247
XL
321 let backtrace = span.macro_backtrace();
322 DiagnosticSpan::from_span_full(span, is_primary, label, suggestion, backtrace, je)
a7813a04
XL
323 }
324
dfeec247
XL
325 fn from_span_full(
326 span: Span,
327 is_primary: bool,
328 label: Option<String>,
329 suggestion: Option<(&String, Applicability)>,
330 mut backtrace: impl Iterator<Item = ExpnData>,
331 je: &JsonEmitter,
332 ) -> DiagnosticSpan {
a1dfa0c6
XL
333 let start = je.sm.lookup_char_pos(span.lo());
334 let end = je.sm.lookup_char_pos(span.hi());
a7813a04 335 let backtrace_step = backtrace.next().map(|bt| {
dfeec247 336 let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je);
416331ca 337 let def_site_span =
dfeec247 338 Self::from_span_full(bt.def_site, false, None, None, vec![].into_iter(), je);
a7813a04
XL
339 Box::new(DiagnosticSpanMacroExpansion {
340 span: call_site,
dfeec247 341 macro_decl_name: bt.kind.descr(),
3b2f2976 342 def_site_span,
a7813a04
XL
343 })
344 });
2c00a5a8 345
a7813a04 346 DiagnosticSpan {
ff7c6d11 347 file_name: start.file.name.to_string(),
e74abb32
XL
348 byte_start: start.file.original_relative_byte_pos(span.lo()).0,
349 byte_end: start.file.original_relative_byte_pos(span.hi()).0,
a7813a04
XL
350 line_start: start.line,
351 line_end: end.line,
352 column_start: start.col.0 + 1,
353 column_end: end.col.0 + 1,
3b2f2976 354 is_primary,
a7813a04 355 text: DiagnosticSpanLine::from_span(span, je),
2c00a5a8 356 suggested_replacement: suggestion.map(|x| x.0.clone()),
94b46f34 357 suggestion_applicability: suggestion.map(|x| x.1),
a7813a04 358 expansion: backtrace_step,
3b2f2976 359 label,
9cc50fc6
SL
360 }
361 }
9cc50fc6 362
a7813a04
XL
363 fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
364 msp.span_labels()
dfeec247
XL
365 .into_iter()
366 .map(|span_str| Self::from_span_label(span_str, None, je))
367 .collect()
a7813a04
XL
368 }
369
dfeec247
XL
370 fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
371 suggestion
372 .substitutions
373 .iter()
374 .flat_map(|substitution| {
375 substitution.parts.iter().map(move |suggestion_inner| {
376 let span_label =
377 SpanLabel { span: suggestion_inner.span, is_primary: true, label: None };
378 DiagnosticSpan::from_span_label(
379 span_label,
380 Some((&suggestion_inner.snippet, suggestion.applicability)),
381 je,
382 )
383 })
384 })
385 .collect()
a7813a04 386 }
54a0048b
SL
387}
388
389impl DiagnosticSpanLine {
dfeec247 390 fn line_from_source_file(
74b04a01 391 sf: &rustc_span::SourceFile,
dfeec247
XL
392 index: usize,
393 h_start: usize,
394 h_end: usize,
395 ) -> DiagnosticSpanLine {
54a0048b 396 DiagnosticSpanLine {
74b04a01 397 text: sf.get_line(index).map_or(String::new(), |l| l.into_owned()),
54a0048b
SL
398 highlight_start: h_start,
399 highlight_end: h_end,
400 }
401 }
402
9fa01778 403 /// Creates a list of DiagnosticSpanLines from span - each line with any part
54a0048b
SL
404 /// of `span` gets a DiagnosticSpanLine, with the highlight indicating the
405 /// `span` within the line.
a7813a04 406 fn from_span(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
dfeec247
XL
407 je.sm
408 .span_to_lines(span)
48663c56 409 .map(|lines| {
ba9703b0
XL
410 // We can't get any lines if the source is unavailable.
411 if !je.sm.ensure_source_file_source_present(lines.file.clone()) {
412 return vec![];
413 }
414
74b04a01 415 let sf = &*lines.file;
dfeec247
XL
416 lines
417 .lines
48663c56 418 .iter()
dfeec247
XL
419 .map(|line| {
420 DiagnosticSpanLine::line_from_source_file(
74b04a01 421 sf,
dfeec247
XL
422 line.line_index,
423 line.start_col.0 + 1,
424 line.end_col.0 + 1,
425 )
426 })
427 .collect()
428 })
429 .unwrap_or_else(|_| vec![])
54a0048b
SL
430 }
431}
432
9cc50fc6 433impl DiagnosticCode {
abe05a73 434 fn map_opt_string(s: Option<DiagnosticId>, je: &JsonEmitter) -> Option<DiagnosticCode> {
9cc50fc6 435 s.map(|s| {
abe05a73
XL
436 let s = match s {
437 DiagnosticId::Error(s) => s,
438 DiagnosticId::Lint(s) => s,
439 };
74b04a01
XL
440 let je_result =
441 je.registry.as_ref().map(|registry| registry.try_find_description(&s)).unwrap();
9cc50fc6 442
74b04a01 443 DiagnosticCode { code: s, explanation: je_result.unwrap_or(None) }
9cc50fc6
SL
444 })
445 }
446}