1 | // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at |
3 | // http://rust-lang.org/COPYRIGHT. |
4 | // |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
8 | // option. This file may not be copied, modified, or distributed |
9 | // except according to those terms. |
10 | |
11 | use std::io::prelude::*; |
12 | use std::io; |
13 | use std::cmp::min; |
14 | use std::sync::Arc; |
15 | use std::collections::HashMap; |
16 | use termcolor::{StandardStream, ColorChoice, ColorSpec, BufferWriter}; |
17 | use termcolor::{WriteColor, Color, Buffer}; |
18 | use std::io::IsTerminal; |
19 | use { Level, Diagnostic, SpanLabel, SpanStyle }; |
20 | use codemap::{CodeMap, File}; |
21 | use snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, StyledString, Style}; |
22 | use styled_buffer::StyledBuffer; |
23 | |
24 | /// Settings for terminal styling. |
25 | #[derive (Clone, Copy, Debug, PartialEq, Eq)] |
26 | pub enum ColorConfig { |
27 | /// Use colored output if stdout is a terminal. |
28 | Auto, |
29 | |
30 | /// Always use colored output. |
31 | Always, |
32 | |
33 | /// Never use colored output. |
34 | Never, |
35 | } |
36 | |
37 | impl ColorConfig { |
38 | fn to_color_choice(&self) -> ColorChoice { |
39 | match *self { |
40 | ColorConfig::Always => ColorChoice::Always, |
41 | ColorConfig::Never => ColorChoice::Never, |
42 | ColorConfig::Auto if std::io::stderr().is_terminal() => { |
43 | ColorChoice::Auto |
44 | } |
45 | ColorConfig::Auto => ColorChoice::Never, |
46 | } |
47 | } |
48 | } |
49 | |
50 | /// Formats and prints diagnostic messages. |
51 | pub struct Emitter<'a> { |
52 | dst: Destination<'a>, |
53 | cm: Option<&'a CodeMap>, |
54 | } |
55 | |
56 | struct FileWithAnnotatedLines { |
57 | file: Arc<File>, |
58 | lines: Vec<Line>, |
59 | multiline_depth: usize, |
60 | } |
61 | |
62 | impl<'a> Emitter<'a> { |
63 | /// Creates an emitter wrapping stderr. |
64 | pub fn stderr(color_config: ColorConfig, code_map: Option<&'a CodeMap>) -> Emitter<'a> { |
65 | let dst = Destination::from_stderr(color_config); |
66 | Emitter { |
67 | dst: dst, |
68 | cm: code_map, |
69 | } |
70 | } |
71 | |
72 | /// Creates an emitter wrapping a vector. |
73 | pub fn vec(vec: &'a mut Vec<u8>, code_map: Option<&'a CodeMap>) -> Emitter<'a> { |
74 | Emitter { |
75 | dst: Raw(Box::new(vec)), |
76 | cm: code_map, |
77 | } |
78 | } |
79 | |
80 | /// Creates an emitter wrapping a boxed `Write` trait object. |
81 | pub fn new(dst: Box<Write + Send + 'a>, code_map: Option<&'a CodeMap>) -> Emitter<'a> { |
82 | Emitter { |
83 | dst: Raw(dst), |
84 | cm: code_map, |
85 | } |
86 | } |
87 | |
88 | fn preprocess_annotations(cm: Option<&'a CodeMap>, spans: &[SpanLabel]) -> Vec<FileWithAnnotatedLines> { |
89 | fn add_annotation_to_file<'a>(file_vec: &mut Vec<FileWithAnnotatedLines>, |
90 | file: Arc<File>, |
91 | line_index: usize, |
92 | ann: Annotation) { |
93 | |
94 | for slot in file_vec.iter_mut() { |
95 | // Look through each of our files for the one we're adding to |
96 | if slot.file.name() == file.name() { |
97 | // See if we already have a line for it |
98 | for line_slot in &mut slot.lines { |
99 | if line_slot.line_index == line_index { |
100 | line_slot.annotations.push(ann); |
101 | return; |
102 | } |
103 | } |
104 | // We don't have a line yet, create one |
105 | slot.lines.push(Line { |
106 | line_index: line_index, |
107 | annotations: vec![ann], |
108 | }); |
109 | slot.lines.sort(); |
110 | return; |
111 | } |
112 | } |
113 | // This is the first time we're seeing the file |
114 | file_vec.push(FileWithAnnotatedLines { |
115 | file: file, |
116 | lines: vec![Line { |
117 | line_index: line_index, |
118 | annotations: vec![ann], |
119 | }], |
120 | multiline_depth: 0, |
121 | }); |
122 | } |
123 | |
124 | let mut output = vec![]; |
125 | let mut multiline_annotations = vec![]; |
126 | |
127 | if let Some(ref cm) = cm { |
128 | for span_label in spans { |
129 | let mut loc = cm.look_up_span(span_label.span); |
130 | |
131 | // Watch out for "empty spans". If we get a span like 6..6, we |
132 | // want to just display a `^` at 6, so convert that to |
133 | // 6..7. This is degenerate input, but it's best to degrade |
134 | // gracefully -- and the parser likes to supply a span like |
135 | // that for EOF, in particular. |
136 | if loc.begin == loc.end { |
137 | loc.end.column = loc.begin.column + 1; |
138 | } |
139 | |
140 | let ann_type = if loc.begin.line != loc.end.line { |
141 | let ml = MultilineAnnotation { |
142 | depth: 1, |
143 | line_start: loc.begin.line, |
144 | line_end: loc.end.line, |
145 | start_col: loc.begin.column, |
146 | end_col: loc.end.column, |
147 | is_primary: span_label.style == SpanStyle::Primary, |
148 | label: span_label.label.clone(), |
149 | }; |
150 | multiline_annotations.push((loc.file.clone(), ml.clone())); |
151 | AnnotationType::Multiline(ml) |
152 | } else { |
153 | AnnotationType::Singleline |
154 | }; |
155 | let ann = Annotation { |
156 | start_col: loc.begin.column, |
157 | end_col: loc.end.column, |
158 | is_primary: span_label.style == SpanStyle::Primary, |
159 | label: span_label.label.clone(), |
160 | annotation_type: ann_type, |
161 | }; |
162 | |
163 | if !ann.is_multiline() { |
164 | add_annotation_to_file(&mut output, |
165 | loc.file, |
166 | loc.begin.line, |
167 | ann); |
168 | } |
169 | } |
170 | } |
171 | |
172 | // Find overlapping multiline annotations, put them at different depths |
173 | multiline_annotations.sort_by(|a, b| { |
174 | (a.1.line_start, a.1.line_end).cmp(&(b.1.line_start, b.1.line_end)) |
175 | }); |
176 | for item in multiline_annotations.clone() { |
177 | let ann = item.1; |
178 | for item in multiline_annotations.iter_mut() { |
179 | let ref mut a = item.1; |
180 | // Move all other multiline annotations overlapping with this one |
181 | // one level to the right. |
182 | if &ann != a && |
183 | num_overlap(ann.line_start, ann.line_end, a.line_start, a.line_end, true) |
184 | { |
185 | a.increase_depth(); |
186 | } else { |
187 | break; |
188 | } |
189 | } |
190 | } |
191 | |
192 | let mut max_depth = 0; // max overlapping multiline spans |
193 | for (file, ann) in multiline_annotations { |
194 | if ann.depth > max_depth { |
195 | max_depth = ann.depth; |
196 | } |
197 | add_annotation_to_file(&mut output, file.clone(), ann.line_start, ann.as_start()); |
198 | let middle = min(ann.line_start + 4, ann.line_end); |
199 | for line in ann.line_start + 1..middle { |
200 | add_annotation_to_file(&mut output, file.clone(), line, ann.as_line()); |
201 | } |
202 | if middle < ann.line_end - 1 { |
203 | for line in ann.line_end - 1..ann.line_end { |
204 | add_annotation_to_file(&mut output, file.clone(), line, ann.as_line()); |
205 | } |
206 | } |
207 | add_annotation_to_file(&mut output, file, ann.line_end, ann.as_end()); |
208 | } |
209 | for file_vec in output.iter_mut() { |
210 | file_vec.multiline_depth = max_depth; |
211 | } |
212 | output |
213 | } |
214 | |
215 | fn render_source_line(&self, |
216 | buffer: &mut StyledBuffer, |
217 | file: &File, |
218 | line: &Line, |
219 | width_offset: usize, |
220 | code_offset: usize) -> Vec<(usize, Style)> { |
221 | |
222 | let source_string = file.source_line(line.line_index); |
223 | |
224 | let line_offset = buffer.num_lines(); |
225 | |
226 | // First create the source line we will highlight. |
227 | buffer.puts(line_offset, code_offset, &source_string, Style::Quotation); |
228 | buffer.puts(line_offset, |
229 | 0, |
230 | &((line.line_index + 1).to_string()), |
231 | Style::LineNumber); |
232 | |
233 | draw_col_separator(buffer, line_offset, width_offset - 2); |
234 | |
235 | // Special case when there's only one annotation involved, it is the start of a multiline |
236 | // span and there's no text at the beginning of the code line. Instead of doing the whole |
237 | // graph: |
238 | // |
239 | // 2 | fn foo() { |
240 | // | _^ |
241 | // 3 | | |
242 | // 4 | | } |
243 | // | |_^ test |
244 | // |
245 | // we simplify the output to: |
246 | // |
247 | // 2 | / fn foo() { |
248 | // 3 | | |
249 | // 4 | | } |
250 | // | |_^ test |
251 | if line.annotations.len() == 1 { |
252 | if let Some(ref ann) = line.annotations.get(0) { |
253 | if let AnnotationType::MultilineStart(depth) = ann.annotation_type { |
254 | if source_string.chars() |
255 | .take(ann.start_col) |
256 | .all(|c| c.is_whitespace()) { |
257 | let style = if ann.is_primary { |
258 | Style::UnderlinePrimary |
259 | } else { |
260 | Style::UnderlineSecondary |
261 | }; |
262 | buffer.putc(line_offset, |
263 | width_offset + depth - 1, |
264 | '/' , |
265 | style); |
266 | return vec![(depth, style)]; |
267 | } |
268 | } |
269 | } |
270 | } |
271 | |
272 | // We want to display like this: |
273 | // |
274 | // vec.push(vec.pop().unwrap()); |
275 | // --- ^^^ - previous borrow ends here |
276 | // | | |
277 | // | error occurs here |
278 | // previous borrow of `vec` occurs here |
279 | // |
280 | // But there are some weird edge cases to be aware of: |
281 | // |
282 | // vec.push(vec.pop().unwrap()); |
283 | // -------- - previous borrow ends here |
284 | // || |
285 | // |this makes no sense |
286 | // previous borrow of `vec` occurs here |
287 | // |
288 | // For this reason, we group the lines into "highlight lines" |
289 | // and "annotations lines", where the highlight lines have the `^`. |
290 | |
291 | // Sort the annotations by (start, end col) |
292 | // The labels are reversed, sort and then reversed again. |
293 | // Consider a list of annotations (A1, A2, C1, C2, B1, B2) where |
294 | // the letter signifies the span. Here we are only sorting by the |
295 | // span and hence, the order of the elements with the same span will |
296 | // not change. On reversing the ordering (|a, b| but b.cmp(a)), you get |
297 | // (C1, C2, B1, B2, A1, A2). All the elements with the same span are |
298 | // still ordered first to last, but all the elements with different |
299 | // spans are ordered by their spans in last to first order. Last to |
300 | // first order is important, because the jiggly lines and | are on |
301 | // the left, so the rightmost span needs to be rendered first, |
302 | // otherwise the lines would end up needing to go over a message. |
303 | let mut annotations = line.annotations.clone(); |
304 | annotations.sort_by(|a,b| b.start_col.cmp(&a.start_col)); |
305 | |
306 | // First, figure out where each label will be positioned. |
307 | // |
308 | // In the case where you have the following annotations: |
309 | // |
310 | // vec.push(vec.pop().unwrap()); |
311 | // -------- - previous borrow ends here [C] |
312 | // || |
313 | // |this makes no sense [B] |
314 | // previous borrow of `vec` occurs here [A] |
315 | // |
316 | // `annotations_position` will hold [(2, A), (1, B), (0, C)]. |
317 | // |
318 | // We try, when possible, to stick the rightmost annotation at the end |
319 | // of the highlight line: |
320 | // |
321 | // vec.push(vec.pop().unwrap()); |
322 | // --- --- - previous borrow ends here |
323 | // |
324 | // But sometimes that's not possible because one of the other |
325 | // annotations overlaps it. For example, from the test |
326 | // `span_overlap_label`, we have the following annotations |
327 | // (written on distinct lines for clarity): |
328 | // |
329 | // fn foo(x: u32) { |
330 | // -------------- |
331 | // - |
332 | // |
333 | // In this case, we can't stick the rightmost-most label on |
334 | // the highlight line, or we would get: |
335 | // |
336 | // fn foo(x: u32) { |
337 | // -------- x_span |
338 | // | |
339 | // fn_span |
340 | // |
341 | // which is totally weird. Instead we want: |
342 | // |
343 | // fn foo(x: u32) { |
344 | // -------------- |
345 | // | | |
346 | // | x_span |
347 | // fn_span |
348 | // |
349 | // which is...less weird, at least. In fact, in general, if |
350 | // the rightmost span overlaps with any other span, we should |
351 | // use the "hang below" version, so we can at least make it |
352 | // clear where the span *starts*. There's an exception for this |
353 | // logic, when the labels do not have a message: |
354 | // |
355 | // fn foo(x: u32) { |
356 | // -------------- |
357 | // | |
358 | // x_span |
359 | // |
360 | // instead of: |
361 | // |
362 | // fn foo(x: u32) { |
363 | // -------------- |
364 | // | | |
365 | // | x_span |
366 | // <EMPTY LINE> |
367 | // |
368 | let mut annotations_position = vec![]; |
369 | let mut line_len = 0; |
370 | let mut p = 0; |
371 | for (i, annotation) in annotations.iter().enumerate() { |
372 | for (j, next) in annotations.iter().enumerate() { |
373 | if overlaps(next, annotation, 0) // This label overlaps with another one and both |
374 | && annotation.has_label() // take space (they have text and are not |
375 | && j > i // multiline lines). |
376 | && p == 0 // We're currently on the first line, move the label one line down |
377 | { |
378 | // This annotation needs a new line in the output. |
379 | p += 1; |
380 | break; |
381 | } |
382 | } |
383 | annotations_position.push((p, annotation)); |
384 | for (j, next) in annotations.iter().enumerate() { |
385 | if j > i { |
386 | let l = if let Some(ref label) = next.label { |
387 | label.len() + 2 |
388 | } else { |
389 | 0 |
390 | }; |
391 | if (overlaps(next, annotation, l) // Do not allow two labels to be in the same |
392 | // line if they overlap including padding, to |
393 | // avoid situations like: |
394 | // |
395 | // fn foo(x: u32) { |
396 | // -------^------ |
397 | // | | |
398 | // fn_spanx_span |
399 | // |
400 | && annotation.has_label() // Both labels must have some text, otherwise |
401 | && next.has_label()) // they are not overlapping. |
402 | // Do not add a new line if this annotation |
403 | // or the next are vertical line placeholders. |
404 | || (annotation.takes_space() // If either this or the next annotation is |
405 | && next.has_label()) // multiline start/end, move it to a new line |
406 | || (annotation.has_label() // so as not to overlap the orizontal lines. |
407 | && next.takes_space()) |
408 | || (annotation.takes_space() && next.takes_space()) |
409 | || (overlaps(next, annotation, l) |
410 | && next.end_col <= annotation.end_col |
411 | && next.has_label() |
412 | && p == 0) // Avoid #42595. |
413 | { |
414 | // This annotation needs a new line in the output. |
415 | p += 1; |
416 | break; |
417 | } |
418 | } |
419 | } |
420 | if line_len < p { |
421 | line_len = p; |
422 | } |
423 | } |
424 | |
425 | if line_len != 0 { |
426 | line_len += 1; |
427 | } |
428 | |
429 | // If there are no annotations or the only annotations on this line are |
430 | // MultilineLine, then there's only code being shown, stop processing. |
431 | if line.annotations.is_empty() || line.annotations.iter() |
432 | .filter(|a| !a.is_line()).collect::<Vec<_>>().len() == 0 |
433 | { |
434 | return vec![]; |
435 | } |
436 | |
437 | // Write the colunmn separator. |
438 | // |
439 | // After this we will have: |
440 | // |
441 | // 2 | fn foo() { |
442 | // | |
443 | // | |
444 | // | |
445 | // 3 | |
446 | // 4 | } |
447 | // | |
448 | for pos in 0..line_len + 1 { |
449 | draw_col_separator(buffer, line_offset + pos + 1, width_offset - 2); |
450 | buffer.putc(line_offset + pos + 1, |
451 | width_offset - 2, |
452 | '|' , |
453 | Style::LineNumber); |
454 | } |
455 | |
456 | // Write the horizontal lines for multiline annotations |
457 | // (only the first and last lines need this). |
458 | // |
459 | // After this we will have: |
460 | // |
461 | // 2 | fn foo() { |
462 | // | __________ |
463 | // | |
464 | // | |
465 | // 3 | |
466 | // 4 | } |
467 | // | _ |
468 | for &(pos, annotation) in &annotations_position { |
469 | let style = if annotation.is_primary { |
470 | Style::UnderlinePrimary |
471 | } else { |
472 | Style::UnderlineSecondary |
473 | }; |
474 | let pos = pos + 1; |
475 | match annotation.annotation_type { |
476 | AnnotationType::MultilineStart(depth) | |
477 | AnnotationType::MultilineEnd(depth) => { |
478 | draw_range(buffer, |
479 | '_' , |
480 | line_offset + pos, |
481 | width_offset + depth, |
482 | code_offset + annotation.start_col, |
483 | style); |
484 | } |
485 | _ => (), |
486 | } |
487 | } |
488 | |
489 | // Write the vertical lines for labels that are on a different line as the underline. |
490 | // |
491 | // After this we will have: |
492 | // |
493 | // 2 | fn foo() { |
494 | // | __________ |
495 | // | | | |
496 | // | | |
497 | // 3 | |
498 | // 4 | | } |
499 | // | |_ |
500 | for &(pos, annotation) in &annotations_position { |
501 | let style = if annotation.is_primary { |
502 | Style::UnderlinePrimary |
503 | } else { |
504 | Style::UnderlineSecondary |
505 | }; |
506 | let pos = pos + 1; |
507 | |
508 | if pos > 1 && (annotation.has_label() || annotation.takes_space()) { |
509 | for p in line_offset + 1..line_offset + pos + 1 { |
510 | buffer.putc(p, |
511 | code_offset + annotation.start_col, |
512 | '|' , |
513 | style); |
514 | } |
515 | } |
516 | match annotation.annotation_type { |
517 | AnnotationType::MultilineStart(depth) => { |
518 | for p in line_offset + pos + 1..line_offset + line_len + 2 { |
519 | buffer.putc(p, |
520 | width_offset + depth - 1, |
521 | '|' , |
522 | style); |
523 | } |
524 | } |
525 | AnnotationType::MultilineEnd(depth) => { |
526 | for p in line_offset..line_offset + pos + 1 { |
527 | buffer.putc(p, |
528 | width_offset + depth - 1, |
529 | '|' , |
530 | style); |
531 | } |
532 | } |
533 | _ => (), |
534 | } |
535 | } |
536 | |
537 | // Write the labels on the annotations that actually have a label. |
538 | // |
539 | // After this we will have: |
540 | // |
541 | // 2 | fn foo() { |
542 | // | __________ |
543 | // | | |
544 | // | something about `foo` |
545 | // 3 | |
546 | // 4 | } |
547 | // | _ test |
548 | for &(pos, annotation) in &annotations_position { |
549 | let style = if annotation.is_primary { |
550 | Style::LabelPrimary |
551 | } else { |
552 | Style::LabelSecondary |
553 | }; |
554 | let (pos, col) = if pos == 0 { |
555 | (pos + 1, annotation.end_col + 1) |
556 | } else { |
557 | (pos + 2, annotation.start_col) |
558 | }; |
559 | if let Some(ref label) = annotation.label { |
560 | buffer.puts(line_offset + pos, |
561 | code_offset + col, |
562 | &label, |
563 | style); |
564 | } |
565 | } |
566 | |
567 | // Sort from biggest span to smallest span so that smaller spans are |
568 | // represented in the output: |
569 | // |
570 | // x | fn foo() |
571 | // | ^^^---^^ |
572 | // | | | |
573 | // | | something about `foo` |
574 | // | something about `fn foo()` |
575 | annotations_position.sort_by(|a, b| { |
576 | // Decreasing order |
577 | a.1.len().cmp(&b.1.len()).reverse() |
578 | }); |
579 | |
580 | // Write the underlines. |
581 | // |
582 | // After this we will have: |
583 | // |
584 | // 2 | fn foo() { |
585 | // | ____-_____^ |
586 | // | | |
587 | // | something about `foo` |
588 | // 3 | |
589 | // 4 | } |
590 | // | _^ test |
591 | for &(_, annotation) in &annotations_position { |
592 | let (underline, style) = if annotation.is_primary { |
593 | ('^' , Style::UnderlinePrimary) |
594 | } else { |
595 | ('-' , Style::UnderlineSecondary) |
596 | }; |
597 | for p in annotation.start_col..annotation.end_col { |
598 | buffer.putc(line_offset + 1, |
599 | code_offset + p, |
600 | underline, |
601 | style); |
602 | } |
603 | } |
604 | annotations_position.iter().filter_map(|&(_, annotation)| { |
605 | match annotation.annotation_type { |
606 | AnnotationType::MultilineStart(p) | AnnotationType::MultilineEnd(p) => { |
607 | let style = if annotation.is_primary { |
608 | Style::LabelPrimary |
609 | } else { |
610 | Style::LabelSecondary |
611 | }; |
612 | Some((p, style)) |
613 | }, |
614 | _ => None |
615 | } |
616 | |
617 | }).collect::<Vec<_>>() |
618 | } |
619 | |
620 | fn get_max_line_num(&mut self, diagnostics: &[Diagnostic]) -> usize { |
621 | if let Some(ref cm) = self.cm { |
622 | diagnostics.iter().map(|d| { |
623 | d.spans.iter().map(|span_label| { |
624 | cm.look_up_pos(span_label.span.high()).position.line |
625 | }).max().unwrap_or(0) |
626 | }).max().unwrap_or(0) |
627 | } else { 0 } |
628 | } |
629 | |
630 | /// Add a left margin to every line but the first, given a padding length and the label being |
631 | /// displayed, keeping the provided highlighting. |
632 | fn msg_to_buffer(&self, |
633 | buffer: &mut StyledBuffer, |
634 | msg: &Vec<(String, Style)>, |
635 | padding: usize, |
636 | label: &str, |
637 | override_style: Option<Style>) { |
638 | |
639 | // The extra 5 ` ` is padding that's always needed to align to the `note: `: |
640 | // |
641 | // error: message |
642 | // --> file.rs:13:20 |
643 | // | |
644 | // 13 | <CODE> |
645 | // | ^^^^ |
646 | // | |
647 | // = note: multiline |
648 | // message |
649 | // ++^^^----xx |
650 | // | | | | |
651 | // | | | magic `2` |
652 | // | | length of label |
653 | // | magic `3` |
654 | // `max_line_num_len` |
655 | let padding = (0..padding + label.len() + 5) |
656 | .map(|_| " " ) |
657 | .collect::<String>(); |
658 | |
659 | /// Return wether `style`, or the override if present and the style is `NoStyle`. |
660 | fn style_or_override(style: Style, override_style: Option<Style>) -> Style { |
661 | if let Some(o) = override_style { |
662 | if style == Style::NoStyle { |
663 | return o; |
664 | } |
665 | } |
666 | style |
667 | } |
668 | |
669 | let mut line_number = 0; |
670 | |
671 | // Provided the following diagnostic message: |
672 | // |
673 | // let msg = vec![ |
674 | // (" |
675 | // ("highlighted multiline\nstring to\nsee how it ", Style::NoStyle), |
676 | // ("looks", Style::Highlight), |
677 | // ("with\nvery ", Style::NoStyle), |
678 | // ("weird", Style::Highlight), |
679 | // (" formats\n", Style::NoStyle), |
680 | // ("see?", Style::Highlight), |
681 | // ]; |
682 | // |
683 | // the expected output on a note is (* surround the highlighted text) |
684 | // |
685 | // = note: highlighted multiline |
686 | // string to |
687 | // see how it *looks* with |
688 | // very *weird* formats |
689 | // see? |
690 | for &(ref text, ref style) in msg.iter() { |
691 | let lines = text.split(' \n' ).collect::<Vec<_>>(); |
692 | if lines.len() > 1 { |
693 | for (i, line) in lines.iter().enumerate() { |
694 | if i != 0 { |
695 | line_number += 1; |
696 | buffer.append(line_number, &padding, Style::NoStyle); |
697 | } |
698 | buffer.append(line_number, line, style_or_override(*style, override_style)); |
699 | } |
700 | } else { |
701 | buffer.append(line_number, text, style_or_override(*style, override_style)); |
702 | } |
703 | } |
704 | } |
705 | |
706 | fn emit_message_default(&mut self, |
707 | spans: &[SpanLabel], |
708 | msg: &Vec<(String, Style)>, |
709 | code: &Option<String>, |
710 | level: &Level, |
711 | max_line_num_len: usize, |
712 | is_secondary: bool) |
713 | -> io::Result<()> { |
714 | let mut buffer = StyledBuffer::new(); |
715 | |
716 | if is_secondary && spans.len() == 0 { |
717 | // This is a secondary message with no span info |
718 | for _ in 0..max_line_num_len { |
719 | buffer.prepend(0, " " , Style::NoStyle); |
720 | } |
721 | draw_note_separator(&mut buffer, 0, max_line_num_len + 1); |
722 | buffer.append(0, &level.to_string(), Style::HeaderMsg); |
723 | buffer.append(0, ": " , Style::NoStyle); |
724 | self.msg_to_buffer(&mut buffer, msg, max_line_num_len, "note" , None); |
725 | } else { |
726 | buffer.append(0, &level.to_string(), Style::Level(level.clone())); |
727 | if let Some(code) = code.as_ref() { |
728 | buffer.append(0, "[" , Style::Level(level.clone())); |
729 | buffer.append(0, &code, Style::Level(level.clone())); |
730 | buffer.append(0, "]" , Style::Level(level.clone())); |
731 | } |
732 | buffer.append(0, ": " , Style::HeaderMsg); |
733 | for &(ref text, _) in msg.iter() { |
734 | buffer.append(0, text, Style::HeaderMsg); |
735 | } |
736 | } |
737 | |
738 | // Preprocess all the annotations so that they are grouped by file and by line number |
739 | // This helps us quickly iterate over the whole message (including secondary file spans) |
740 | let mut annotated_files = Emitter::preprocess_annotations(self.cm, spans); |
741 | |
742 | // Make sure our primary file comes first |
743 | let primary_lo = if let (Some(ref cm), Some(ref primary_span)) = |
744 | (self.cm.as_ref(), spans.iter().find(|x| x.style == SpanStyle::Primary)) { |
745 | cm.look_up_pos(primary_span.span.low()) |
746 | } else { |
747 | // If we don't have span information, emit and exit |
748 | emit_to_destination(&buffer.render(), level, &mut self.dst)?; |
749 | return Ok(()); |
750 | }; |
751 | if let Ok(pos) = |
752 | annotated_files.binary_search_by(|x| x.file.name().cmp(&primary_lo.file.name())) { |
753 | annotated_files.swap(0, pos); |
754 | } |
755 | |
756 | // Print out the annotate source lines that correspond with the error |
757 | for annotated_file in annotated_files { |
758 | // print out the span location and spacer before we print the annotated source |
759 | // to do this, we need to know if this span will be primary |
760 | let is_primary = primary_lo.file.name() == annotated_file.file.name(); |
761 | if is_primary { |
762 | // remember where we are in the output buffer for easy reference |
763 | let buffer_msg_line_offset = buffer.num_lines(); |
764 | |
765 | buffer.prepend(buffer_msg_line_offset, "--> " , Style::LineNumber); |
766 | let loc = primary_lo.clone(); |
767 | buffer.append(buffer_msg_line_offset, |
768 | &format!(" {}: {}: {}" , loc.file.name(), loc.position.line + 1, loc.position.column + 1), |
769 | Style::LineAndColumn); |
770 | for _ in 0..max_line_num_len { |
771 | buffer.prepend(buffer_msg_line_offset, " " , Style::NoStyle); |
772 | } |
773 | } else { |
774 | // remember where we are in the output buffer for easy reference |
775 | let buffer_msg_line_offset = buffer.num_lines(); |
776 | |
777 | // Add spacing line |
778 | draw_col_separator(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1); |
779 | |
780 | // Then, the secondary file indicator |
781 | buffer.prepend(buffer_msg_line_offset + 1, "::: " , Style::LineNumber); |
782 | buffer.append(buffer_msg_line_offset + 1, |
783 | annotated_file.file.name(), |
784 | Style::LineAndColumn); |
785 | for _ in 0..max_line_num_len { |
786 | buffer.prepend(buffer_msg_line_offset + 1, " " , Style::NoStyle); |
787 | } |
788 | } |
789 | |
790 | // Put in the spacer between the location and annotated source |
791 | let buffer_msg_line_offset = buffer.num_lines(); |
792 | draw_col_separator_no_space(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1); |
793 | |
794 | // Contains the vertical lines' positions for active multiline annotations |
795 | let mut multilines = HashMap::new(); |
796 | |
797 | // Next, output the annotate source for this file |
798 | for line_idx in 0..annotated_file.lines.len() { |
799 | let previous_buffer_line = buffer.num_lines(); |
800 | |
801 | let width_offset = 3 + max_line_num_len; |
802 | let code_offset = if annotated_file.multiline_depth == 0 { |
803 | width_offset |
804 | } else { |
805 | width_offset + annotated_file.multiline_depth + 1 |
806 | }; |
807 | |
808 | let depths = self.render_source_line(&mut buffer, |
809 | &annotated_file.file, |
810 | &annotated_file.lines[line_idx], |
811 | width_offset, |
812 | code_offset); |
813 | |
814 | let mut to_add = HashMap::new(); |
815 | |
816 | for (depth, style) in depths { |
817 | if multilines.get(&depth).is_some() { |
818 | multilines.remove(&depth); |
819 | } else { |
820 | to_add.insert(depth, style); |
821 | } |
822 | } |
823 | |
824 | // Set the multiline annotation vertical lines to the left of |
825 | // the code in this line. |
826 | for (depth, style) in &multilines { |
827 | for line in previous_buffer_line..buffer.num_lines() { |
828 | draw_multiline_line(&mut buffer, |
829 | line, |
830 | width_offset, |
831 | *depth, |
832 | *style); |
833 | } |
834 | } |
835 | // check to see if we need to print out or elide lines that come between |
836 | // this annotated line and the next one. |
837 | if line_idx < (annotated_file.lines.len() - 1) { |
838 | let line_idx_delta = annotated_file.lines[line_idx + 1].line_index - |
839 | annotated_file.lines[line_idx].line_index; |
840 | if line_idx_delta > 2 { |
841 | let last_buffer_line_num = buffer.num_lines(); |
842 | buffer.puts(last_buffer_line_num, 0, "..." , Style::LineNumber); |
843 | |
844 | // Set the multiline annotation vertical lines on `...` bridging line. |
845 | for (depth, style) in &multilines { |
846 | draw_multiline_line(&mut buffer, |
847 | last_buffer_line_num, |
848 | width_offset, |
849 | *depth, |
850 | *style); |
851 | } |
852 | } else if line_idx_delta == 2 { |
853 | let unannotated_line = annotated_file.file |
854 | .source_line(annotated_file.lines[line_idx].line_index); |
855 | |
856 | let last_buffer_line_num = buffer.num_lines(); |
857 | |
858 | buffer.puts(last_buffer_line_num, |
859 | 0, |
860 | &(annotated_file.lines[line_idx + 1].line_index - 1) |
861 | .to_string(), |
862 | Style::LineNumber); |
863 | draw_col_separator(&mut buffer, last_buffer_line_num, 1 + max_line_num_len); |
864 | buffer.puts(last_buffer_line_num, |
865 | code_offset, |
866 | &unannotated_line, |
867 | Style::Quotation); |
868 | |
869 | for (depth, style) in &multilines { |
870 | draw_multiline_line(&mut buffer, |
871 | last_buffer_line_num, |
872 | width_offset, |
873 | *depth, |
874 | *style); |
875 | } |
876 | } |
877 | } |
878 | |
879 | multilines.extend(&to_add); |
880 | } |
881 | } |
882 | |
883 | // final step: take our styled buffer, render it, then output it |
884 | emit_to_destination(&buffer.render(), level, &mut self.dst)?; |
885 | |
886 | Ok(()) |
887 | } |
888 | |
889 | /// Print a group of diagnostic messages. |
890 | /// |
891 | /// The messages within a group are printed atomically without spacing between them, and share |
892 | /// consistent formatting elements, such as aligned line number width. |
893 | pub fn emit(&mut self, msgs: &[Diagnostic]) { |
894 | let max_line_num = self.get_max_line_num(msgs) + 1; |
895 | let max_line_num_len = max_line_num.to_string().len(); |
896 | |
897 | for msg in msgs { |
898 | match self.emit_message_default(&msg.spans[..], &vec![(msg.message.clone(), Style::NoStyle)], &msg.code, &msg.level, max_line_num_len, false) { |
899 | Ok(()) => (), |
900 | Err(e) => panic!("failed to emit error: {}" , e) |
901 | } |
902 | } |
903 | |
904 | let mut dst = self.dst.writable(); |
905 | match write!(dst, " \n" ) { |
906 | Err(e) => panic!("failed to emit error: {}" , e), |
907 | _ => { |
908 | match dst.flush() { |
909 | Err(e) => panic!("failed to emit error: {}" , e), |
910 | _ => (), |
911 | } |
912 | } |
913 | } |
914 | } |
915 | } |
916 | |
917 | |
918 | fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { |
919 | buffer.puts(line, col, string:"| " , Style::LineNumber); |
920 | } |
921 | |
922 | fn draw_col_separator_no_space(buffer: &mut StyledBuffer, line: usize, col: usize) { |
923 | draw_col_separator_no_space_with_style(buffer, line, col, Style::LineNumber); |
924 | } |
925 | |
926 | fn draw_col_separator_no_space_with_style(buffer: &mut StyledBuffer, |
927 | line: usize, |
928 | col: usize, |
929 | style: Style) { |
930 | buffer.putc(line, col, chr:'|' , style); |
931 | } |
932 | |
933 | fn draw_range(buffer: &mut StyledBuffer, symbol: char, line: usize, |
934 | col_from: usize, col_to: usize, style: Style) { |
935 | for col: usize in col_from..col_to { |
936 | buffer.putc(line, col, chr:symbol, style); |
937 | } |
938 | } |
939 | |
940 | fn draw_note_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { |
941 | buffer.puts(line, col, string:"= " , Style::LineNumber); |
942 | } |
943 | |
944 | fn draw_multiline_line(buffer: &mut StyledBuffer, |
945 | line: usize, |
946 | offset: usize, |
947 | depth: usize, |
948 | style: Style) |
949 | { |
950 | buffer.putc(line, col:offset + depth - 1, chr:'|' , style); |
951 | } |
952 | |
953 | fn num_overlap(a_start: usize, a_end: usize, b_start: usize, b_end:usize, inclusive: bool) -> bool { |
954 | let extra: usize = if inclusive { |
955 | 1 |
956 | } else { |
957 | 0 |
958 | }; |
959 | |
960 | (a_start >= b_start && a_start < b_end + extra) |
961 | || (b_start >= a_start && b_start < a_end + extra) |
962 | } |
963 | fn overlaps(a1: &Annotation, a2: &Annotation, padding: usize) -> bool { |
964 | num_overlap(a_start:a1.start_col, a_end:a1.end_col + padding, b_start:a2.start_col, b_end:a2.end_col, inclusive:false) |
965 | } |
966 | |
967 | fn emit_to_destination(rendered_buffer: &Vec<Vec<StyledString>>, |
968 | lvl: &Level, |
969 | dst: &mut Destination) |
970 | -> io::Result<()> { |
971 | use lock; |
972 | |
973 | let mut dst = dst.writable(); |
974 | |
975 | // In order to prevent error message interleaving, where multiple error lines get intermixed |
976 | // when multiple compiler processes error simultaneously, we emit errors with additional |
977 | // steps. |
978 | // |
979 | // On Unix systems, we write into a buffered terminal rather than directly to a terminal. When |
980 | // the .flush() is called we take the buffer created from the buffered writes and write it at |
981 | // one shot. Because the Unix systems use ANSI for the colors, which is a text-based styling |
982 | // scheme, this buffered approach works and maintains the styling. |
983 | // |
984 | // On Windows, styling happens through calls to a terminal API. This prevents us from using the |
985 | // same buffering approach. Instead, we use a global Windows mutex, which we acquire long |
986 | // enough to output the full error message, then we release. |
987 | let _buffer_lock = lock::acquire_global_lock("rustc_errors" ); |
988 | for line in rendered_buffer { |
989 | for part in line { |
990 | dst.apply_style(lvl.clone(), part.style)?; |
991 | write!(dst, " {}" , part.text)?; |
992 | dst.reset()?; |
993 | } |
994 | write!(dst, " \n" )?; |
995 | } |
996 | dst.flush()?; |
997 | Ok(()) |
998 | } |
999 | |
1000 | #[allow (dead_code)] |
1001 | enum Destination<'a> { |
1002 | Terminal(StandardStream), |
1003 | Buffered(BufferWriter), |
1004 | Raw(Box<Write + Send + 'a>), |
1005 | } |
1006 | |
1007 | use self::Destination::*; |
1008 | |
1009 | enum WritableDst<'a, 'b> { |
1010 | Terminal(&'b mut StandardStream), |
1011 | Buffered(&'b mut BufferWriter, Buffer), |
1012 | Raw(&'b mut Box<Write + Send + 'a>), |
1013 | } |
1014 | |
1015 | impl<'a> Destination<'a> { |
1016 | fn from_stderr(color: ColorConfig) -> Destination<'a> { |
1017 | let choice = color.to_color_choice(); |
1018 | // On Windows we'll be performing global synchronization on the entire |
1019 | // system for emitting rustc errors, so there's no need to buffer |
1020 | // anything. |
1021 | // |
1022 | // On non-Windows we rely on the atomicity of `write` to ensure errors |
1023 | // don't get all jumbled up. |
1024 | if cfg!(windows) { |
1025 | Destination::Terminal(StandardStream::stderr(choice)) |
1026 | } else { |
1027 | Destination::Buffered(BufferWriter::stderr(choice)) |
1028 | } |
1029 | } |
1030 | |
1031 | fn writable<'b>(&'b mut self) -> WritableDst<'a, 'b> { |
1032 | match *self { |
1033 | Destination::Terminal(ref mut t) => WritableDst::Terminal(t), |
1034 | Destination::Buffered(ref mut t) => { |
1035 | let buf = t.buffer(); |
1036 | WritableDst::Buffered(t, buf) |
1037 | } |
1038 | Destination::Raw(ref mut t) => WritableDst::Raw(t), |
1039 | } |
1040 | } |
1041 | } |
1042 | |
1043 | impl<'a, 'b> WritableDst<'a, 'b> { |
1044 | fn apply_style(&mut self, lvl: Level, style: Style) -> io::Result<()> { |
1045 | let mut spec = ColorSpec::new(); |
1046 | match style { |
1047 | Style::LineAndColumn => {} |
1048 | Style::LineNumber => { |
1049 | spec.set_bold(true); |
1050 | spec.set_intense(true); |
1051 | if cfg!(windows) { |
1052 | spec.set_fg(Some(Color::Cyan)); |
1053 | } else { |
1054 | spec.set_fg(Some(Color::Blue)); |
1055 | } |
1056 | } |
1057 | Style::Quotation => {} |
1058 | Style::HeaderMsg => { |
1059 | spec.set_bold(true); |
1060 | if cfg!(windows) { |
1061 | spec.set_intense(true) |
1062 | .set_fg(Some(Color::White)); |
1063 | } |
1064 | } |
1065 | Style::UnderlinePrimary | Style::LabelPrimary => { |
1066 | spec = lvl.color(); |
1067 | spec.set_bold(true); |
1068 | } |
1069 | Style::UnderlineSecondary | |
1070 | Style::LabelSecondary => { |
1071 | spec.set_bold(true) |
1072 | .set_intense(true); |
1073 | if cfg!(windows) { |
1074 | spec.set_fg(Some(Color::Cyan)); |
1075 | } else { |
1076 | spec.set_fg(Some(Color::Blue)); |
1077 | } |
1078 | } |
1079 | Style::NoStyle => {} |
1080 | Style::Level(lvl) => { |
1081 | spec = lvl.color(); |
1082 | spec.set_bold(true); |
1083 | } |
1084 | Style::Highlight => { |
1085 | spec.set_bold(true); |
1086 | } |
1087 | } |
1088 | self.set_color(&spec) |
1089 | } |
1090 | |
1091 | fn set_color(&mut self, color: &ColorSpec) -> io::Result<()> { |
1092 | match *self { |
1093 | WritableDst::Terminal(ref mut t) => t.set_color(color), |
1094 | WritableDst::Buffered(_, ref mut t) => t.set_color(color), |
1095 | WritableDst::Raw(_) => Ok(()) |
1096 | } |
1097 | } |
1098 | |
1099 | fn reset(&mut self) -> io::Result<()> { |
1100 | match *self { |
1101 | WritableDst::Terminal(ref mut t) => t.reset(), |
1102 | WritableDst::Buffered(_, ref mut t) => t.reset(), |
1103 | WritableDst::Raw(_) => Ok(()), |
1104 | } |
1105 | } |
1106 | } |
1107 | |
1108 | impl<'a, 'b> Write for WritableDst<'a, 'b> { |
1109 | fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { |
1110 | match *self { |
1111 | WritableDst::Terminal(ref mut t: &mut &mut StandardStream) => t.write(buf:bytes), |
1112 | WritableDst::Buffered(_, ref mut buf: &mut Buffer) => buf.write(buf:bytes), |
1113 | WritableDst::Raw(ref mut w: &mut &mut Box) => w.write(buf:bytes), |
1114 | } |
1115 | } |
1116 | |
1117 | fn flush(&mut self) -> io::Result<()> { |
1118 | match *self { |
1119 | WritableDst::Terminal(ref mut t: &mut &mut StandardStream) => t.flush(), |
1120 | WritableDst::Buffered(_, ref mut buf: &mut Buffer) => buf.flush(), |
1121 | WritableDst::Raw(ref mut w: &mut &mut Box) => w.flush(), |
1122 | } |
1123 | } |
1124 | } |
1125 | |
1126 | impl<'a, 'b> Drop for WritableDst<'a, 'b> { |
1127 | fn drop(&mut self) { |
1128 | match *self { |
1129 | WritableDst::Buffered(ref mut dst: &mut &mut BufferWriter, ref mut buf: &mut Buffer) => { |
1130 | drop(dst.print(buf)); |
1131 | } |
1132 | _ => {} |
1133 | } |
1134 | } |
1135 | } |
1136 | |