]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/diagnostic.rs
Provide a filemap ctor with line info
[rust.git] / src / libsyntax / diagnostic.rs
1 // Copyright 2012 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 pub use self::Level::*;
12 pub use self::RenderSpan::*;
13 pub use self::ColorConfig::*;
14 use self::Destination::*;
15
16 use codemap::{self, COMMAND_LINE_SP, COMMAND_LINE_EXPN, Pos, Span};
17 use diagnostics;
18
19 use std::cell::{RefCell, Cell};
20 use std::{cmp, error, fmt};
21 use std::io::prelude::*;
22 use std::io;
23 use term::{self, WriterWrapper};
24 use libc;
25
26 /// maximum number of lines we will print for each error; arbitrary.
27 const MAX_LINES: usize = 6;
28
29 #[derive(Clone)]
30 pub enum RenderSpan {
31     /// A FullSpan renders with both with an initial line for the
32     /// message, prefixed by file:linenum, followed by a summary of
33     /// the source code covered by the span.
34     FullSpan(Span),
35
36     /// Similar to a FullSpan, but the cited position is the end of
37     /// the span, instead of the start. Used, at least, for telling
38     /// compiletest/runtest to look at the last line of the span
39     /// (since `end_highlight_lines` displays an arrow to the end
40     /// of the span).
41     EndSpan(Span),
42
43     /// A suggestion renders with both with an initial line for the
44     /// message, prefixed by file:linenum, followed by a summary
45     /// of hypothetical source code, where the `String` is spliced
46     /// into the lines in place of the code covered by the span.
47     Suggestion(Span, String),
48
49     /// A FileLine renders with just a line for the message prefixed
50     /// by file:linenum.
51     FileLine(Span),
52 }
53
54 impl RenderSpan {
55     fn span(&self) -> Span {
56         match *self {
57             FullSpan(s) |
58             Suggestion(s, _) |
59             EndSpan(s) |
60             FileLine(s) =>
61                 s
62         }
63     }
64 }
65
66 #[derive(Clone, Copy)]
67 pub enum ColorConfig {
68     Auto,
69     Always,
70     Never
71 }
72
73 pub trait Emitter {
74     fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, Span)>,
75             msg: &str, code: Option<&str>, lvl: Level);
76     fn custom_emit(&mut self, cm: &codemap::CodeMap,
77                    sp: RenderSpan, msg: &str, lvl: Level);
78 }
79
80 /// Used as a return value to signify a fatal error occurred. (It is also
81 /// used as the argument to panic at the moment, but that will eventually
82 /// not be true.)
83 #[derive(Copy, Clone, Debug)]
84 #[must_use]
85 pub struct FatalError;
86
87 impl fmt::Display for FatalError {
88     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
89         write!(f, "parser fatal error")
90     }
91 }
92
93 impl error::Error for FatalError {
94     fn description(&self) -> &str {
95         "The parser has encountered a fatal error"
96     }
97 }
98
99 /// Signifies that the compiler died with an explicit call to `.bug`
100 /// or `.span_bug` rather than a failed assertion, etc.
101 #[derive(Copy, Clone, Debug)]
102 pub struct ExplicitBug;
103
104 impl fmt::Display for ExplicitBug {
105     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
106         write!(f, "parser internal bug")
107     }
108 }
109
110 impl error::Error for ExplicitBug {
111     fn description(&self) -> &str {
112         "The parser has encountered an internal bug"
113     }
114 }
115
116 /// A span-handler is like a handler but also
117 /// accepts span information for source-location
118 /// reporting.
119 pub struct SpanHandler {
120     pub handler: Handler,
121     pub cm: codemap::CodeMap,
122 }
123
124 impl SpanHandler {
125     pub fn new(handler: Handler, cm: codemap::CodeMap) -> SpanHandler {
126         SpanHandler {
127             handler: handler,
128             cm: cm,
129         }
130     }
131     pub fn span_fatal(&self, sp: Span, msg: &str) -> FatalError {
132         self.handler.emit(Some((&self.cm, sp)), msg, Fatal);
133         return FatalError;
134     }
135     pub fn span_fatal_with_code(&self, sp: Span, msg: &str, code: &str) -> FatalError {
136         self.handler.emit_with_code(Some((&self.cm, sp)), msg, code, Fatal);
137         return FatalError;
138     }
139     pub fn span_err(&self, sp: Span, msg: &str) {
140         self.handler.emit(Some((&self.cm, sp)), msg, Error);
141         self.handler.bump_err_count();
142     }
143     pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) {
144         self.handler.emit_with_code(Some((&self.cm, sp)), msg, code, Error);
145         self.handler.bump_err_count();
146     }
147     pub fn span_warn(&self, sp: Span, msg: &str) {
148         self.handler.emit(Some((&self.cm, sp)), msg, Warning);
149     }
150     pub fn span_warn_with_code(&self, sp: Span, msg: &str, code: &str) {
151         self.handler.emit_with_code(Some((&self.cm, sp)), msg, code, Warning);
152     }
153     pub fn span_note(&self, sp: Span, msg: &str) {
154         self.handler.emit(Some((&self.cm, sp)), msg, Note);
155     }
156     pub fn span_end_note(&self, sp: Span, msg: &str) {
157         self.handler.custom_emit(&self.cm, EndSpan(sp), msg, Note);
158     }
159     pub fn span_help(&self, sp: Span, msg: &str) {
160         self.handler.emit(Some((&self.cm, sp)), msg, Help);
161     }
162     /// Prints out a message with a suggested edit of the code.
163     ///
164     /// See `diagnostic::RenderSpan::Suggestion` for more information.
165     pub fn span_suggestion(&self, sp: Span, msg: &str, suggestion: String) {
166         self.handler.custom_emit(&self.cm, Suggestion(sp, suggestion), msg, Help);
167     }
168     pub fn fileline_note(&self, sp: Span, msg: &str) {
169         self.handler.custom_emit(&self.cm, FileLine(sp), msg, Note);
170     }
171     pub fn fileline_help(&self, sp: Span, msg: &str) {
172         self.handler.custom_emit(&self.cm, FileLine(sp), msg, Help);
173     }
174     pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
175         self.handler.emit(Some((&self.cm, sp)), msg, Bug);
176         panic!(ExplicitBug);
177     }
178     pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! {
179         self.span_bug(sp, &format!("unimplemented {}", msg));
180     }
181     pub fn handler<'a>(&'a self) -> &'a Handler {
182         &self.handler
183     }
184 }
185
186 /// A handler deals with errors; certain errors
187 /// (fatal, bug, unimpl) may cause immediate exit,
188 /// others log errors for later reporting.
189 pub struct Handler {
190     err_count: Cell<usize>,
191     emit: RefCell<Box<Emitter + Send>>,
192     pub can_emit_warnings: bool
193 }
194
195 impl Handler {
196     pub fn new(color_config: ColorConfig,
197                registry: Option<diagnostics::registry::Registry>,
198                can_emit_warnings: bool) -> Handler {
199         let emitter = Box::new(EmitterWriter::stderr(color_config, registry));
200         Handler::with_emitter(can_emit_warnings, emitter)
201     }
202     pub fn with_emitter(can_emit_warnings: bool, e: Box<Emitter + Send>) -> Handler {
203         Handler {
204             err_count: Cell::new(0),
205             emit: RefCell::new(e),
206             can_emit_warnings: can_emit_warnings
207         }
208     }
209     pub fn fatal(&self, msg: &str) -> ! {
210         self.emit.borrow_mut().emit(None, msg, None, Fatal);
211         panic!(FatalError);
212     }
213     pub fn err(&self, msg: &str) {
214         self.emit.borrow_mut().emit(None, msg, None, Error);
215         self.bump_err_count();
216     }
217     pub fn bump_err_count(&self) {
218         self.err_count.set(self.err_count.get() + 1);
219     }
220     pub fn err_count(&self) -> usize {
221         self.err_count.get()
222     }
223     pub fn has_errors(&self) -> bool {
224         self.err_count.get() > 0
225     }
226     pub fn abort_if_errors(&self) {
227         let s;
228         match self.err_count.get() {
229           0 => return,
230           1 => s = "aborting due to previous error".to_string(),
231           _   => {
232             s = format!("aborting due to {} previous errors",
233                         self.err_count.get());
234           }
235         }
236         self.fatal(&s[..]);
237     }
238     pub fn warn(&self, msg: &str) {
239         self.emit.borrow_mut().emit(None, msg, None, Warning);
240     }
241     pub fn note(&self, msg: &str) {
242         self.emit.borrow_mut().emit(None, msg, None, Note);
243     }
244     pub fn help(&self, msg: &str) {
245         self.emit.borrow_mut().emit(None, msg, None, Help);
246     }
247     pub fn bug(&self, msg: &str) -> ! {
248         self.emit.borrow_mut().emit(None, msg, None, Bug);
249         panic!(ExplicitBug);
250     }
251     pub fn unimpl(&self, msg: &str) -> ! {
252         self.bug(&format!("unimplemented {}", msg));
253     }
254     pub fn emit(&self,
255                 cmsp: Option<(&codemap::CodeMap, Span)>,
256                 msg: &str,
257                 lvl: Level) {
258         if lvl == Warning && !self.can_emit_warnings { return }
259         self.emit.borrow_mut().emit(cmsp, msg, None, lvl);
260     }
261     pub fn emit_with_code(&self,
262                           cmsp: Option<(&codemap::CodeMap, Span)>,
263                           msg: &str,
264                           code: &str,
265                           lvl: Level) {
266         if lvl == Warning && !self.can_emit_warnings { return }
267         self.emit.borrow_mut().emit(cmsp, msg, Some(code), lvl);
268     }
269     pub fn custom_emit(&self, cm: &codemap::CodeMap,
270                        sp: RenderSpan, msg: &str, lvl: Level) {
271         if lvl == Warning && !self.can_emit_warnings { return }
272         self.emit.borrow_mut().custom_emit(cm, sp, msg, lvl);
273     }
274 }
275
276 #[derive(Copy, PartialEq, Clone, Debug)]
277 pub enum Level {
278     Bug,
279     Fatal,
280     Error,
281     Warning,
282     Note,
283     Help,
284 }
285
286 impl fmt::Display for Level {
287     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
288         use std::fmt::Display;
289
290         match *self {
291             Bug => "error: internal compiler error".fmt(f),
292             Fatal | Error => "error".fmt(f),
293             Warning => "warning".fmt(f),
294             Note => "note".fmt(f),
295             Help => "help".fmt(f),
296         }
297     }
298 }
299
300 impl Level {
301     fn color(self) -> term::color::Color {
302         match self {
303             Bug | Fatal | Error => term::color::BRIGHT_RED,
304             Warning => term::color::BRIGHT_YELLOW,
305             Note => term::color::BRIGHT_GREEN,
306             Help => term::color::BRIGHT_CYAN,
307         }
308     }
309 }
310
311 pub struct EmitterWriter {
312     dst: Destination,
313     registry: Option<diagnostics::registry::Registry>
314 }
315
316 enum Destination {
317     Terminal(Box<term::Terminal<WriterWrapper> + Send>),
318     Raw(Box<Write + Send>),
319 }
320
321 impl EmitterWriter {
322     pub fn stderr(color_config: ColorConfig,
323                   registry: Option<diagnostics::registry::Registry>) -> EmitterWriter {
324         let stderr = io::stderr();
325
326         let use_color = match color_config {
327             Always => true,
328             Never  => false,
329             Auto   => stderr_isatty(),
330         };
331
332         if use_color {
333             let dst = match term::stderr() {
334                 Some(t) => Terminal(t),
335                 None    => Raw(Box::new(stderr)),
336             };
337             EmitterWriter { dst: dst, registry: registry }
338         } else {
339             EmitterWriter { dst: Raw(Box::new(stderr)), registry: registry }
340         }
341     }
342
343     pub fn new(dst: Box<Write + Send>,
344                registry: Option<diagnostics::registry::Registry>) -> EmitterWriter {
345         EmitterWriter { dst: Raw(dst), registry: registry }
346     }
347
348     fn print_maybe_styled(&mut self,
349                           msg: &str,
350                           color: term::attr::Attr) -> io::Result<()> {
351         match self.dst {
352             Terminal(ref mut t) => {
353                 try!(t.attr(color));
354                 // If `msg` ends in a newline, we need to reset the color before
355                 // the newline. We're making the assumption that we end up writing
356                 // to a `LineBufferedWriter`, which means that emitting the reset
357                 // after the newline ends up buffering the reset until we print
358                 // another line or exit. Buffering the reset is a problem if we're
359                 // sharing the terminal with any other programs (e.g. other rustc
360                 // instances via `make -jN`).
361                 //
362                 // Note that if `msg` contains any internal newlines, this will
363                 // result in the `LineBufferedWriter` flushing twice instead of
364                 // once, which still leaves the opportunity for interleaved output
365                 // to be miscolored. We assume this is rare enough that we don't
366                 // have to worry about it.
367                 if msg.ends_with("\n") {
368                     try!(t.write_all(msg[..msg.len()-1].as_bytes()));
369                     try!(t.reset());
370                     try!(t.write_all(b"\n"));
371                 } else {
372                     try!(t.write_all(msg.as_bytes()));
373                     try!(t.reset());
374                 }
375                 Ok(())
376             }
377             Raw(ref mut w) => w.write_all(msg.as_bytes()),
378         }
379     }
380
381     fn print_diagnostic(&mut self, topic: &str, lvl: Level,
382                         msg: &str, code: Option<&str>) -> io::Result<()> {
383         if !topic.is_empty() {
384             try!(write!(&mut self.dst, "{} ", topic));
385         }
386
387         try!(self.print_maybe_styled(&format!("{}: ", lvl.to_string()),
388                                      term::attr::ForegroundColor(lvl.color())));
389         try!(self.print_maybe_styled(&format!("{}", msg),
390                                      term::attr::Bold));
391
392         match code {
393             Some(code) => {
394                 let style = term::attr::ForegroundColor(term::color::BRIGHT_MAGENTA);
395                 try!(self.print_maybe_styled(&format!(" [{}]", code.clone()), style));
396             }
397             None => ()
398         }
399         try!(write!(&mut self.dst, "\n"));
400         Ok(())
401     }
402
403     fn emit_(&mut self, cm: &codemap::CodeMap, rsp: RenderSpan,
404              msg: &str, code: Option<&str>, lvl: Level) -> io::Result<()> {
405         let sp = rsp.span();
406
407         // We cannot check equality directly with COMMAND_LINE_SP
408         // since PartialEq is manually implemented to ignore the ExpnId
409         let ss = if sp.expn_id == COMMAND_LINE_EXPN {
410             "<command line option>".to_string()
411         } else if let EndSpan(_) = rsp {
412             let span_end = Span { lo: sp.hi, hi: sp.hi, expn_id: sp.expn_id};
413             cm.span_to_string(span_end)
414         } else {
415             cm.span_to_string(sp)
416         };
417
418         try!(self.print_diagnostic(&ss[..], lvl, msg, code));
419
420         match rsp {
421             FullSpan(_) => {
422                 try!(self.highlight_lines(cm, sp, lvl, cm.span_to_lines(sp)));
423                 try!(self.print_macro_backtrace(cm, sp));
424             }
425             EndSpan(_) => {
426                 try!(self.end_highlight_lines(cm, sp, lvl, cm.span_to_lines(sp)));
427                 try!(self.print_macro_backtrace(cm, sp));
428             }
429             Suggestion(_, ref suggestion) => {
430                 try!(self.highlight_suggestion(cm, sp, suggestion));
431                 try!(self.print_macro_backtrace(cm, sp));
432             }
433             FileLine(..) => {
434                 // no source text in this case!
435             }
436         }
437
438         match code {
439             Some(code) =>
440                 match self.registry.as_ref().and_then(|registry| registry.find_description(code)) {
441                     Some(_) => {
442                         try!(self.print_diagnostic(&ss[..], Help,
443                                                    &format!("run `rustc --explain {}` to see a \
444                                                              detailed explanation", code), None));
445                     }
446                     None => ()
447                 },
448             None => (),
449         }
450         Ok(())
451     }
452
453     fn highlight_suggestion(&mut self,
454                             cm: &codemap::CodeMap,
455                             sp: Span,
456                             suggestion: &str)
457                             -> io::Result<()>
458     {
459         let lines = cm.span_to_lines(sp).unwrap();
460         assert!(!lines.lines.is_empty());
461
462         // To build up the result, we want to take the snippet from the first
463         // line that precedes the span, prepend that with the suggestion, and
464         // then append the snippet from the last line that trails the span.
465         let fm = &lines.file;
466
467         let first_line = &lines.lines[0];
468         let prefix = fm.get_line(first_line.line_index)
469                        .map(|l| &l[..first_line.start_col.0])
470                        .unwrap_or("");
471
472         let last_line = lines.lines.last().unwrap();
473         let suffix = fm.get_line(last_line.line_index)
474                        .map(|l| &l[last_line.end_col.0..])
475                        .unwrap_or("");
476
477         let complete = format!("{}{}{}", prefix, suggestion, suffix);
478
479         // print the suggestion without any line numbers, but leave
480         // space for them. This helps with lining up with previous
481         // snippets from the actual error being reported.
482         let fm = &*lines.file;
483         let mut lines = complete.lines();
484         for (line, line_index) in lines.by_ref().take(MAX_LINES).zip(first_line.line_index..) {
485             let elided_line_num = format!("{}", line_index+1);
486             try!(write!(&mut self.dst, "{0}:{1:2$} {3}\n",
487                         fm.name, "", elided_line_num.len(), line));
488         }
489
490         // if we elided some lines, add an ellipsis
491         if lines.next().is_some() {
492             let elided_line_num = format!("{}", first_line.line_index + MAX_LINES + 1);
493             try!(write!(&mut self.dst, "{0:1$} {0:2$} ...\n",
494                         "", fm.name.len(), elided_line_num.len()));
495         }
496
497         Ok(())
498     }
499
500     fn highlight_lines(&mut self,
501                        cm: &codemap::CodeMap,
502                        sp: Span,
503                        lvl: Level,
504                        lines: codemap::FileLinesResult)
505                        -> io::Result<()>
506     {
507         let lines = match lines {
508             Ok(lines) => lines,
509             Err(_) => {
510                 try!(write!(&mut self.dst, "(internal compiler error: unprintable span)\n"));
511                 return Ok(());
512             }
513         };
514
515         let fm = &*lines.file;
516
517         let line_strings: Option<Vec<&str>> =
518             lines.lines.iter()
519                        .map(|info| fm.get_line(info.line_index))
520                        .collect();
521
522         let line_strings = match line_strings {
523             None => { return Ok(()); }
524             Some(line_strings) => line_strings
525         };
526
527         // Display only the first MAX_LINES lines.
528         let all_lines = lines.lines.len();
529         let display_lines = cmp::min(all_lines, MAX_LINES);
530         let display_line_infos = &lines.lines[..display_lines];
531         let display_line_strings = &line_strings[..display_lines];
532
533         // Calculate the widest number to format evenly and fix #11715
534         assert!(display_line_infos.len() > 0);
535         let mut max_line_num = display_line_infos[display_line_infos.len() - 1].line_index + 1;
536         let mut digits = 0;
537         while max_line_num > 0 {
538             max_line_num /= 10;
539             digits += 1;
540         }
541
542         // Print the offending lines
543         for (line_info, line) in display_line_infos.iter().zip(display_line_strings) {
544             try!(write!(&mut self.dst, "{}:{:>width$} {}\n",
545                         fm.name,
546                         line_info.line_index + 1,
547                         line,
548                         width=digits));
549         }
550
551         // If we elided something, put an ellipsis.
552         if display_lines < all_lines {
553             let last_line_index = display_line_infos.last().unwrap().line_index;
554             let s = format!("{}:{} ", fm.name, last_line_index + 1);
555             try!(write!(&mut self.dst, "{0:1$}...\n", "", s.len()));
556         }
557
558         // FIXME (#3260)
559         // If there's one line at fault we can easily point to the problem
560         if lines.lines.len() == 1 {
561             let lo = cm.lookup_char_pos(sp.lo);
562             let mut digits = 0;
563             let mut num = (lines.lines[0].line_index + 1) / 10;
564
565             // how many digits must be indent past?
566             while num > 0 { num /= 10; digits += 1; }
567
568             let mut s = String::new();
569             // Skip is the number of characters we need to skip because they are
570             // part of the 'filename:line ' part of the previous line.
571             let skip = fm.name.chars().count() + digits + 3;
572             for _ in 0..skip {
573                 s.push(' ');
574             }
575             if let Some(orig) = fm.get_line(lines.lines[0].line_index) {
576                 let mut col = skip;
577                 let mut lastc = ' ';
578                 let mut iter = orig.chars().enumerate();
579                 for (pos, ch) in iter.by_ref() {
580                     lastc = ch;
581                     if pos >= lo.col.to_usize() { break; }
582                     // Whenever a tab occurs on the previous line, we insert one on
583                     // the error-point-squiggly-line as well (instead of a space).
584                     // That way the squiggly line will usually appear in the correct
585                     // position.
586                     match ch {
587                         '\t' => {
588                             col += 8 - col%8;
589                             s.push('\t');
590                         },
591                         _ => {
592                             col += 1;
593                             s.push(' ');
594                         },
595                     }
596                 }
597
598                 try!(write!(&mut self.dst, "{}", s));
599                 let mut s = String::from("^");
600                 let count = match lastc {
601                     // Most terminals have a tab stop every eight columns by default
602                     '\t' => 8 - col%8,
603                     _ => 1,
604                 };
605                 col += count;
606                 s.extend(::std::iter::repeat('~').take(count));
607
608                 let hi = cm.lookup_char_pos(sp.hi);
609                 if hi.col != lo.col {
610                     for (pos, ch) in iter {
611                         if pos >= hi.col.to_usize() { break; }
612                         let count = match ch {
613                             '\t' => 8 - col%8,
614                             _ => 1,
615                         };
616                         col += count;
617                         s.extend(::std::iter::repeat('~').take(count));
618                     }
619                 }
620
621                 if s.len() > 1 {
622                     // One extra squiggly is replaced by a "^"
623                     s.pop();
624                 }
625
626                 try!(self.print_maybe_styled(&format!("{}\n", s),
627                                              term::attr::ForegroundColor(lvl.color())));
628             }
629         }
630         Ok(())
631     }
632
633     /// Here are the differences between this and the normal `highlight_lines`:
634     /// `end_highlight_lines` will always put arrow on the last byte of the
635     /// span (instead of the first byte). Also, when the span is too long (more
636     /// than 6 lines), `end_highlight_lines` will print the first line, then
637     /// dot dot dot, then last line, whereas `highlight_lines` prints the first
638     /// six lines.
639     #[allow(deprecated)]
640     fn end_highlight_lines(&mut self,
641                            cm: &codemap::CodeMap,
642                            sp: Span,
643                            lvl: Level,
644                            lines: codemap::FileLinesResult)
645                           -> io::Result<()> {
646         let lines = match lines {
647             Ok(lines) => lines,
648             Err(_) => {
649                 try!(write!(&mut self.dst, "(internal compiler error: unprintable span)\n"));
650                 return Ok(());
651             }
652         };
653
654         let fm = &*lines.file;
655
656         let lines = &lines.lines[..];
657         if lines.len() > MAX_LINES {
658             if let Some(line) = fm.get_line(lines[0].line_index) {
659                 try!(write!(&mut self.dst, "{}:{} {}\n", fm.name,
660                             lines[0].line_index + 1, line));
661             }
662             try!(write!(&mut self.dst, "...\n"));
663             let last_line_index = lines[lines.len() - 1].line_index;
664             if let Some(last_line) = fm.get_line(last_line_index) {
665                 try!(write!(&mut self.dst, "{}:{} {}\n", fm.name,
666                             last_line_index + 1, last_line));
667             }
668         } else {
669             for line_info in lines {
670                 if let Some(line) = fm.get_line(line_info.line_index) {
671                     try!(write!(&mut self.dst, "{}:{} {}\n", fm.name,
672                                 line_info.line_index + 1, line));
673                 }
674             }
675         }
676         let last_line_start = format!("{}:{} ", fm.name, lines[lines.len()-1].line_index + 1);
677         let hi = cm.lookup_char_pos(sp.hi);
678         let skip = last_line_start.chars().count();
679         let mut s = String::new();
680         for _ in 0..skip {
681             s.push(' ');
682         }
683         if let Some(orig) = fm.get_line(lines[0].line_index) {
684             let iter = orig.chars().enumerate();
685             for (pos, ch) in iter {
686                 // Span seems to use half-opened interval, so subtract 1
687                 if pos >= hi.col.to_usize() - 1 { break; }
688                 // Whenever a tab occurs on the previous line, we insert one on
689                 // the error-point-squiggly-line as well (instead of a space).
690                 // That way the squiggly line will usually appear in the correct
691                 // position.
692                 match ch {
693                     '\t' => s.push('\t'),
694                     _ => s.push(' '),
695                 }
696             }
697         }
698         s.push('^');
699         s.push('\n');
700         self.print_maybe_styled(&s[..],
701                                 term::attr::ForegroundColor(lvl.color()))
702     }
703
704     fn print_macro_backtrace(&mut self,
705                              cm: &codemap::CodeMap,
706                              sp: Span)
707                              -> io::Result<()> {
708         let cs = try!(cm.with_expn_info(sp.expn_id, |expn_info| -> io::Result<_> {
709             match expn_info {
710                 Some(ei) => {
711                     let ss = ei.callee.span.map_or(String::new(),
712                                                    |span| cm.span_to_string(span));
713                     let (pre, post) = match ei.callee.format {
714                         codemap::MacroAttribute => ("#[", "]"),
715                         codemap::MacroBang => ("", "!"),
716                         codemap::CompilerExpansion => ("", ""),
717                     };
718                     try!(self.print_diagnostic(&ss, Note,
719                                                &format!("in expansion of {}{}{}",
720                                                         pre,
721                                                         ei.callee.name,
722                                                         post),
723                                                None));
724                     let ss = cm.span_to_string(ei.call_site);
725                     try!(self.print_diagnostic(&ss, Note, "expansion site", None));
726                     Ok(Some(ei.call_site))
727                 }
728                 None => Ok(None)
729         }
730         }));
731         cs.map_or(Ok(()), |call_site| self.print_macro_backtrace(cm, call_site))
732     }
733 }
734
735 #[cfg(unix)]
736 fn stderr_isatty() -> bool {
737     unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
738 }
739 #[cfg(windows)]
740 fn stderr_isatty() -> bool {
741     const STD_ERROR_HANDLE: libc::DWORD = -12i32 as libc::DWORD;
742     extern "system" {
743         fn GetStdHandle(which: libc::DWORD) -> libc::HANDLE;
744         fn GetConsoleMode(hConsoleHandle: libc::HANDLE,
745                           lpMode: libc::LPDWORD) -> libc::BOOL;
746     }
747     unsafe {
748         let handle = GetStdHandle(STD_ERROR_HANDLE);
749         let mut out = 0;
750         GetConsoleMode(handle, &mut out) != 0
751     }
752 }
753
754 impl Write for Destination {
755     fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
756         match *self {
757             Terminal(ref mut t) => t.write(bytes),
758             Raw(ref mut w) => w.write(bytes),
759         }
760     }
761     fn flush(&mut self) -> io::Result<()> {
762         match *self {
763             Terminal(ref mut t) => t.flush(),
764             Raw(ref mut w) => w.flush(),
765         }
766     }
767 }
768
769 impl Emitter for EmitterWriter {
770     fn emit(&mut self,
771             cmsp: Option<(&codemap::CodeMap, Span)>,
772             msg: &str, code: Option<&str>, lvl: Level) {
773         let error = match cmsp {
774             Some((cm, COMMAND_LINE_SP)) => self.emit_(cm,
775                                                 FileLine(COMMAND_LINE_SP),
776                                                 msg, code, lvl),
777             Some((cm, sp)) => self.emit_(cm, FullSpan(sp), msg, code, lvl),
778             None => self.print_diagnostic("", lvl, msg, code),
779         };
780
781         match error {
782             Ok(()) => {}
783             Err(e) => panic!("failed to print diagnostics: {:?}", e),
784         }
785     }
786
787     fn custom_emit(&mut self, cm: &codemap::CodeMap,
788                    sp: RenderSpan, msg: &str, lvl: Level) {
789         match self.emit_(cm, sp, msg, None, lvl) {
790             Ok(()) => {}
791             Err(e) => panic!("failed to print diagnostics: {:?}", e),
792         }
793     }
794 }
795
796 pub fn expect<T, M>(diag: &SpanHandler, opt: Option<T>, msg: M) -> T where
797     M: FnOnce() -> String,
798 {
799     match opt {
800         Some(t) => t,
801         None => diag.handler().bug(&msg()),
802     }
803 }
804
805 #[cfg(test)]
806 mod test {
807     use super::{EmitterWriter, Level};
808     use codemap::{mk_sp, CodeMap, BytePos};
809     use std::sync::{Arc, Mutex};
810     use std::io::{self, Write};
811     use std::str::from_utf8;
812
813     // Diagnostic doesn't align properly in span where line number increases by one digit
814     #[test]
815     fn test_hilight_suggestion_issue_11715() {
816         struct Sink(Arc<Mutex<Vec<u8>>>);
817         impl Write for Sink {
818             fn write(&mut self, data: &[u8]) -> io::Result<usize> {
819                 Write::write(&mut *self.0.lock().unwrap(), data)
820             }
821             fn flush(&mut self) -> io::Result<()> { Ok(()) }
822         }
823         let data = Arc::new(Mutex::new(Vec::new()));
824         let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())), None);
825         let cm = CodeMap::new();
826         let content = "abcdefg
827         koksi
828         line3
829         line4
830         cinq
831         line6
832         line7
833         line8
834         line9
835         line10
836         e-lä-vän
837         tolv
838         dreizehn
839         ";
840         let file = cm.new_filemap_and_lines("dummy.txt", content);
841         let start = file.lines.borrow()[7];
842         let end = file.lines.borrow()[11];
843         let sp = mk_sp(start, end);
844         let lvl = Level::Error;
845         println!("span_to_lines");
846         let lines = cm.span_to_lines(sp);
847         println!("highlight_lines");
848         ew.highlight_lines(&cm, sp, lvl, lines).unwrap();
849         println!("done");
850         let vec = data.lock().unwrap().clone();
851         let vec: &[u8] = &vec;
852         let str = from_utf8(vec).unwrap();
853         println!("{}", str);
854         assert_eq!(str, "dummy.txt: 8         line8\n\
855                          dummy.txt: 9         line9\n\
856                          dummy.txt:10         line10\n\
857                          dummy.txt:11         e-lä-vän\n\
858                          dummy.txt:12         tolv\n");
859     }
860 }