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