]> git.proxmox.com Git - rustc.git/blob - src/librustc_errors/emitter.rs
New upstream version 1.13.0+dfsg1
[rustc.git] / src / librustc_errors / emitter.rs
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 self::Destination::*;
12
13 use syntax_pos::{COMMAND_LINE_SP, DUMMY_SP, FileMap, Span, MultiSpan, CharPos};
14
15 use {Level, CodeSuggestion, DiagnosticBuilder, SubDiagnostic, CodeMapper};
16 use RenderSpan::*;
17 use snippet::{StyledString, Style, Annotation, Line};
18 use styled_buffer::StyledBuffer;
19
20 use std::io::prelude::*;
21 use std::io;
22 use std::rc::Rc;
23 use term;
24
25 /// Emitter trait for emitting errors.
26 pub trait Emitter {
27 /// Emit a structured diagnostic.
28 fn emit(&mut self, db: &DiagnosticBuilder);
29 }
30
31 impl Emitter for EmitterWriter {
32 fn emit(&mut self, db: &DiagnosticBuilder) {
33 let mut primary_span = db.span.clone();
34 let mut children = db.children.clone();
35 self.fix_multispans_in_std_macros(&mut primary_span, &mut children);
36 self.emit_messages_default(&db.level, &db.message, &db.code, &primary_span, &children);
37 }
38 }
39
40 /// maximum number of lines we will print for each error; arbitrary.
41 pub const MAX_HIGHLIGHT_LINES: usize = 6;
42
43 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
44 pub enum ColorConfig {
45 Auto,
46 Always,
47 Never,
48 }
49
50 impl ColorConfig {
51 fn use_color(&self) -> bool {
52 match *self {
53 ColorConfig::Always => true,
54 ColorConfig::Never => false,
55 ColorConfig::Auto => stderr_isatty(),
56 }
57 }
58 }
59
60 pub struct EmitterWriter {
61 dst: Destination,
62 cm: Option<Rc<CodeMapper>>,
63 }
64
65 struct FileWithAnnotatedLines {
66 file: Rc<FileMap>,
67 lines: Vec<Line>,
68 }
69
70
71 /// Do not use this for messages that end in `\n` – use `println_maybe_styled` instead. See
72 /// `EmitterWriter::print_maybe_styled` for details.
73 macro_rules! print_maybe_styled {
74 ($dst: expr, $style: expr, $($arg: tt)*) => {
75 $dst.print_maybe_styled(format_args!($($arg)*), $style, false)
76 }
77 }
78
79 macro_rules! println_maybe_styled {
80 ($dst: expr, $style: expr, $($arg: tt)*) => {
81 $dst.print_maybe_styled(format_args!($($arg)*), $style, true)
82 }
83 }
84
85 impl EmitterWriter {
86 pub fn stderr(color_config: ColorConfig,
87 code_map: Option<Rc<CodeMapper>>)
88 -> EmitterWriter {
89 if color_config.use_color() {
90 let dst = Destination::from_stderr();
91 EmitterWriter { dst: dst,
92 cm: code_map}
93 } else {
94 EmitterWriter { dst: Raw(Box::new(io::stderr())),
95 cm: code_map}
96 }
97 }
98
99 pub fn new(dst: Box<Write + Send>,
100 code_map: Option<Rc<CodeMapper>>)
101 -> EmitterWriter {
102 EmitterWriter { dst: Raw(dst),
103 cm: code_map}
104 }
105
106 fn preprocess_annotations(&self, msp: &MultiSpan) -> Vec<FileWithAnnotatedLines> {
107 fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>,
108 file: Rc<FileMap>,
109 line_index: usize,
110 ann: Annotation) {
111
112 for slot in file_vec.iter_mut() {
113 // Look through each of our files for the one we're adding to
114 if slot.file.name == file.name {
115 // See if we already have a line for it
116 for line_slot in &mut slot.lines {
117 if line_slot.line_index == line_index {
118 line_slot.annotations.push(ann);
119 return;
120 }
121 }
122 // We don't have a line yet, create one
123 slot.lines.push(Line {
124 line_index: line_index,
125 annotations: vec![ann],
126 });
127 slot.lines.sort();
128 return;
129 }
130 }
131 // This is the first time we're seeing the file
132 file_vec.push(FileWithAnnotatedLines {
133 file: file,
134 lines: vec![Line {
135 line_index: line_index,
136 annotations: vec![ann],
137 }],
138 });
139 }
140
141 let mut output = vec![];
142
143 if let Some(ref cm) = self.cm {
144 for span_label in msp.span_labels() {
145 if span_label.span == DUMMY_SP || span_label.span == COMMAND_LINE_SP {
146 continue;
147 }
148 let lo = cm.lookup_char_pos(span_label.span.lo);
149 let mut hi = cm.lookup_char_pos(span_label.span.hi);
150 let mut is_minimized = false;
151
152 // If the span is multi-line, simplify down to the span of one character
153 if lo.line != hi.line {
154 hi.line = lo.line;
155 hi.col = CharPos(lo.col.0 + 1);
156 is_minimized = true;
157 }
158
159 // Watch out for "empty spans". If we get a span like 6..6, we
160 // want to just display a `^` at 6, so convert that to
161 // 6..7. This is degenerate input, but it's best to degrade
162 // gracefully -- and the parser likes to supply a span like
163 // that for EOF, in particular.
164 if lo.col == hi.col {
165 hi.col = CharPos(lo.col.0 + 1);
166 }
167
168 add_annotation_to_file(&mut output,
169 lo.file,
170 lo.line,
171 Annotation {
172 start_col: lo.col.0,
173 end_col: hi.col.0,
174 is_primary: span_label.is_primary,
175 is_minimized: is_minimized,
176 label: span_label.label.clone(),
177 });
178 }
179 }
180 output
181 }
182
183 fn render_source_line(&self,
184 buffer: &mut StyledBuffer,
185 file: Rc<FileMap>,
186 line: &Line,
187 width_offset: usize) {
188 let source_string = file.get_line(line.line_index - 1)
189 .unwrap_or("");
190
191 let line_offset = buffer.num_lines();
192
193 // First create the source line we will highlight.
194 buffer.puts(line_offset, width_offset, &source_string, Style::Quotation);
195 buffer.puts(line_offset,
196 0,
197 &(line.line_index.to_string()),
198 Style::LineNumber);
199
200 draw_col_separator(buffer, line_offset, width_offset - 2);
201
202 if line.annotations.is_empty() {
203 return;
204 }
205
206 // We want to display like this:
207 //
208 // vec.push(vec.pop().unwrap());
209 // --- ^^^ _ previous borrow ends here
210 // | |
211 // | error occurs here
212 // previous borrow of `vec` occurs here
213 //
214 // But there are some weird edge cases to be aware of:
215 //
216 // vec.push(vec.pop().unwrap());
217 // -------- - previous borrow ends here
218 // ||
219 // |this makes no sense
220 // previous borrow of `vec` occurs here
221 //
222 // For this reason, we group the lines into "highlight lines"
223 // and "annotations lines", where the highlight lines have the `~`.
224
225 // Sort the annotations by (start, end col)
226 let mut annotations = line.annotations.clone();
227 annotations.sort();
228
229 // Next, create the highlight line.
230 for annotation in &annotations {
231 for p in annotation.start_col..annotation.end_col {
232 if annotation.is_primary {
233 buffer.putc(line_offset + 1,
234 width_offset + p,
235 '^',
236 Style::UnderlinePrimary);
237 if !annotation.is_minimized {
238 buffer.set_style(line_offset,
239 width_offset + p,
240 Style::UnderlinePrimary);
241 }
242 } else {
243 buffer.putc(line_offset + 1,
244 width_offset + p,
245 '-',
246 Style::UnderlineSecondary);
247 if !annotation.is_minimized {
248 buffer.set_style(line_offset,
249 width_offset + p,
250 Style::UnderlineSecondary);
251 }
252 }
253 }
254 }
255 draw_col_separator(buffer, line_offset + 1, width_offset - 2);
256
257 // Now we are going to write labels in. To start, we'll exclude
258 // the annotations with no labels.
259 let (labeled_annotations, unlabeled_annotations): (Vec<_>, _) = annotations.into_iter()
260 .partition(|a| a.label.is_some());
261
262 // If there are no annotations that need text, we're done.
263 if labeled_annotations.is_empty() {
264 return;
265 }
266 // Now add the text labels. We try, when possible, to stick the rightmost
267 // annotation at the end of the highlight line:
268 //
269 // vec.push(vec.pop().unwrap());
270 // --- --- - previous borrow ends here
271 //
272 // But sometimes that's not possible because one of the other
273 // annotations overlaps it. For example, from the test
274 // `span_overlap_label`, we have the following annotations
275 // (written on distinct lines for clarity):
276 //
277 // fn foo(x: u32) {
278 // --------------
279 // -
280 //
281 // In this case, we can't stick the rightmost-most label on
282 // the highlight line, or we would get:
283 //
284 // fn foo(x: u32) {
285 // -------- x_span
286 // |
287 // fn_span
288 //
289 // which is totally weird. Instead we want:
290 //
291 // fn foo(x: u32) {
292 // --------------
293 // | |
294 // | x_span
295 // fn_span
296 //
297 // which is...less weird, at least. In fact, in general, if
298 // the rightmost span overlaps with any other span, we should
299 // use the "hang below" version, so we can at least make it
300 // clear where the span *starts*.
301 let mut labeled_annotations = &labeled_annotations[..];
302 match labeled_annotations.split_last().unwrap() {
303 (last, previous) => {
304 if previous.iter()
305 .chain(&unlabeled_annotations)
306 .all(|a| !overlaps(a, last)) {
307 // append the label afterwards; we keep it in a separate
308 // string
309 let highlight_label: String = format!(" {}", last.label.as_ref().unwrap());
310 if last.is_primary {
311 buffer.append(line_offset + 1, &highlight_label, Style::LabelPrimary);
312 } else {
313 buffer.append(line_offset + 1, &highlight_label, Style::LabelSecondary);
314 }
315 labeled_annotations = previous;
316 }
317 }
318 }
319
320 // If that's the last annotation, we're done
321 if labeled_annotations.is_empty() {
322 return;
323 }
324
325 for (index, annotation) in labeled_annotations.iter().enumerate() {
326 // Leave:
327 // - 1 extra line
328 // - One line for each thing that comes after
329 let comes_after = labeled_annotations.len() - index - 1;
330 let blank_lines = 3 + comes_after;
331
332 // For each blank line, draw a `|` at our column. The
333 // text ought to be long enough for this.
334 for index in 2..blank_lines {
335 if annotation.is_primary {
336 buffer.putc(line_offset + index,
337 width_offset + annotation.start_col,
338 '|',
339 Style::UnderlinePrimary);
340 } else {
341 buffer.putc(line_offset + index,
342 width_offset + annotation.start_col,
343 '|',
344 Style::UnderlineSecondary);
345 }
346 draw_col_separator(buffer, line_offset + index, width_offset - 2);
347 }
348
349 if annotation.is_primary {
350 buffer.puts(line_offset + blank_lines,
351 width_offset + annotation.start_col,
352 annotation.label.as_ref().unwrap(),
353 Style::LabelPrimary);
354 } else {
355 buffer.puts(line_offset + blank_lines,
356 width_offset + annotation.start_col,
357 annotation.label.as_ref().unwrap(),
358 Style::LabelSecondary);
359 }
360 draw_col_separator(buffer, line_offset + blank_lines, width_offset - 2);
361 }
362 }
363
364 fn get_multispan_max_line_num(&mut self, msp: &MultiSpan) -> usize {
365 let mut max = 0;
366 if let Some(ref cm) = self.cm {
367 for primary_span in msp.primary_spans() {
368 if primary_span != &DUMMY_SP && primary_span != &COMMAND_LINE_SP {
369 let hi = cm.lookup_char_pos(primary_span.hi);
370 if hi.line > max {
371 max = hi.line;
372 }
373 }
374 }
375 for span_label in msp.span_labels() {
376 if span_label.span != DUMMY_SP && span_label.span != COMMAND_LINE_SP {
377 let hi = cm.lookup_char_pos(span_label.span.hi);
378 if hi.line > max {
379 max = hi.line;
380 }
381 }
382 }
383 }
384 max
385 }
386
387 fn get_max_line_num(&mut self, span: &MultiSpan, children: &Vec<SubDiagnostic>) -> usize {
388 let mut max = 0;
389
390 let primary = self.get_multispan_max_line_num(span);
391 max = if primary > max { primary } else { max };
392
393 for sub in children {
394 let sub_result = self.get_multispan_max_line_num(&sub.span);
395 max = if sub_result > max { primary } else { max };
396 }
397 max
398 }
399
400 // This "fixes" MultiSpans that contain Spans that are pointing to locations inside of
401 // <*macros>. Since these locations are often difficult to read, we move these Spans from
402 // <*macros> to their corresponding use site.
403 fn fix_multispan_in_std_macros(&mut self, span: &mut MultiSpan) -> bool {
404 let mut spans_updated = false;
405
406 if let Some(ref cm) = self.cm {
407 let mut before_after: Vec<(Span, Span)> = vec![];
408 let mut new_labels: Vec<(Span, String)> = vec![];
409
410 // First, find all the spans in <*macros> and point instead at their use site
411 for sp in span.primary_spans() {
412 if (*sp == COMMAND_LINE_SP) || (*sp == DUMMY_SP) {
413 continue;
414 }
415 if cm.span_to_filename(sp.clone()).contains("macros>") {
416 let v = cm.macro_backtrace(sp.clone());
417 if let Some(use_site) = v.last() {
418 before_after.push((sp.clone(), use_site.call_site.clone()));
419 }
420 }
421 for trace in cm.macro_backtrace(sp.clone()).iter().rev() {
422 // Only show macro locations that are local
423 // and display them like a span_note
424 if let Some(def_site) = trace.def_site_span {
425 if (def_site == COMMAND_LINE_SP) || (def_site == DUMMY_SP) {
426 continue;
427 }
428 // Check to make sure we're not in any <*macros>
429 if !cm.span_to_filename(def_site).contains("macros>") &&
430 !trace.macro_decl_name.starts_with("#[")
431 {
432 new_labels.push((trace.call_site,
433 "in this macro invocation".to_string()));
434 break;
435 }
436 }
437 }
438 }
439 for (label_span, label_text) in new_labels {
440 span.push_span_label(label_span, label_text);
441 }
442 for sp_label in span.span_labels() {
443 if (sp_label.span == COMMAND_LINE_SP) || (sp_label.span == DUMMY_SP) {
444 continue;
445 }
446 if cm.span_to_filename(sp_label.span.clone()).contains("macros>") {
447 let v = cm.macro_backtrace(sp_label.span.clone());
448 if let Some(use_site) = v.last() {
449 before_after.push((sp_label.span.clone(), use_site.call_site.clone()));
450 }
451 }
452 }
453 // After we have them, make sure we replace these 'bad' def sites with their use sites
454 for (before, after) in before_after {
455 span.replace(before, after);
456 spans_updated = true;
457 }
458 }
459
460 spans_updated
461 }
462
463 // This does a small "fix" for multispans by looking to see if it can find any that
464 // point directly at <*macros>. Since these are often difficult to read, this
465 // will change the span to point at the use site.
466 fn fix_multispans_in_std_macros(&mut self,
467 span: &mut MultiSpan,
468 children: &mut Vec<SubDiagnostic>) {
469 let mut spans_updated = self.fix_multispan_in_std_macros(span);
470 for child in children.iter_mut() {
471 spans_updated |= self.fix_multispan_in_std_macros(&mut child.span);
472 }
473 if spans_updated {
474 children.push(SubDiagnostic {
475 level: Level::Note,
476 message:"this error originates in a macro outside of the current \
477 crate".to_string(),
478 span: MultiSpan::new(),
479 render_span: None
480 });
481 }
482 }
483
484 fn emit_message_default(&mut self,
485 msp: &MultiSpan,
486 msg: &str,
487 code: &Option<String>,
488 level: &Level,
489 max_line_num_len: usize,
490 is_secondary: bool)
491 -> io::Result<()> {
492 let mut buffer = StyledBuffer::new();
493
494 if msp.primary_spans().is_empty() && msp.span_labels().is_empty() && is_secondary {
495 // This is a secondary message with no span info
496 for _ in 0..max_line_num_len {
497 buffer.prepend(0, " ", Style::NoStyle);
498 }
499 draw_note_separator(&mut buffer, 0, max_line_num_len + 1);
500 buffer.append(0, &level.to_string(), Style::HeaderMsg);
501 buffer.append(0, ": ", Style::NoStyle);
502 buffer.append(0, msg, Style::NoStyle);
503 }
504 else {
505 buffer.append(0, &level.to_string(), Style::Level(level.clone()));
506 match code {
507 &Some(ref code) => {
508 buffer.append(0, "[", Style::Level(level.clone()));
509 buffer.append(0, &code, Style::Level(level.clone()));
510 buffer.append(0, "]", Style::Level(level.clone()));
511 }
512 _ => {}
513 }
514 buffer.append(0, ": ", Style::HeaderMsg);
515 buffer.append(0, msg, Style::HeaderMsg);
516 }
517
518 // Preprocess all the annotations so that they are grouped by file and by line number
519 // This helps us quickly iterate over the whole message (including secondary file spans)
520 let mut annotated_files = self.preprocess_annotations(msp);
521
522 // Make sure our primary file comes first
523 let primary_lo =
524 if let (Some(ref cm), Some(ref primary_span)) = (self.cm.as_ref(),
525 msp.primary_span().as_ref()) {
526 if primary_span != &&DUMMY_SP && primary_span != &&COMMAND_LINE_SP {
527 cm.lookup_char_pos(primary_span.lo)
528 }
529 else {
530 emit_to_destination(&buffer.render(), level, &mut self.dst)?;
531 return Ok(());
532 }
533 } else {
534 // If we don't have span information, emit and exit
535 emit_to_destination(&buffer.render(), level, &mut self.dst)?;
536 return Ok(());
537 };
538 if let Ok(pos) =
539 annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name)) {
540 annotated_files.swap(0, pos);
541 }
542
543 // Print out the annotate source lines that correspond with the error
544 for annotated_file in annotated_files {
545 // print out the span location and spacer before we print the annotated source
546 // to do this, we need to know if this span will be primary
547 let is_primary = primary_lo.file.name == annotated_file.file.name;
548 if is_primary {
549 // remember where we are in the output buffer for easy reference
550 let buffer_msg_line_offset = buffer.num_lines();
551
552 buffer.prepend(buffer_msg_line_offset, "--> ", Style::LineNumber);
553 let loc = primary_lo.clone();
554 buffer.append(buffer_msg_line_offset,
555 &format!("{}:{}:{}", loc.file.name, loc.line, loc.col.0 + 1),
556 Style::LineAndColumn);
557 for _ in 0..max_line_num_len {
558 buffer.prepend(buffer_msg_line_offset, " ", Style::NoStyle);
559 }
560 } else {
561 // remember where we are in the output buffer for easy reference
562 let buffer_msg_line_offset = buffer.num_lines();
563
564 // Add spacing line
565 draw_col_separator(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1);
566
567 // Then, the secondary file indicator
568 buffer.prepend(buffer_msg_line_offset + 1, "::: ", Style::LineNumber);
569 buffer.append(buffer_msg_line_offset + 1,
570 &annotated_file.file.name,
571 Style::LineAndColumn);
572 for _ in 0..max_line_num_len {
573 buffer.prepend(buffer_msg_line_offset + 1, " ", Style::NoStyle);
574 }
575 }
576
577 // Put in the spacer between the location and annotated source
578 let buffer_msg_line_offset = buffer.num_lines();
579 draw_col_separator_no_space(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1);
580
581 // Next, output the annotate source for this file
582 for line_idx in 0..annotated_file.lines.len() {
583 self.render_source_line(&mut buffer,
584 annotated_file.file.clone(),
585 &annotated_file.lines[line_idx],
586 3 + max_line_num_len);
587
588 // check to see if we need to print out or elide lines that come between
589 // this annotated line and the next one
590 if line_idx < (annotated_file.lines.len() - 1) {
591 let line_idx_delta = annotated_file.lines[line_idx + 1].line_index -
592 annotated_file.lines[line_idx].line_index;
593 if line_idx_delta > 2 {
594 let last_buffer_line_num = buffer.num_lines();
595 buffer.puts(last_buffer_line_num, 0, "...", Style::LineNumber);
596 } else if line_idx_delta == 2 {
597 let unannotated_line = annotated_file.file
598 .get_line(annotated_file.lines[line_idx].line_index)
599 .unwrap_or("");
600
601 let last_buffer_line_num = buffer.num_lines();
602
603 buffer.puts(last_buffer_line_num,
604 0,
605 &(annotated_file.lines[line_idx + 1].line_index - 1)
606 .to_string(),
607 Style::LineNumber);
608 draw_col_separator(&mut buffer, last_buffer_line_num, 1 + max_line_num_len);
609 buffer.puts(last_buffer_line_num,
610 3 + max_line_num_len,
611 &unannotated_line,
612 Style::Quotation);
613 }
614 }
615 }
616 }
617
618 // final step: take our styled buffer, render it, then output it
619 emit_to_destination(&buffer.render(), level, &mut self.dst)?;
620
621 Ok(())
622 }
623 fn emit_suggestion_default(&mut self,
624 suggestion: &CodeSuggestion,
625 level: &Level,
626 msg: &str,
627 max_line_num_len: usize)
628 -> io::Result<()> {
629 use std::borrow::Borrow;
630
631 let primary_span = suggestion.msp.primary_span().unwrap();
632 if let Some(ref cm) = self.cm {
633 let mut buffer = StyledBuffer::new();
634
635 buffer.append(0, &level.to_string(), Style::Level(level.clone()));
636 buffer.append(0, ": ", Style::HeaderMsg);
637 buffer.append(0, msg, Style::HeaderMsg);
638
639 let lines = cm.span_to_lines(primary_span).unwrap();
640
641 assert!(!lines.lines.is_empty());
642
643 let complete = suggestion.splice_lines(cm.borrow());
644
645 // print the suggestion without any line numbers, but leave
646 // space for them. This helps with lining up with previous
647 // snippets from the actual error being reported.
648 let mut lines = complete.lines();
649 let mut row_num = 1;
650 for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) {
651 draw_col_separator(&mut buffer, row_num, max_line_num_len + 1);
652 buffer.append(row_num, line, Style::NoStyle);
653 row_num += 1;
654 }
655
656 // if we elided some lines, add an ellipsis
657 if let Some(_) = lines.next() {
658 buffer.append(row_num, "...", Style::NoStyle);
659 }
660 emit_to_destination(&buffer.render(), level, &mut self.dst)?;
661 }
662 Ok(())
663 }
664 fn emit_messages_default(&mut self,
665 level: &Level,
666 message: &String,
667 code: &Option<String>,
668 span: &MultiSpan,
669 children: &Vec<SubDiagnostic>) {
670 let max_line_num = self.get_max_line_num(span, children);
671 let max_line_num_len = max_line_num.to_string().len();
672
673 match self.emit_message_default(span,
674 message,
675 code,
676 level,
677 max_line_num_len,
678 false) {
679 Ok(()) => {
680 if !children.is_empty() {
681 let mut buffer = StyledBuffer::new();
682 draw_col_separator_no_space(&mut buffer, 0, max_line_num_len + 1);
683 match emit_to_destination(&buffer.render(), level, &mut self.dst) {
684 Ok(()) => (),
685 Err(e) => panic!("failed to emit error: {}", e)
686 }
687 }
688 for child in children {
689 match child.render_span {
690 Some(FullSpan(ref msp)) => {
691 match self.emit_message_default(msp,
692 &child.message,
693 &None,
694 &child.level,
695 max_line_num_len,
696 true) {
697 Err(e) => panic!("failed to emit error: {}", e),
698 _ => ()
699 }
700 },
701 Some(Suggestion(ref cs)) => {
702 match self.emit_suggestion_default(cs,
703 &child.level,
704 &child.message,
705 max_line_num_len) {
706 Err(e) => panic!("failed to emit error: {}", e),
707 _ => ()
708 }
709 },
710 None => {
711 match self.emit_message_default(&child.span,
712 &child.message,
713 &None,
714 &child.level,
715 max_line_num_len,
716 true) {
717 Err(e) => panic!("failed to emit error: {}", e),
718 _ => ()
719 }
720 }
721 }
722 }
723 }
724 Err(e) => panic!("failed to emit error: {}", e)
725 }
726 match write!(&mut self.dst, "\n") {
727 Err(e) => panic!("failed to emit error: {}", e),
728 _ => match self.dst.flush() {
729 Err(e) => panic!("failed to emit error: {}", e),
730 _ => ()
731 }
732 }
733 }
734 }
735
736 fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
737 buffer.puts(line, col, "| ", Style::LineNumber);
738 }
739
740 fn draw_col_separator_no_space(buffer: &mut StyledBuffer, line: usize, col: usize) {
741 buffer.puts(line, col, "|", Style::LineNumber);
742 }
743
744 fn draw_note_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
745 buffer.puts(line, col, "= ", Style::LineNumber);
746 }
747
748 fn overlaps(a1: &Annotation, a2: &Annotation) -> bool {
749 (a2.start_col..a2.end_col).contains(a1.start_col) ||
750 (a1.start_col..a1.end_col).contains(a2.start_col)
751 }
752
753 fn emit_to_destination(rendered_buffer: &Vec<Vec<StyledString>>,
754 lvl: &Level,
755 dst: &mut Destination) -> io::Result<()> {
756 use lock;
757
758 // In order to prevent error message interleaving, where multiple error lines get intermixed
759 // when multiple compiler processes error simultaneously, we emit errors with additional
760 // steps.
761 //
762 // On Unix systems, we write into a buffered terminal rather than directly to a terminal. When
763 // the .flush() is called we take the buffer created from the buffered writes and write it at
764 // one shot. Because the Unix systems use ANSI for the colors, which is a text-based styling
765 // scheme, this buffered approach works and maintains the styling.
766 //
767 // On Windows, styling happens through calls to a terminal API. This prevents us from using the
768 // same buffering approach. Instead, we use a global Windows mutex, which we acquire long
769 // enough to output the full error message, then we release.
770 let _buffer_lock = lock::acquire_global_lock("rustc_errors");
771 for line in rendered_buffer {
772 for part in line {
773 dst.apply_style(lvl.clone(), part.style)?;
774 write!(dst, "{}", part.text)?;
775 dst.reset_attrs()?;
776 }
777 write!(dst, "\n")?;
778 }
779 dst.flush()?;
780 Ok(())
781 }
782
783 #[cfg(unix)]
784 fn stderr_isatty() -> bool {
785 use libc;
786 unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
787 }
788 #[cfg(windows)]
789 fn stderr_isatty() -> bool {
790 type DWORD = u32;
791 type BOOL = i32;
792 type HANDLE = *mut u8;
793 const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD;
794 extern "system" {
795 fn GetStdHandle(which: DWORD) -> HANDLE;
796 fn GetConsoleMode(hConsoleHandle: HANDLE,
797 lpMode: *mut DWORD) -> BOOL;
798 }
799 unsafe {
800 let handle = GetStdHandle(STD_ERROR_HANDLE);
801 let mut out = 0;
802 GetConsoleMode(handle, &mut out) != 0
803 }
804 }
805
806 pub type BufferedStderr = term::Terminal<Output = BufferedWriter> + Send;
807
808 pub enum Destination {
809 Terminal(Box<term::StderrTerminal>),
810 BufferedTerminal(Box<BufferedStderr>),
811 Raw(Box<Write + Send>),
812 }
813
814 /// Buffered writer gives us a way on Unix to buffer up an entire error message before we output
815 /// it. This helps to prevent interleaving of multiple error messages when multiple compiler
816 /// processes error simultaneously
817 pub struct BufferedWriter {
818 buffer: Vec<u8>,
819 }
820
821 impl BufferedWriter {
822 // note: we use _new because the conditional compilation at its use site may make this
823 // this function unused on some platforms
824 fn _new() -> BufferedWriter {
825 BufferedWriter {
826 buffer: vec![]
827 }
828 }
829 }
830
831 impl Write for BufferedWriter {
832 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
833 for b in buf {
834 self.buffer.push(*b);
835 }
836 Ok(buf.len())
837 }
838 fn flush(&mut self) -> io::Result<()> {
839 let mut stderr = io::stderr();
840 let result = (|| {
841 stderr.write_all(&self.buffer)?;
842 stderr.flush()
843 })();
844 self.buffer.clear();
845 result
846 }
847 }
848
849 impl Destination {
850 #[cfg(not(windows))]
851 /// When not on Windows, prefer the buffered terminal so that we can buffer an entire error
852 /// to be emitted at one time.
853 fn from_stderr() -> Destination {
854 let stderr: Option<Box<BufferedStderr>> =
855 term::TerminfoTerminal::new(BufferedWriter::_new())
856 .map(|t| Box::new(t) as Box<BufferedStderr>);
857
858 match stderr {
859 Some(t) => BufferedTerminal(t),
860 None => Raw(Box::new(io::stderr())),
861 }
862 }
863
864 #[cfg(windows)]
865 /// Return a normal, unbuffered terminal when on Windows.
866 fn from_stderr() -> Destination {
867 let stderr: Option<Box<term::StderrTerminal>> =
868 term::TerminfoTerminal::new(io::stderr())
869 .map(|t| Box::new(t) as Box<term::StderrTerminal>)
870 .or_else(|| term::WinConsole::new(io::stderr()).ok()
871 .map(|t| Box::new(t) as Box<term::StderrTerminal>));
872
873 match stderr {
874 Some(t) => Terminal(t),
875 None => Raw(Box::new(io::stderr())),
876 }
877 }
878
879 fn apply_style(&mut self,
880 lvl: Level,
881 style: Style)
882 -> io::Result<()> {
883 match style {
884 Style::FileNameStyle | Style::LineAndColumn => {}
885 Style::LineNumber => {
886 self.start_attr(term::Attr::Bold)?;
887 if cfg!(windows) {
888 self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_CYAN))?;
889 } else {
890 self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?;
891 }
892 }
893 Style::ErrorCode => {
894 self.start_attr(term::Attr::Bold)?;
895 self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA))?;
896 }
897 Style::Quotation => {}
898 Style::OldSchoolNote => {
899 self.start_attr(term::Attr::Bold)?;
900 self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_GREEN))?;
901 }
902 Style::OldSchoolNoteText | Style::HeaderMsg => {
903 self.start_attr(term::Attr::Bold)?;
904 if cfg!(windows) {
905 self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_WHITE))?;
906 }
907 }
908 Style::UnderlinePrimary | Style::LabelPrimary => {
909 self.start_attr(term::Attr::Bold)?;
910 self.start_attr(term::Attr::ForegroundColor(lvl.color()))?;
911 }
912 Style::UnderlineSecondary |
913 Style::LabelSecondary => {
914 self.start_attr(term::Attr::Bold)?;
915 if cfg!(windows) {
916 self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_CYAN))?;
917 } else {
918 self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?;
919 }
920 }
921 Style::NoStyle => {}
922 Style::Level(l) => {
923 self.start_attr(term::Attr::Bold)?;
924 self.start_attr(term::Attr::ForegroundColor(l.color()))?;
925 }
926 }
927 Ok(())
928 }
929
930 fn start_attr(&mut self, attr: term::Attr) -> io::Result<()> {
931 match *self {
932 Terminal(ref mut t) => { t.attr(attr)?; }
933 BufferedTerminal(ref mut t) => { t.attr(attr)?; }
934 Raw(_) => { }
935 }
936 Ok(())
937 }
938
939 fn reset_attrs(&mut self) -> io::Result<()> {
940 match *self {
941 Terminal(ref mut t) => { t.reset()?; }
942 BufferedTerminal(ref mut t) => { t.reset()?; }
943 Raw(_) => { }
944 }
945 Ok(())
946 }
947 }
948
949 impl Write for Destination {
950 fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
951 match *self {
952 Terminal(ref mut t) => t.write(bytes),
953 BufferedTerminal(ref mut t) => t.write(bytes),
954 Raw(ref mut w) => w.write(bytes),
955 }
956 }
957 fn flush(&mut self) -> io::Result<()> {
958 match *self {
959 Terminal(ref mut t) => t.flush(),
960 BufferedTerminal(ref mut t) => t.flush(),
961 Raw(ref mut w) => w.flush(),
962 }
963 }
964 }