]>
Commit | Line | Data |
---|---|---|
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 | 12 | use rustc_span::source_map::{FilePathMapping, SourceMap}; |
9cc50fc6 | 13 | |
60c5eb7d | 14 | use crate::emitter::{Emitter, HumanReadableErrorType}; |
dfeec247 | 15 | use crate::registry::Registry; |
2b03887a | 16 | use crate::translation::{to_fluent_args, Translate}; |
29967ef6 | 17 | use crate::DiagnosticId; |
04454e1e FG |
18 | use crate::{ |
19 | CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic, | |
20 | }; | |
136023e0 | 21 | use rustc_lint_defs::Applicability; |
9cc50fc6 | 22 | |
60c5eb7d | 23 | use rustc_data_structures::sync::Lrc; |
04454e1e | 24 | use rustc_error_messages::FluentArgs; |
dfeec247 | 25 | use rustc_span::hygiene::ExpnData; |
04454e1e | 26 | use rustc_span::Span; |
9cc50fc6 | 27 | use std::io::{self, Write}; |
48663c56 | 28 | use std::path::Path; |
ff7c6d11 | 29 | use std::sync::{Arc, Mutex}; |
dfeec247 | 30 | use std::vec; |
9cc50fc6 | 31 | |
923072b8 | 32 | use serde::Serialize; |
9cc50fc6 | 33 | |
e74abb32 XL |
34 | #[cfg(test)] |
35 | mod tests; | |
36 | ||
9cc50fc6 | 37 | pub struct JsonEmitter { |
8faf50e0 | 38 | dst: Box<dyn Write + Send>, |
9cc50fc6 | 39 | registry: Option<Registry>, |
60c5eb7d | 40 | sm: Lrc<SourceMap>, |
04454e1e FG |
41 | fluent_bundle: Option<Lrc<FluentBundle>>, |
42 | fallback_bundle: LazyFallbackBundle, | |
abe05a73 | 43 | pretty: bool, |
0531ce1d | 44 | ui_testing: bool, |
48663c56 | 45 | json_rendered: HumanReadableErrorType, |
064997fb | 46 | diagnostic_width: Option<usize>, |
74b04a01 | 47 | macro_backtrace: bool, |
9cc50fc6 SL |
48 | } |
49 | ||
50 | impl JsonEmitter { | |
48663c56 XL |
51 | pub fn stderr( |
52 | registry: Option<Registry>, | |
53 | source_map: Lrc<SourceMap>, | |
04454e1e FG |
54 | fluent_bundle: Option<Lrc<FluentBundle>>, |
55 | fallback_bundle: LazyFallbackBundle, | |
48663c56 XL |
56 | pretty: bool, |
57 | json_rendered: HumanReadableErrorType, | |
064997fb | 58 | diagnostic_width: Option<usize>, |
74b04a01 | 59 | macro_backtrace: bool, |
48663c56 | 60 | ) -> JsonEmitter { |
c30ab7b3 | 61 | JsonEmitter { |
74b04a01 | 62 | dst: Box::new(io::BufWriter::new(io::stderr())), |
3b2f2976 | 63 | registry, |
a1dfa0c6 | 64 | sm: source_map, |
04454e1e FG |
65 | fluent_bundle, |
66 | fallback_bundle, | |
abe05a73 | 67 | pretty, |
0531ce1d | 68 | ui_testing: false, |
48663c56 | 69 | json_rendered, |
064997fb | 70 | diagnostic_width, |
74b04a01 | 71 | macro_backtrace, |
c30ab7b3 SL |
72 | } |
73 | } | |
74 | ||
e1599b0c XL |
75 | pub fn basic( |
76 | pretty: bool, | |
77 | json_rendered: HumanReadableErrorType, | |
04454e1e FG |
78 | fluent_bundle: Option<Lrc<FluentBundle>>, |
79 | fallback_bundle: LazyFallbackBundle, | |
064997fb | 80 | diagnostic_width: Option<usize>, |
74b04a01 | 81 | macro_backtrace: bool, |
e1599b0c | 82 | ) -> JsonEmitter { |
7cac9316 | 83 | let file_path_mapping = FilePathMapping::empty(); |
dfeec247 XL |
84 | JsonEmitter::stderr( |
85 | None, | |
86 | Lrc::new(SourceMap::new(file_path_mapping)), | |
04454e1e FG |
87 | fluent_bundle, |
88 | fallback_bundle, | |
dfeec247 XL |
89 | pretty, |
90 | json_rendered, | |
064997fb | 91 | diagnostic_width, |
74b04a01 | 92 | macro_backtrace, |
dfeec247 | 93 | ) |
9cc50fc6 SL |
94 | } |
95 | ||
48663c56 XL |
96 | pub fn new( |
97 | dst: Box<dyn Write + Send>, | |
98 | registry: Option<Registry>, | |
99 | source_map: Lrc<SourceMap>, | |
04454e1e FG |
100 | fluent_bundle: Option<Lrc<FluentBundle>>, |
101 | fallback_bundle: LazyFallbackBundle, | |
48663c56 XL |
102 | pretty: bool, |
103 | json_rendered: HumanReadableErrorType, | |
064997fb | 104 | diagnostic_width: Option<usize>, |
74b04a01 | 105 | macro_backtrace: bool, |
48663c56 | 106 | ) -> JsonEmitter { |
9cc50fc6 | 107 | JsonEmitter { |
3b2f2976 XL |
108 | dst, |
109 | registry, | |
a1dfa0c6 | 110 | sm: source_map, |
04454e1e FG |
111 | fluent_bundle, |
112 | fallback_bundle, | |
abe05a73 | 113 | pretty, |
0531ce1d | 114 | ui_testing: false, |
48663c56 | 115 | json_rendered, |
064997fb | 116 | diagnostic_width, |
74b04a01 | 117 | macro_backtrace, |
9cc50fc6 SL |
118 | } |
119 | } | |
0531ce1d XL |
120 | |
121 | pub fn ui_testing(self, ui_testing: bool) -> Self { | |
122 | Self { ui_testing, ..self } | |
123 | } | |
9cc50fc6 SL |
124 | } |
125 | ||
f2b60f7d FG |
126 | impl Translate for JsonEmitter { |
127 | fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { | |
128 | self.fluent_bundle.as_ref() | |
129 | } | |
130 | ||
131 | fn fallback_fluent_bundle(&self) -> &FluentBundle { | |
132 | &**self.fallback_bundle | |
133 | } | |
134 | } | |
135 | ||
9cc50fc6 | 136 | impl Emitter for JsonEmitter { |
60c5eb7d | 137 | fn emit_diagnostic(&mut self, diag: &crate::Diagnostic) { |
e74abb32 | 138 | let data = Diagnostic::from_errors_diagnostic(diag, self); |
abe05a73 | 139 | let result = if self.pretty { |
923072b8 | 140 | writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&data).unwrap()) |
abe05a73 | 141 | } else { |
923072b8 | 142 | writeln!(&mut self.dst, "{}", serde_json::to_string(&data).unwrap()) |
74b04a01 XL |
143 | } |
144 | .and_then(|_| self.dst.flush()); | |
abe05a73 | 145 | if let Err(e) = result { |
9cc50fc6 SL |
146 | panic!("failed to print diagnostics: {:?}", e); |
147 | } | |
148 | } | |
48663c56 | 149 | |
dc9dc135 XL |
150 | fn emit_artifact_notification(&mut self, path: &Path, artifact_type: &str) { |
151 | let data = ArtifactNotification { artifact: path, emit: artifact_type }; | |
48663c56 | 152 | let result = if self.pretty { |
923072b8 | 153 | writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&data).unwrap()) |
48663c56 | 154 | } else { |
923072b8 | 155 | writeln!(&mut self.dst, "{}", serde_json::to_string(&data).unwrap()) |
74b04a01 XL |
156 | } |
157 | .and_then(|_| self.dst.flush()); | |
48663c56 XL |
158 | if let Err(e) = result { |
159 | panic!("failed to print notification: {:?}", e); | |
160 | } | |
161 | } | |
e74abb32 | 162 | |
136023e0 | 163 | fn emit_future_breakage_report(&mut self, diags: Vec<crate::Diagnostic>) { |
29967ef6 XL |
164 | let data: Vec<FutureBreakageItem> = diags |
165 | .into_iter() | |
136023e0 | 166 | .map(|mut diag| { |
29967ef6 | 167 | if diag.level == crate::Level::Allow { |
923072b8 | 168 | diag.level = crate::Level::Warning(None); |
29967ef6 | 169 | } |
136023e0 | 170 | FutureBreakageItem { diagnostic: Diagnostic::from_errors_diagnostic(&diag, self) } |
29967ef6 XL |
171 | }) |
172 | .collect(); | |
173 | let report = FutureIncompatReport { future_incompat_report: data }; | |
174 | let result = if self.pretty { | |
923072b8 | 175 | writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&report).unwrap()) |
29967ef6 | 176 | } else { |
923072b8 | 177 | writeln!(&mut self.dst, "{}", serde_json::to_string(&report).unwrap()) |
29967ef6 XL |
178 | } |
179 | .and_then(|_| self.dst.flush()); | |
180 | if let Err(e) = result { | |
181 | panic!("failed to print future breakage report: {:?}", e); | |
182 | } | |
183 | } | |
184 | ||
04454e1e FG |
185 | fn emit_unused_externs(&mut self, lint_level: rustc_lint_defs::Level, unused_externs: &[&str]) { |
186 | let lint_level = lint_level.as_str(); | |
cdc7bbd5 XL |
187 | let data = UnusedExterns { lint_level, unused_extern_names: unused_externs }; |
188 | let result = if self.pretty { | |
923072b8 | 189 | writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&data).unwrap()) |
cdc7bbd5 | 190 | } else { |
923072b8 | 191 | writeln!(&mut self.dst, "{}", serde_json::to_string(&data).unwrap()) |
cdc7bbd5 XL |
192 | } |
193 | .and_then(|_| self.dst.flush()); | |
194 | if let Err(e) = result { | |
195 | panic!("failed to print unused externs: {:?}", e); | |
196 | } | |
197 | } | |
198 | ||
60c5eb7d | 199 | fn source_map(&self) -> Option<&Lrc<SourceMap>> { |
e74abb32 XL |
200 | Some(&self.sm) |
201 | } | |
202 | ||
203 | fn should_show_explain(&self) -> bool { | |
29967ef6 | 204 | !matches!(self.json_rendered, HumanReadableErrorType::Short(_)) |
e74abb32 | 205 | } |
9cc50fc6 SL |
206 | } |
207 | ||
208 | // The following data types are provided just for serialisation. | |
209 | ||
923072b8 | 210 | #[derive(Serialize)] |
32a655c1 | 211 | struct Diagnostic { |
9cc50fc6 | 212 | /// The primary error message. |
32a655c1 | 213 | message: String, |
9cc50fc6 SL |
214 | code: Option<DiagnosticCode>, |
215 | /// "error: internal compiler error", "error", "warning", "note", "help". | |
216 | level: &'static str, | |
7453a54e | 217 | spans: Vec<DiagnosticSpan>, |
a7813a04 | 218 | /// Associated diagnostic messages. |
32a655c1 | 219 | children: Vec<Diagnostic>, |
ff7c6d11 | 220 | /// The message as rustc would render it. |
a7813a04 | 221 | rendered: Option<String>, |
9cc50fc6 SL |
222 | } |
223 | ||
923072b8 | 224 | #[derive(Serialize)] |
9cc50fc6 SL |
225 | struct DiagnosticSpan { |
226 | file_name: String, | |
227 | byte_start: u32, | |
228 | byte_end: u32, | |
229 | /// 1-based. | |
230 | line_start: usize, | |
231 | line_end: usize, | |
232 | /// 1-based, character offset. | |
233 | column_start: usize, | |
234 | column_end: usize, | |
a7813a04 XL |
235 | /// Is this a "primary" span -- meaning the point, or one of the points, |
236 | /// where the error occurred? | |
237 | is_primary: bool, | |
54a0048b SL |
238 | /// Source text from the start of line_start to the end of line_end. |
239 | text: Vec<DiagnosticSpanLine>, | |
a7813a04 XL |
240 | /// Label that should be placed at this location (if any) |
241 | label: Option<String>, | |
242 | /// If we are suggesting a replacement, this will contain text | |
abe05a73 | 243 | /// that should be sliced in atop this span. |
a7813a04 | 244 | suggested_replacement: Option<String>, |
2c00a5a8 | 245 | /// If the suggestion is approximate |
83c7162d | 246 | suggestion_applicability: Option<Applicability>, |
a7813a04 XL |
247 | /// Macro invocations that created the code at this span, if any. |
248 | expansion: Option<Box<DiagnosticSpanMacroExpansion>>, | |
54a0048b SL |
249 | } |
250 | ||
923072b8 | 251 | #[derive(Serialize)] |
54a0048b SL |
252 | struct DiagnosticSpanLine { |
253 | text: String, | |
a7813a04 | 254 | |
54a0048b SL |
255 | /// 1-based, character offset in self.text. |
256 | highlight_start: usize, | |
a7813a04 | 257 | |
54a0048b | 258 | highlight_end: usize, |
9cc50fc6 SL |
259 | } |
260 | ||
923072b8 | 261 | #[derive(Serialize)] |
a7813a04 XL |
262 | struct DiagnosticSpanMacroExpansion { |
263 | /// span where macro was applied to generate this code; note that | |
264 | /// this may itself derive from a macro (if | |
265 | /// `span.expansion.is_some()`) | |
266 | span: DiagnosticSpan, | |
267 | ||
268 | /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]") | |
269 | macro_decl_name: String, | |
270 | ||
271 | /// span where macro was defined (if known) | |
416331ca | 272 | def_site_span: DiagnosticSpan, |
a7813a04 XL |
273 | } |
274 | ||
923072b8 | 275 | #[derive(Serialize)] |
9cc50fc6 SL |
276 | struct DiagnosticCode { |
277 | /// The code itself. | |
278 | code: String, | |
279 | /// An explanation for the code. | |
280 | explanation: Option<&'static str>, | |
281 | } | |
282 | ||
923072b8 | 283 | #[derive(Serialize)] |
48663c56 XL |
284 | struct ArtifactNotification<'a> { |
285 | /// The path of the artifact. | |
286 | artifact: &'a Path, | |
dc9dc135 XL |
287 | /// What kind of artifact we're emitting. |
288 | emit: &'a str, | |
48663c56 XL |
289 | } |
290 | ||
923072b8 | 291 | #[derive(Serialize)] |
29967ef6 | 292 | struct FutureBreakageItem { |
29967ef6 XL |
293 | diagnostic: Diagnostic, |
294 | } | |
295 | ||
923072b8 | 296 | #[derive(Serialize)] |
29967ef6 XL |
297 | struct FutureIncompatReport { |
298 | future_incompat_report: Vec<FutureBreakageItem>, | |
299 | } | |
300 | ||
cdc7bbd5 XL |
301 | // NOTE: Keep this in sync with the equivalent structs in rustdoc's |
302 | // doctest component (as well as cargo). | |
303 | // We could unify this struct the one in rustdoc but they have different | |
304 | // ownership semantics, so doing so would create wasteful allocations. | |
923072b8 | 305 | #[derive(Serialize)] |
cdc7bbd5 XL |
306 | struct UnusedExterns<'a, 'b, 'c> { |
307 | /// The severity level of the unused dependencies lint | |
308 | lint_level: &'a str, | |
309 | /// List of unused externs by their names. | |
310 | unused_extern_names: &'b [&'c str], | |
311 | } | |
312 | ||
32a655c1 | 313 | impl Diagnostic { |
dfeec247 | 314 | fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic { |
2b03887a | 315 | let args = to_fluent_args(diag.args()); |
04454e1e FG |
316 | let sugg = diag.suggestions.iter().flatten().map(|sugg| { |
317 | let translated_message = je.translate_message(&sugg.msg, &args); | |
318 | Diagnostic { | |
319 | message: translated_message.to_string(), | |
320 | code: None, | |
321 | level: "help", | |
322 | spans: DiagnosticSpan::from_suggestion(sugg, &args, je), | |
323 | children: vec![], | |
324 | rendered: None, | |
325 | } | |
7cac9316 | 326 | }); |
ff7c6d11 XL |
327 | |
328 | // generate regular command line output and store it in the json | |
329 | ||
330 | // A threadsafe buffer for writing. | |
331 | #[derive(Default, Clone)] | |
332 | struct BufWriter(Arc<Mutex<Vec<u8>>>); | |
333 | ||
334 | impl Write for BufWriter { | |
335 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | |
336 | self.0.lock().unwrap().write(buf) | |
337 | } | |
338 | fn flush(&mut self) -> io::Result<()> { | |
339 | self.0.lock().unwrap().flush() | |
340 | } | |
341 | } | |
342 | let buf = BufWriter::default(); | |
343 | let output = buf.clone(); | |
dfeec247 | 344 | je.json_rendered |
f035d41b XL |
345 | .new_emitter( |
346 | Box::new(buf), | |
347 | Some(je.sm.clone()), | |
04454e1e FG |
348 | je.fluent_bundle.clone(), |
349 | je.fallback_bundle.clone(), | |
f035d41b | 350 | false, |
064997fb | 351 | je.diagnostic_width, |
f035d41b XL |
352 | je.macro_backtrace, |
353 | ) | |
dfeec247 XL |
354 | .ui_testing(je.ui_testing) |
355 | .emit_diagnostic(diag); | |
ff7c6d11 XL |
356 | let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap(); |
357 | let output = String::from_utf8(output).unwrap(); | |
358 | ||
04454e1e | 359 | let translated_message = je.translate_messages(&diag.message, &args); |
9cc50fc6 | 360 | Diagnostic { |
04454e1e | 361 | message: translated_message.to_string(), |
e74abb32 XL |
362 | code: DiagnosticCode::map_opt_string(diag.code.clone(), je), |
363 | level: diag.level.to_str(), | |
04454e1e | 364 | spans: DiagnosticSpan::from_multispan(&diag.span, &args, je), |
dfeec247 XL |
365 | children: diag |
366 | .children | |
367 | .iter() | |
04454e1e | 368 | .map(|c| Diagnostic::from_sub_diagnostic(c, &args, je)) |
dfeec247 XL |
369 | .chain(sugg) |
370 | .collect(), | |
ff7c6d11 | 371 | rendered: Some(output), |
9cc50fc6 SL |
372 | } |
373 | } | |
374 | ||
04454e1e FG |
375 | fn from_sub_diagnostic( |
376 | diag: &SubDiagnostic, | |
377 | args: &FluentArgs<'_>, | |
378 | je: &JsonEmitter, | |
379 | ) -> Diagnostic { | |
380 | let translated_message = je.translate_messages(&diag.message, args); | |
9cc50fc6 | 381 | Diagnostic { |
04454e1e | 382 | message: translated_message.to_string(), |
9cc50fc6 | 383 | code: None, |
e74abb32 | 384 | level: diag.level.to_str(), |
dfeec247 XL |
385 | spans: diag |
386 | .render_span | |
387 | .as_ref() | |
04454e1e FG |
388 | .map(|sp| DiagnosticSpan::from_multispan(sp, args, je)) |
389 | .unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, args, je)), | |
9cc50fc6 | 390 | children: vec![], |
7cac9316 | 391 | rendered: None, |
9cc50fc6 SL |
392 | } |
393 | } | |
394 | } | |
395 | ||
396 | impl DiagnosticSpan { | |
dfeec247 XL |
397 | fn from_span_label( |
398 | span: SpanLabel, | |
399 | suggestion: Option<(&String, Applicability)>, | |
04454e1e | 400 | args: &FluentArgs<'_>, |
dfeec247 XL |
401 | je: &JsonEmitter, |
402 | ) -> DiagnosticSpan { | |
04454e1e FG |
403 | Self::from_span_etc( |
404 | span.span, | |
405 | span.is_primary, | |
406 | span.label.as_ref().map(|m| je.translate_message(m, args)).map(|m| m.to_string()), | |
407 | suggestion, | |
408 | je, | |
409 | ) | |
9cc50fc6 SL |
410 | } |
411 | ||
dfeec247 XL |
412 | fn from_span_etc( |
413 | span: Span, | |
414 | is_primary: bool, | |
415 | label: Option<String>, | |
416 | suggestion: Option<(&String, Applicability)>, | |
417 | je: &JsonEmitter, | |
418 | ) -> DiagnosticSpan { | |
a7813a04 XL |
419 | // obtain the full backtrace from the `macro_backtrace` |
420 | // helper; in some ways, it'd be better to expand the | |
421 | // backtrace ourselves, but the `macro_backtrace` helper makes | |
422 | // some decision, such as dropping some frames, and I don't | |
423 | // want to duplicate that logic here. | |
dfeec247 XL |
424 | let backtrace = span.macro_backtrace(); |
425 | DiagnosticSpan::from_span_full(span, is_primary, label, suggestion, backtrace, je) | |
a7813a04 XL |
426 | } |
427 | ||
dfeec247 XL |
428 | fn from_span_full( |
429 | span: Span, | |
430 | is_primary: bool, | |
431 | label: Option<String>, | |
432 | suggestion: Option<(&String, Applicability)>, | |
433 | mut backtrace: impl Iterator<Item = ExpnData>, | |
434 | je: &JsonEmitter, | |
435 | ) -> DiagnosticSpan { | |
a1dfa0c6 XL |
436 | let start = je.sm.lookup_char_pos(span.lo()); |
437 | let end = je.sm.lookup_char_pos(span.hi()); | |
a7813a04 | 438 | let backtrace_step = backtrace.next().map(|bt| { |
dfeec247 | 439 | let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je); |
5e7ed085 FG |
440 | let def_site_span = Self::from_span_full( |
441 | je.sm.guess_head_span(bt.def_site), | |
442 | false, | |
443 | None, | |
444 | None, | |
445 | [].into_iter(), | |
446 | je, | |
447 | ); | |
a7813a04 XL |
448 | Box::new(DiagnosticSpanMacroExpansion { |
449 | span: call_site, | |
dfeec247 | 450 | macro_decl_name: bt.kind.descr(), |
3b2f2976 | 451 | def_site_span, |
a7813a04 XL |
452 | }) |
453 | }); | |
2c00a5a8 | 454 | |
a7813a04 | 455 | DiagnosticSpan { |
94222f64 | 456 | file_name: je.sm.filename_for_diagnostics(&start.file.name).to_string(), |
e74abb32 XL |
457 | byte_start: start.file.original_relative_byte_pos(span.lo()).0, |
458 | byte_end: start.file.original_relative_byte_pos(span.hi()).0, | |
a7813a04 XL |
459 | line_start: start.line, |
460 | line_end: end.line, | |
461 | column_start: start.col.0 + 1, | |
462 | column_end: end.col.0 + 1, | |
3b2f2976 | 463 | is_primary, |
a7813a04 | 464 | text: DiagnosticSpanLine::from_span(span, je), |
2c00a5a8 | 465 | suggested_replacement: suggestion.map(|x| x.0.clone()), |
94b46f34 | 466 | suggestion_applicability: suggestion.map(|x| x.1), |
a7813a04 | 467 | expansion: backtrace_step, |
3b2f2976 | 468 | label, |
9cc50fc6 SL |
469 | } |
470 | } | |
9cc50fc6 | 471 | |
04454e1e FG |
472 | fn from_multispan( |
473 | msp: &MultiSpan, | |
474 | args: &FluentArgs<'_>, | |
475 | je: &JsonEmitter, | |
476 | ) -> Vec<DiagnosticSpan> { | |
a7813a04 | 477 | msp.span_labels() |
dfeec247 | 478 | .into_iter() |
04454e1e | 479 | .map(|span_str| Self::from_span_label(span_str, None, args, je)) |
dfeec247 | 480 | .collect() |
a7813a04 XL |
481 | } |
482 | ||
04454e1e FG |
483 | fn from_suggestion( |
484 | suggestion: &CodeSuggestion, | |
485 | args: &FluentArgs<'_>, | |
486 | je: &JsonEmitter, | |
487 | ) -> Vec<DiagnosticSpan> { | |
dfeec247 XL |
488 | suggestion |
489 | .substitutions | |
490 | .iter() | |
491 | .flat_map(|substitution| { | |
492 | substitution.parts.iter().map(move |suggestion_inner| { | |
493 | let span_label = | |
494 | SpanLabel { span: suggestion_inner.span, is_primary: true, label: None }; | |
495 | DiagnosticSpan::from_span_label( | |
496 | span_label, | |
497 | Some((&suggestion_inner.snippet, suggestion.applicability)), | |
04454e1e | 498 | args, |
dfeec247 XL |
499 | je, |
500 | ) | |
501 | }) | |
502 | }) | |
503 | .collect() | |
a7813a04 | 504 | } |
54a0048b SL |
505 | } |
506 | ||
507 | impl DiagnosticSpanLine { | |
dfeec247 | 508 | fn line_from_source_file( |
74b04a01 | 509 | sf: &rustc_span::SourceFile, |
dfeec247 XL |
510 | index: usize, |
511 | h_start: usize, | |
512 | h_end: usize, | |
513 | ) -> DiagnosticSpanLine { | |
54a0048b | 514 | DiagnosticSpanLine { |
cdc7bbd5 | 515 | text: sf.get_line(index).map_or_else(String::new, |l| l.into_owned()), |
54a0048b SL |
516 | highlight_start: h_start, |
517 | highlight_end: h_end, | |
518 | } | |
519 | } | |
520 | ||
9fa01778 | 521 | /// Creates a list of DiagnosticSpanLines from span - each line with any part |
54a0048b SL |
522 | /// of `span` gets a DiagnosticSpanLine, with the highlight indicating the |
523 | /// `span` within the line. | |
a7813a04 | 524 | fn from_span(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> { |
dfeec247 XL |
525 | je.sm |
526 | .span_to_lines(span) | |
48663c56 | 527 | .map(|lines| { |
ba9703b0 XL |
528 | // We can't get any lines if the source is unavailable. |
529 | if !je.sm.ensure_source_file_source_present(lines.file.clone()) { | |
530 | return vec![]; | |
531 | } | |
532 | ||
74b04a01 | 533 | let sf = &*lines.file; |
dfeec247 XL |
534 | lines |
535 | .lines | |
48663c56 | 536 | .iter() |
dfeec247 XL |
537 | .map(|line| { |
538 | DiagnosticSpanLine::line_from_source_file( | |
74b04a01 | 539 | sf, |
dfeec247 XL |
540 | line.line_index, |
541 | line.start_col.0 + 1, | |
542 | line.end_col.0 + 1, | |
543 | ) | |
544 | }) | |
545 | .collect() | |
546 | }) | |
547 | .unwrap_or_else(|_| vec![]) | |
54a0048b SL |
548 | } |
549 | } | |
550 | ||
9cc50fc6 | 551 | impl DiagnosticCode { |
abe05a73 | 552 | fn map_opt_string(s: Option<DiagnosticId>, je: &JsonEmitter) -> Option<DiagnosticCode> { |
9cc50fc6 | 553 | s.map(|s| { |
abe05a73 XL |
554 | let s = match s { |
555 | DiagnosticId::Error(s) => s, | |
136023e0 | 556 | DiagnosticId::Lint { name, .. } => name, |
abe05a73 | 557 | }; |
74b04a01 XL |
558 | let je_result = |
559 | je.registry.as_ref().map(|registry| registry.try_find_description(&s)).unwrap(); | |
9cc50fc6 | 560 | |
74b04a01 | 561 | DiagnosticCode { code: s, explanation: je_result.unwrap_or(None) } |
9cc50fc6 SL |
562 | }) |
563 | } | |
564 | } |