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.
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.
11 pub use self::Level
::*;
12 pub use self::RenderSpan
::*;
13 pub use self::ColorConfig
::*;
14 use self::Destination
::*;
16 use codemap
::{self, COMMAND_LINE_SP, COMMAND_LINE_EXPN, Pos, Span}
;
19 use std
::cell
::{RefCell, Cell}
;
20 use std
::{cmp, error, fmt}
;
21 use std
::io
::prelude
::*;
23 use term
::{self, WriterWrapper}
;
26 /// maximum number of lines we will print for each error; arbitrary.
27 const MAX_LINES
: usize = 6;
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.
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
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
),
49 /// A FileLine renders with just a line for the message prefixed
55 fn span(&self) -> Span
{
66 #[derive(Clone, Copy)]
67 pub enum ColorConfig
{
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
);
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
83 #[derive(Copy, Clone, Debug)]
85 pub struct FatalError
;
87 impl fmt
::Display
for FatalError
{
88 fn fmt(&self, f
: &mut fmt
::Formatter
) -> Result
<(), fmt
::Error
> {
89 write
!(f
, "parser fatal error")
93 impl error
::Error
for FatalError
{
94 fn description(&self) -> &str {
95 "The parser has encountered a fatal error"
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
;
104 impl fmt
::Display
for ExplicitBug
{
105 fn fmt(&self, f
: &mut fmt
::Formatter
) -> Result
<(), fmt
::Error
> {
106 write
!(f
, "parser internal bug")
110 impl error
::Error
for ExplicitBug
{
111 fn description(&self) -> &str {
112 "The parser has encountered an internal bug"
116 /// A span-handler is like a handler but also
117 /// accepts span information for source-location
119 pub struct SpanHandler
{
120 pub handler
: Handler
,
121 pub cm
: codemap
::CodeMap
,
125 pub fn new(handler
: Handler
, cm
: codemap
::CodeMap
) -> SpanHandler
{
131 pub fn span_fatal(&self, sp
: Span
, msg
: &str) -> FatalError
{
132 self.handler
.emit(Some((&self.cm
, sp
)), msg
, Fatal
);
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
);
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();
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();
147 pub fn span_warn(&self, sp
: Span
, msg
: &str) {
148 self.handler
.emit(Some((&self.cm
, sp
)), msg
, Warning
);
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
);
153 pub fn span_note(&self, sp
: Span
, msg
: &str) {
154 self.handler
.emit(Some((&self.cm
, sp
)), msg
, Note
);
156 pub fn span_end_note(&self, sp
: Span
, msg
: &str) {
157 self.handler
.custom_emit(&self.cm
, EndSpan(sp
), msg
, Note
);
159 pub fn span_help(&self, sp
: Span
, msg
: &str) {
160 self.handler
.emit(Some((&self.cm
, sp
)), msg
, Help
);
162 /// Prints out a message with a suggested edit of the code.
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
);
168 pub fn fileline_note(&self, sp
: Span
, msg
: &str) {
169 self.handler
.custom_emit(&self.cm
, FileLine(sp
), msg
, Note
);
171 pub fn fileline_help(&self, sp
: Span
, msg
: &str) {
172 self.handler
.custom_emit(&self.cm
, FileLine(sp
), msg
, Help
);
174 pub fn span_bug(&self, sp
: Span
, msg
: &str) -> ! {
175 self.handler
.emit(Some((&self.cm
, sp
)), msg
, Bug
);
178 pub fn span_unimpl(&self, sp
: Span
, msg
: &str) -> ! {
179 self.span_bug(sp
, &format
!("unimplemented {}", msg
));
181 pub fn handler
<'a
>(&'a
self) -> &'a Handler
{
186 /// A handler deals with errors; certain errors
187 /// (fatal, bug, unimpl) may cause immediate exit,
188 /// others log errors for later reporting.
190 err_count
: Cell
<usize>,
191 emit
: RefCell
<Box
<Emitter
+ Send
>>,
192 pub can_emit_warnings
: bool
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
)
202 pub fn with_emitter(can_emit_warnings
: bool
, e
: Box
<Emitter
+ Send
>) -> Handler
{
204 err_count
: Cell
::new(0),
205 emit
: RefCell
::new(e
),
206 can_emit_warnings
: can_emit_warnings
209 pub fn fatal(&self, msg
: &str) -> ! {
210 self.emit
.borrow_mut().emit(None
, msg
, None
, Fatal
);
212 // Suppress the fatal error message from the panic below as we've
213 // already terminated in our own "legitimate" fashion.
214 io
::set_panic(Box
::new(io
::sink()));
217 pub fn err(&self, msg
: &str) {
218 self.emit
.borrow_mut().emit(None
, msg
, None
, Error
);
219 self.bump_err_count();
221 pub fn bump_err_count(&self) {
222 self.err_count
.set(self.err_count
.get() + 1);
224 pub fn err_count(&self) -> usize {
227 pub fn has_errors(&self) -> bool
{
228 self.err_count
.get() > 0
230 pub fn abort_if_errors(&self) {
232 match self.err_count
.get() {
234 1 => s
= "aborting due to previous error".to_string(),
236 s
= format
!("aborting due to {} previous errors",
237 self.err_count
.get());
242 pub fn warn(&self, msg
: &str) {
243 self.emit
.borrow_mut().emit(None
, msg
, None
, Warning
);
245 pub fn note(&self, msg
: &str) {
246 self.emit
.borrow_mut().emit(None
, msg
, None
, Note
);
248 pub fn help(&self, msg
: &str) {
249 self.emit
.borrow_mut().emit(None
, msg
, None
, Help
);
251 pub fn bug(&self, msg
: &str) -> ! {
252 self.emit
.borrow_mut().emit(None
, msg
, None
, Bug
);
255 pub fn unimpl(&self, msg
: &str) -> ! {
256 self.bug(&format
!("unimplemented {}", msg
));
259 cmsp
: Option
<(&codemap
::CodeMap
, Span
)>,
262 if lvl
== Warning
&& !self.can_emit_warnings { return }
263 self.emit
.borrow_mut().emit(cmsp
, msg
, None
, lvl
);
265 pub fn emit_with_code(&self,
266 cmsp
: Option
<(&codemap
::CodeMap
, Span
)>,
270 if lvl
== Warning
&& !self.can_emit_warnings { return }
271 self.emit
.borrow_mut().emit(cmsp
, msg
, Some(code
), lvl
);
273 pub fn custom_emit(&self, cm
: &codemap
::CodeMap
,
274 sp
: RenderSpan
, msg
: &str, lvl
: Level
) {
275 if lvl
== Warning
&& !self.can_emit_warnings { return }
276 self.emit
.borrow_mut().custom_emit(cm
, sp
, msg
, lvl
);
280 #[derive(Copy, PartialEq, Clone, Debug)]
290 impl fmt
::Display
for Level
{
291 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
292 use std
::fmt
::Display
;
295 Bug
=> "error: internal compiler error".fmt(f
),
296 Fatal
| Error
=> "error".fmt(f
),
297 Warning
=> "warning".fmt(f
),
298 Note
=> "note".fmt(f
),
299 Help
=> "help".fmt(f
),
305 fn color(self) -> term
::color
::Color
{
307 Bug
| Fatal
| Error
=> term
::color
::BRIGHT_RED
,
308 Warning
=> term
::color
::BRIGHT_YELLOW
,
309 Note
=> term
::color
::BRIGHT_GREEN
,
310 Help
=> term
::color
::BRIGHT_CYAN
,
315 pub struct EmitterWriter
{
317 registry
: Option
<diagnostics
::registry
::Registry
>
321 Terminal(Box
<term
::Terminal
<WriterWrapper
> + Send
>),
322 Raw(Box
<Write
+ Send
>),
325 /// Do not use this for messages that end in `\n` – use `println_maybe_styled` instead. See
326 /// `EmitterWriter::print_maybe_styled` for details.
327 macro_rules
! print_maybe_styled
{
328 ($writer
: expr
, $style
: expr
, $
($arg
: tt
)*) => {
329 $writer
.print_maybe_styled(format_args
!($
($arg
)*), $style
, false)
333 macro_rules
! println_maybe_styled
{
334 ($writer
: expr
, $style
: expr
, $
($arg
: tt
)*) => {
335 $writer
.print_maybe_styled(format_args
!($
($arg
)*), $style
, true)
340 pub fn stderr(color_config
: ColorConfig
,
341 registry
: Option
<diagnostics
::registry
::Registry
>) -> EmitterWriter
{
342 let stderr
= io
::stderr();
344 let use_color
= match color_config
{
347 Auto
=> stderr_isatty(),
351 let dst
= match term
::stderr() {
352 Some(t
) => Terminal(t
),
353 None
=> Raw(Box
::new(stderr
)),
355 EmitterWriter { dst: dst, registry: registry }
357 EmitterWriter { dst: Raw(Box::new(stderr)), registry: registry }
361 pub fn new(dst
: Box
<Write
+ Send
>,
362 registry
: Option
<diagnostics
::registry
::Registry
>) -> EmitterWriter
{
363 EmitterWriter { dst: Raw(dst), registry: registry }
366 fn print_maybe_styled(&mut self,
367 args
: fmt
::Arguments
,
368 color
: term
::attr
::Attr
,
369 print_newline_at_end
: bool
) -> io
::Result
<()> {
371 Terminal(ref mut t
) => {
373 // If `msg` ends in a newline, we need to reset the color before
374 // the newline. We're making the assumption that we end up writing
375 // to a `LineBufferedWriter`, which means that emitting the reset
376 // after the newline ends up buffering the reset until we print
377 // another line or exit. Buffering the reset is a problem if we're
378 // sharing the terminal with any other programs (e.g. other rustc
379 // instances via `make -jN`).
381 // Note that if `msg` contains any internal newlines, this will
382 // result in the `LineBufferedWriter` flushing twice instead of
383 // once, which still leaves the opportunity for interleaved output
384 // to be miscolored. We assume this is rare enough that we don't
385 // have to worry about it.
386 try
!(t
.write_fmt(args
));
388 if print_newline_at_end
{
395 try
!(w
.write_fmt(args
));
396 if print_newline_at_end
{
405 fn print_diagnostic(&mut self, topic
: &str, lvl
: Level
,
406 msg
: &str, code
: Option
<&str>) -> io
::Result
<()> {
407 if !topic
.is_empty() {
408 try
!(write
!(&mut self.dst
, "{} ", topic
));
411 try
!(print_maybe_styled
!(self, term
::attr
::ForegroundColor(lvl
.color()),
412 "{}: ", lvl
.to_string()));
413 try
!(print_maybe_styled
!(self, term
::attr
::Bold
, "{}", msg
));
417 let style
= term
::attr
::ForegroundColor(term
::color
::BRIGHT_MAGENTA
);
418 try
!(print_maybe_styled
!(self, style
, " [{}]", code
.clone()));
422 try
!(write
!(&mut self.dst
, "\n"));
426 fn emit_(&mut self, cm
: &codemap
::CodeMap
, rsp
: RenderSpan
,
427 msg
: &str, code
: Option
<&str>, lvl
: Level
) -> io
::Result
<()> {
430 // We cannot check equality directly with COMMAND_LINE_SP
431 // since PartialEq is manually implemented to ignore the ExpnId
432 let ss
= if sp
.expn_id
== COMMAND_LINE_EXPN
{
433 "<command line option>".to_string()
434 } else if let EndSpan(_
) = rsp
{
435 let span_end
= Span { lo: sp.hi, hi: sp.hi, expn_id: sp.expn_id}
;
436 cm
.span_to_string(span_end
)
438 cm
.span_to_string(sp
)
441 try
!(self.print_diagnostic(&ss
[..], lvl
, msg
, code
));
445 try
!(self.highlight_lines(cm
, sp
, lvl
, cm
.span_to_lines(sp
)));
446 try
!(self.print_macro_backtrace(cm
, sp
));
449 try
!(self.end_highlight_lines(cm
, sp
, lvl
, cm
.span_to_lines(sp
)));
450 try
!(self.print_macro_backtrace(cm
, sp
));
452 Suggestion(_
, ref suggestion
) => {
453 try
!(self.highlight_suggestion(cm
, sp
, suggestion
));
454 try
!(self.print_macro_backtrace(cm
, sp
));
457 // no source text in this case!
463 match self.registry
.as_ref().and_then(|registry
| registry
.find_description(code
)) {
465 try
!(self.print_diagnostic(&ss
[..], Help
,
466 &format
!("run `rustc --explain {}` to see a \
467 detailed explanation", code
), None
));
476 fn highlight_suggestion(&mut self,
477 cm
: &codemap
::CodeMap
,
482 let lines
= cm
.span_to_lines(sp
).unwrap();
483 assert
!(!lines
.lines
.is_empty());
485 // To build up the result, we want to take the snippet from the first
486 // line that precedes the span, prepend that with the suggestion, and
487 // then append the snippet from the last line that trails the span.
488 let fm
= &lines
.file
;
490 let first_line
= &lines
.lines
[0];
491 let prefix
= fm
.get_line(first_line
.line_index
)
492 .map(|l
| &l
[..first_line
.start_col
.0])
495 let last_line
= lines
.lines
.last().unwrap();
496 let suffix
= fm
.get_line(last_line
.line_index
)
497 .map(|l
| &l
[last_line
.end_col
.0..])
500 let complete
= format
!("{}{}{}", prefix
, suggestion
, suffix
);
502 // print the suggestion without any line numbers, but leave
503 // space for them. This helps with lining up with previous
504 // snippets from the actual error being reported.
505 let fm
= &*lines
.file
;
506 let mut lines
= complete
.lines();
507 for (line
, line_index
) in lines
.by_ref().take(MAX_LINES
).zip(first_line
.line_index
..) {
508 let elided_line_num
= format
!("{}", line_index
+1);
509 try
!(write
!(&mut self.dst
, "{0}:{1:2$} {3}\n",
510 fm
.name
, "", elided_line_num
.len(), line
));
513 // if we elided some lines, add an ellipsis
514 if lines
.next().is_some() {
515 let elided_line_num
= format
!("{}", first_line
.line_index
+ MAX_LINES
+ 1);
516 try
!(write
!(&mut self.dst
, "{0:1$} {0:2$} ...\n",
517 "", fm
.name
.len(), elided_line_num
.len()));
523 fn highlight_lines(&mut self,
524 cm
: &codemap
::CodeMap
,
527 lines
: codemap
::FileLinesResult
)
530 let lines
= match lines
{
533 try
!(write
!(&mut self.dst
, "(internal compiler error: unprintable span)\n"));
538 let fm
= &*lines
.file
;
540 let line_strings
: Option
<Vec
<&str>> =
542 .map(|info
| fm
.get_line(info
.line_index
))
545 let line_strings
= match line_strings
{
546 None
=> { return Ok(()); }
547 Some(line_strings
) => line_strings
550 // Display only the first MAX_LINES lines.
551 let all_lines
= lines
.lines
.len();
552 let display_lines
= cmp
::min(all_lines
, MAX_LINES
);
553 let display_line_infos
= &lines
.lines
[..display_lines
];
554 let display_line_strings
= &line_strings
[..display_lines
];
556 // Calculate the widest number to format evenly and fix #11715
557 assert
!(display_line_infos
.len() > 0);
558 let mut max_line_num
= display_line_infos
[display_line_infos
.len() - 1].line_index
+ 1;
560 while max_line_num
> 0 {
565 // Print the offending lines
566 for (line_info
, line
) in display_line_infos
.iter().zip(display_line_strings
) {
567 try
!(write
!(&mut self.dst
, "{}:{:>width$} {}\n",
569 line_info
.line_index
+ 1,
574 // If we elided something, put an ellipsis.
575 if display_lines
< all_lines
{
576 let last_line_index
= display_line_infos
.last().unwrap().line_index
;
577 let s
= format
!("{}:{} ", fm
.name
, last_line_index
+ 1);
578 try
!(write
!(&mut self.dst
, "{0:1$}...\n", "", s
.len()));
582 // If there's one line at fault we can easily point to the problem
583 if lines
.lines
.len() == 1 {
584 let lo
= cm
.lookup_char_pos(sp
.lo
);
586 let mut num
= (lines
.lines
[0].line_index
+ 1) / 10;
588 // how many digits must be indent past?
589 while num
> 0 { num /= 10; digits += 1; }
591 let mut s
= String
::new();
592 // Skip is the number of characters we need to skip because they are
593 // part of the 'filename:line ' part of the previous line.
594 let skip
= fm
.name
.chars().count() + digits
+ 3;
598 if let Some(orig
) = fm
.get_line(lines
.lines
[0].line_index
) {
601 let mut iter
= orig
.chars().enumerate();
602 for (pos
, ch
) in iter
.by_ref() {
604 if pos
>= lo
.col
.to_usize() { break; }
605 // Whenever a tab occurs on the previous line, we insert one on
606 // the error-point-squiggly-line as well (instead of a space).
607 // That way the squiggly line will usually appear in the correct
621 try
!(write
!(&mut self.dst
, "{}", s
));
622 let mut s
= String
::from("^");
623 let count
= match lastc
{
624 // Most terminals have a tab stop every eight columns by default
629 s
.extend(::std
::iter
::repeat('
~'
).take(count
));
631 let hi
= cm
.lookup_char_pos(sp
.hi
);
632 if hi
.col
!= lo
.col
{
633 for (pos
, ch
) in iter
{
634 if pos
>= hi
.col
.to_usize() { break; }
635 let count
= match ch
{
640 s
.extend(::std
::iter
::repeat('
~'
).take(count
));
645 // One extra squiggly is replaced by a "^"
649 try
!(println_maybe_styled
!(self, term
::attr
::ForegroundColor(lvl
.color()),
656 /// Here are the differences between this and the normal `highlight_lines`:
657 /// `end_highlight_lines` will always put arrow on the last byte of the
658 /// span (instead of the first byte). Also, when the span is too long (more
659 /// than 6 lines), `end_highlight_lines` will print the first line, then
660 /// dot dot dot, then last line, whereas `highlight_lines` prints the first
663 fn end_highlight_lines(&mut self,
664 cm
: &codemap
::CodeMap
,
667 lines
: codemap
::FileLinesResult
)
669 let lines
= match lines
{
672 try
!(write
!(&mut self.dst
, "(internal compiler error: unprintable span)\n"));
677 let fm
= &*lines
.file
;
679 let lines
= &lines
.lines
[..];
680 if lines
.len() > MAX_LINES
{
681 if let Some(line
) = fm
.get_line(lines
[0].line_index
) {
682 try
!(write
!(&mut self.dst
, "{}:{} {}\n", fm
.name
,
683 lines
[0].line_index
+ 1, line
));
685 try
!(write
!(&mut self.dst
, "...\n"));
686 let last_line_index
= lines
[lines
.len() - 1].line_index
;
687 if let Some(last_line
) = fm
.get_line(last_line_index
) {
688 try
!(write
!(&mut self.dst
, "{}:{} {}\n", fm
.name
,
689 last_line_index
+ 1, last_line
));
692 for line_info
in lines
{
693 if let Some(line
) = fm
.get_line(line_info
.line_index
) {
694 try
!(write
!(&mut self.dst
, "{}:{} {}\n", fm
.name
,
695 line_info
.line_index
+ 1, line
));
699 let last_line_start
= format
!("{}:{} ", fm
.name
, lines
[lines
.len()-1].line_index
+ 1);
700 let hi
= cm
.lookup_char_pos(sp
.hi
);
701 let skip
= last_line_start
.chars().count();
702 let mut s
= String
::new();
706 if let Some(orig
) = fm
.get_line(lines
[0].line_index
) {
707 let iter
= orig
.chars().enumerate();
708 for (pos
, ch
) in iter
{
709 // Span seems to use half-opened interval, so subtract 1
710 if pos
>= hi
.col
.to_usize() - 1 { break; }
711 // Whenever a tab occurs on the previous line, we insert one on
712 // the error-point-squiggly-line as well (instead of a space).
713 // That way the squiggly line will usually appear in the correct
716 '
\t'
=> s
.push('
\t'
),
722 println_maybe_styled
!(self, term
::attr
::ForegroundColor(lvl
.color()),
726 fn print_macro_backtrace(&mut self,
727 cm
: &codemap
::CodeMap
,
730 let cs
= try
!(cm
.with_expn_info(sp
.expn_id
, |expn_info
| -> io
::Result
<_
> {
733 let ss
= ei
.callee
.span
.map_or(String
::new(),
734 |span
| cm
.span_to_string(span
));
735 let (pre
, post
) = match ei
.callee
.format
{
736 codemap
::MacroAttribute
=> ("#[", "]"),
737 codemap
::MacroBang
=> ("", "!"),
738 codemap
::CompilerExpansion
=> ("", ""),
740 try
!(self.print_diagnostic(&ss
, Note
,
741 &format
!("in expansion of {}{}{}",
746 let ss
= cm
.span_to_string(ei
.call_site
);
747 try
!(self.print_diagnostic(&ss
, Note
, "expansion site", None
));
748 Ok(Some(ei
.call_site
))
753 cs
.map_or(Ok(()), |call_site
| self.print_macro_backtrace(cm
, call_site
))
758 fn stderr_isatty() -> bool
{
759 unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
762 fn stderr_isatty() -> bool
{
763 const STD_ERROR_HANDLE
: libc
::DWORD
= -12i32 as libc
::DWORD
;
765 fn GetStdHandle(which
: libc
::DWORD
) -> libc
::HANDLE
;
766 fn GetConsoleMode(hConsoleHandle
: libc
::HANDLE
,
767 lpMode
: libc
::LPDWORD
) -> libc
::BOOL
;
770 let handle
= GetStdHandle(STD_ERROR_HANDLE
);
772 GetConsoleMode(handle
, &mut out
) != 0
776 impl Write
for Destination
{
777 fn write(&mut self, bytes
: &[u8]) -> io
::Result
<usize> {
779 Terminal(ref mut t
) => t
.write(bytes
),
780 Raw(ref mut w
) => w
.write(bytes
),
783 fn flush(&mut self) -> io
::Result
<()> {
785 Terminal(ref mut t
) => t
.flush(),
786 Raw(ref mut w
) => w
.flush(),
791 impl Emitter
for EmitterWriter
{
793 cmsp
: Option
<(&codemap
::CodeMap
, Span
)>,
794 msg
: &str, code
: Option
<&str>, lvl
: Level
) {
795 let error
= match cmsp
{
796 Some((cm
, COMMAND_LINE_SP
)) => self.emit_(cm
,
797 FileLine(COMMAND_LINE_SP
),
799 Some((cm
, sp
)) => self.emit_(cm
, FullSpan(sp
), msg
, code
, lvl
),
800 None
=> self.print_diagnostic("", lvl
, msg
, code
),
805 Err(e
) => panic
!("failed to print diagnostics: {:?}", e
),
809 fn custom_emit(&mut self, cm
: &codemap
::CodeMap
,
810 sp
: RenderSpan
, msg
: &str, lvl
: Level
) {
811 match self.emit_(cm
, sp
, msg
, None
, lvl
) {
813 Err(e
) => panic
!("failed to print diagnostics: {:?}", e
),
818 pub fn expect
<T
, M
>(diag
: &SpanHandler
, opt
: Option
<T
>, msg
: M
) -> T
where
819 M
: FnOnce() -> String
,
823 None
=> diag
.handler().bug(&msg()),
829 use super::{EmitterWriter, Level}
;
830 use codemap
::{mk_sp, CodeMap, BytePos}
;
831 use std
::sync
::{Arc, Mutex}
;
832 use std
::io
::{self, Write}
;
833 use std
::str::from_utf8
;
835 // Diagnostic doesn't align properly in span where line number increases by one digit
837 fn test_hilight_suggestion_issue_11715() {
838 struct Sink(Arc
<Mutex
<Vec
<u8>>>);
839 impl Write
for Sink
{
840 fn write(&mut self, data
: &[u8]) -> io
::Result
<usize> {
841 Write
::write(&mut *self.0.lock().unwrap(), data
)
843 fn flush(&mut self) -> io
::Result
<()> { Ok(()) }
845 let data
= Arc
::new(Mutex
::new(Vec
::new()));
846 let mut ew
= EmitterWriter
::new(Box
::new(Sink(data
.clone())), None
);
847 let cm
= CodeMap
::new();
848 let content
= "abcdefg
862 let file
= cm
.new_filemap_and_lines("dummy.txt", content
);
863 let start
= file
.lines
.borrow()[7];
864 let end
= file
.lines
.borrow()[11];
865 let sp
= mk_sp(start
, end
);
866 let lvl
= Level
::Error
;
867 println
!("span_to_lines");
868 let lines
= cm
.span_to_lines(sp
);
869 println
!("highlight_lines");
870 ew
.highlight_lines(&cm
, sp
, lvl
, lines
).unwrap();
872 let vec
= data
.lock().unwrap().clone();
873 let vec
: &[u8] = &vec
;
874 let str = from_utf8(vec
).unwrap();
876 assert_eq
!(str, "dummy.txt: 8 line8\n\
877 dummy.txt: 9 line9\n\
878 dummy.txt:10 line10\n\
879 dummy.txt:11 e-lä-vän\n\
880 dummy.txt:12 tolv\n");