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