]> git.lizzy.rs Git - rust.git/blob - src/librustc_errors/emitter.rs
5fd9226aba9d4ea619619f93d32a3d200a666eeb
[rust.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                             new_labels.push((trace.call_site,
431                                              "in this macro invocation".to_string()));
432                             break;
433                         }
434                     }
435                 }
436             }
437             for (label_span, label_text) in new_labels {
438                 span.push_span_label(label_span, label_text);
439             }
440             for sp_label in span.span_labels() {
441                 if (sp_label.span == COMMAND_LINE_SP) || (sp_label.span == DUMMY_SP) {
442                     continue;
443                 }
444                 if cm.span_to_filename(sp_label.span.clone()).contains("macros>") {
445                     let v = cm.macro_backtrace(sp_label.span.clone());
446                     if let Some(use_site) = v.last() {
447                         before_after.push((sp_label.span.clone(), use_site.call_site.clone()));
448                     }
449                 }
450             }
451             // After we have them, make sure we replace these 'bad' def sites with their use sites
452             for (before, after) in before_after {
453                 span.replace(before, after);
454                 spans_updated = true;
455             }
456         }
457
458         spans_updated
459     }
460
461     // This does a small "fix" for multispans by looking to see if it can find any that
462     // point directly at <*macros>. Since these are often difficult to read, this
463     // will change the span to point at the use site.
464     fn fix_multispans_in_std_macros(&mut self,
465                                     span: &mut MultiSpan,
466                                     children: &mut Vec<SubDiagnostic>) {
467         let mut spans_updated = self.fix_multispan_in_std_macros(span);
468         for child in children.iter_mut() {
469             spans_updated |= self.fix_multispan_in_std_macros(&mut child.span);
470         }
471         if spans_updated {
472             children.push(SubDiagnostic {
473                 level: Level::Note,
474                 message: "this error originates in a macro from the standard library".to_string(),
475                 span: MultiSpan::new(),
476                 render_span: None
477             });
478         }
479     }
480
481     fn emit_message_default(&mut self,
482                             msp: &MultiSpan,
483                             msg: &str,
484                             code: &Option<String>,
485                             level: &Level,
486                             max_line_num_len: usize,
487                             is_secondary: bool)
488                             -> io::Result<()> {
489         let mut buffer = StyledBuffer::new();
490
491         if msp.primary_spans().is_empty() && msp.span_labels().is_empty() && is_secondary {
492             // This is a secondary message with no span info
493             for _ in 0..max_line_num_len {
494                 buffer.prepend(0, " ", Style::NoStyle);
495             }
496             draw_note_separator(&mut buffer, 0, max_line_num_len + 1);
497             buffer.append(0, &level.to_string(), Style::HeaderMsg);
498             buffer.append(0, ": ", Style::NoStyle);
499             buffer.append(0, msg, Style::NoStyle);
500         }
501         else {
502             buffer.append(0, &level.to_string(), Style::Level(level.clone()));
503             match code {
504                 &Some(ref code) => {
505                     buffer.append(0, "[", Style::Level(level.clone()));
506                     buffer.append(0, &code, Style::Level(level.clone()));
507                     buffer.append(0, "]", Style::Level(level.clone()));
508                 }
509                 _ => {}
510             }
511             buffer.append(0, ": ", Style::HeaderMsg);
512             buffer.append(0, msg, Style::HeaderMsg);
513         }
514
515         // Preprocess all the annotations so that they are grouped by file and by line number
516         // This helps us quickly iterate over the whole message (including secondary file spans)
517         let mut annotated_files = self.preprocess_annotations(msp);
518
519         // Make sure our primary file comes first
520         let primary_lo =
521             if let (Some(ref cm), Some(ref primary_span)) = (self.cm.as_ref(),
522                                                              msp.primary_span().as_ref()) {
523                 if primary_span != &&DUMMY_SP && primary_span != &&COMMAND_LINE_SP {
524                     cm.lookup_char_pos(primary_span.lo)
525                 }
526                 else {
527                     emit_to_destination(&buffer.render(), level, &mut self.dst)?;
528                     return Ok(());
529                 }
530             } else {
531                 // If we don't have span information, emit and exit
532                 emit_to_destination(&buffer.render(), level, &mut self.dst)?;
533                 return Ok(());
534             };
535         if let Ok(pos) =
536                 annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name)) {
537             annotated_files.swap(0, pos);
538         }
539
540         // Print out the annotate source lines that correspond with the error
541         for annotated_file in annotated_files {
542             // print out the span location and spacer before we print the annotated source
543             // to do this, we need to know if this span will be primary
544             let is_primary = primary_lo.file.name == annotated_file.file.name;
545             if is_primary {
546                 // remember where we are in the output buffer for easy reference
547                 let buffer_msg_line_offset = buffer.num_lines();
548
549                 buffer.prepend(buffer_msg_line_offset, "--> ", Style::LineNumber);
550                 let loc = primary_lo.clone();
551                 buffer.append(buffer_msg_line_offset,
552                                 &format!("{}:{}:{}", loc.file.name, loc.line, loc.col.0 + 1),
553                                 Style::LineAndColumn);
554                 for _ in 0..max_line_num_len {
555                     buffer.prepend(buffer_msg_line_offset, " ", Style::NoStyle);
556                 }
557             } else {
558                 // remember where we are in the output buffer for easy reference
559                 let buffer_msg_line_offset = buffer.num_lines();
560
561                 // Add spacing line
562                 draw_col_separator(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1);
563
564                 // Then, the secondary file indicator
565                 buffer.prepend(buffer_msg_line_offset + 1, "::: ", Style::LineNumber);
566                 buffer.append(buffer_msg_line_offset + 1,
567                                 &annotated_file.file.name,
568                                 Style::LineAndColumn);
569                 for _ in 0..max_line_num_len {
570                     buffer.prepend(buffer_msg_line_offset + 1, " ", Style::NoStyle);
571                 }
572             }
573
574             // Put in the spacer between the location and annotated source
575             let buffer_msg_line_offset = buffer.num_lines();
576             draw_col_separator_no_space(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1);
577
578             // Next, output the annotate source for this file
579             for line_idx in 0..annotated_file.lines.len() {
580                 self.render_source_line(&mut buffer,
581                                         annotated_file.file.clone(),
582                                         &annotated_file.lines[line_idx],
583                                         3 + max_line_num_len);
584
585                 // check to see if we need to print out or elide lines that come between
586                 // this annotated line and the next one
587                 if line_idx < (annotated_file.lines.len() - 1) {
588                     let line_idx_delta = annotated_file.lines[line_idx + 1].line_index -
589                                             annotated_file.lines[line_idx].line_index;
590                     if line_idx_delta > 2 {
591                         let last_buffer_line_num = buffer.num_lines();
592                         buffer.puts(last_buffer_line_num, 0, "...", Style::LineNumber);
593                     } else if line_idx_delta == 2 {
594                         let unannotated_line = annotated_file.file
595                             .get_line(annotated_file.lines[line_idx].line_index)
596                             .unwrap_or("");
597
598                         let last_buffer_line_num = buffer.num_lines();
599
600                         buffer.puts(last_buffer_line_num,
601                                     0,
602                                     &(annotated_file.lines[line_idx + 1].line_index - 1)
603                                         .to_string(),
604                                     Style::LineNumber);
605                         draw_col_separator(&mut buffer, last_buffer_line_num, 1 + max_line_num_len);
606                         buffer.puts(last_buffer_line_num,
607                                     3 + max_line_num_len,
608                                     &unannotated_line,
609                                     Style::Quotation);
610                     }
611                 }
612             }
613         }
614
615         // final step: take our styled buffer, render it, then output it
616         emit_to_destination(&buffer.render(), level, &mut self.dst)?;
617
618         Ok(())
619     }
620     fn emit_suggestion_default(&mut self,
621                                suggestion: &CodeSuggestion,
622                                level: &Level,
623                                msg: &str,
624                                max_line_num_len: usize)
625                                -> io::Result<()> {
626         use std::borrow::Borrow;
627
628         let primary_span = suggestion.msp.primary_span().unwrap();
629         if let Some(ref cm) = self.cm {
630             let mut buffer = StyledBuffer::new();
631
632             buffer.append(0, &level.to_string(), Style::Level(level.clone()));
633             buffer.append(0, ": ", Style::HeaderMsg);
634             buffer.append(0, msg, Style::HeaderMsg);
635
636             let lines = cm.span_to_lines(primary_span).unwrap();
637
638             assert!(!lines.lines.is_empty());
639
640             let complete = suggestion.splice_lines(cm.borrow());
641
642             // print the suggestion without any line numbers, but leave
643             // space for them. This helps with lining up with previous
644             // snippets from the actual error being reported.
645             let mut lines = complete.lines();
646             let mut row_num = 1;
647             for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) {
648                 draw_col_separator(&mut buffer, row_num, max_line_num_len + 1);
649                 buffer.append(row_num, line, Style::NoStyle);
650                 row_num += 1;
651             }
652
653             // if we elided some lines, add an ellipsis
654             if let Some(_) = lines.next() {
655                 buffer.append(row_num, "...", Style::NoStyle);
656             }
657             emit_to_destination(&buffer.render(), level, &mut self.dst)?;
658         }
659         Ok(())
660     }
661     fn emit_messages_default(&mut self,
662                              level: &Level,
663                              message: &String,
664                              code: &Option<String>,
665                              span: &MultiSpan,
666                              children: &Vec<SubDiagnostic>) {
667         let max_line_num = self.get_max_line_num(span, children);
668         let max_line_num_len = max_line_num.to_string().len();
669
670         match self.emit_message_default(span,
671                                         message,
672                                         code,
673                                         level,
674                                         max_line_num_len,
675                                         false) {
676             Ok(()) => {
677                 if !children.is_empty() {
678                     let mut buffer = StyledBuffer::new();
679                     draw_col_separator_no_space(&mut buffer, 0, max_line_num_len + 1);
680                     match emit_to_destination(&buffer.render(), level, &mut self.dst) {
681                         Ok(()) => (),
682                         Err(e) => panic!("failed to emit error: {}", e)
683                     }
684                 }
685                 for child in children {
686                     match child.render_span {
687                         Some(FullSpan(ref msp)) => {
688                             match self.emit_message_default(msp,
689                                                             &child.message,
690                                                             &None,
691                                                             &child.level,
692                                                             max_line_num_len,
693                                                             true) {
694                                 Err(e) => panic!("failed to emit error: {}", e),
695                                 _ => ()
696                             }
697                         },
698                         Some(Suggestion(ref cs)) => {
699                             match self.emit_suggestion_default(cs,
700                                                                &child.level,
701                                                                &child.message,
702                                                                max_line_num_len) {
703                                 Err(e) => panic!("failed to emit error: {}", e),
704                                 _ => ()
705                             }
706                         },
707                         None => {
708                             match self.emit_message_default(&child.span,
709                                                             &child.message,
710                                                             &None,
711                                                             &child.level,
712                                                             max_line_num_len,
713                                                             true) {
714                                 Err(e) => panic!("failed to emit error: {}", e),
715                                 _ => ()
716                             }
717                         }
718                     }
719                 }
720             }
721             Err(e) => panic!("failed to emit error: {}", e)
722         }
723         match write!(&mut self.dst, "\n") {
724             Err(e) => panic!("failed to emit error: {}", e),
725             _ => ()
726         }
727     }
728 }
729
730 fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
731     buffer.puts(line, col, "| ", Style::LineNumber);
732 }
733
734 fn draw_col_separator_no_space(buffer: &mut StyledBuffer, line: usize, col: usize) {
735     buffer.puts(line, col, "|", Style::LineNumber);
736 }
737
738 fn draw_note_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
739     buffer.puts(line, col, "= ", Style::LineNumber);
740 }
741
742 fn overlaps(a1: &Annotation, a2: &Annotation) -> bool {
743     (a2.start_col..a2.end_col).contains(a1.start_col) ||
744     (a1.start_col..a1.end_col).contains(a2.start_col)
745 }
746
747 fn emit_to_destination(rendered_buffer: &Vec<Vec<StyledString>>,
748         lvl: &Level,
749         dst: &mut Destination) -> io::Result<()> {
750     for line in rendered_buffer {
751         for part in line {
752             dst.apply_style(lvl.clone(), part.style)?;
753             write!(dst, "{}", part.text)?;
754             dst.reset_attrs()?;
755         }
756         write!(dst, "\n")?;
757     }
758     Ok(())
759 }
760
761 #[cfg(unix)]
762 fn stderr_isatty() -> bool {
763     use libc;
764     unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
765 }
766 #[cfg(windows)]
767 fn stderr_isatty() -> bool {
768     type DWORD = u32;
769     type BOOL = i32;
770     type HANDLE = *mut u8;
771     const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD;
772     extern "system" {
773         fn GetStdHandle(which: DWORD) -> HANDLE;
774         fn GetConsoleMode(hConsoleHandle: HANDLE,
775                           lpMode: *mut DWORD) -> BOOL;
776     }
777     unsafe {
778         let handle = GetStdHandle(STD_ERROR_HANDLE);
779         let mut out = 0;
780         GetConsoleMode(handle, &mut out) != 0
781     }
782 }
783
784 pub enum Destination {
785     Terminal(Box<term::StderrTerminal>),
786     Raw(Box<Write + Send>),
787 }
788
789 impl Destination {
790     fn from_stderr() -> Destination {
791         match term::stderr() {
792             Some(t) => Terminal(t),
793             None    => Raw(Box::new(io::stderr())),
794         }
795     }
796
797     fn apply_style(&mut self,
798                    lvl: Level,
799                    style: Style)
800                    -> io::Result<()> {
801         match style {
802             Style::FileNameStyle | Style::LineAndColumn => {}
803             Style::LineNumber => {
804                 try!(self.start_attr(term::Attr::Bold));
805                 try!(self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE)));
806             }
807             Style::ErrorCode => {
808                 try!(self.start_attr(term::Attr::Bold));
809                 try!(self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA)));
810             }
811             Style::Quotation => {}
812             Style::OldSchoolNote => {
813                 try!(self.start_attr(term::Attr::Bold));
814                 try!(self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_GREEN)));
815             }
816             Style::OldSchoolNoteText | Style::HeaderMsg => {
817                 try!(self.start_attr(term::Attr::Bold));
818             }
819             Style::UnderlinePrimary | Style::LabelPrimary => {
820                 try!(self.start_attr(term::Attr::Bold));
821                 try!(self.start_attr(term::Attr::ForegroundColor(lvl.color())));
822             }
823             Style::UnderlineSecondary |
824             Style::LabelSecondary => {
825                 try!(self.start_attr(term::Attr::Bold));
826                 try!(self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE)));
827             }
828             Style::NoStyle => {}
829             Style::Level(l) => {
830                 try!(self.start_attr(term::Attr::Bold));
831                 try!(self.start_attr(term::Attr::ForegroundColor(l.color())));
832             }
833         }
834         Ok(())
835     }
836
837     fn start_attr(&mut self, attr: term::Attr) -> io::Result<()> {
838         match *self {
839             Terminal(ref mut t) => { t.attr(attr)?; }
840             Raw(_) => { }
841         }
842         Ok(())
843     }
844
845     fn reset_attrs(&mut self) -> io::Result<()> {
846         match *self {
847             Terminal(ref mut t) => { t.reset()?; }
848             Raw(_) => { }
849         }
850         Ok(())
851     }
852 }
853
854 impl Write for Destination {
855     fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
856         match *self {
857             Terminal(ref mut t) => t.write(bytes),
858             Raw(ref mut w) => w.write(bytes),
859         }
860     }
861     fn flush(&mut self) -> io::Result<()> {
862         match *self {
863             Terminal(ref mut t) => t.flush(),
864             Raw(ref mut w) => w.flush(),
865         }
866     }
867 }