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.
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 use self::Destination
::*;
13 use syntax_pos
::{COMMAND_LINE_SP, DUMMY_SP, FileMap, Span, MultiSpan, CharPos}
;
15 use {Level, CodeSuggestion, DiagnosticBuilder, SubDiagnostic, CodeMapper}
;
17 use snippet
::{StyledString, Style, Annotation, Line}
;
18 use styled_buffer
::StyledBuffer
;
20 use std
::io
::prelude
::*;
25 /// Emitter trait for emitting errors.
27 /// Emit a structured diagnostic.
28 fn emit(&mut self, db
: &DiagnosticBuilder
);
31 impl Emitter
for EmitterWriter
{
32 fn emit(&mut self, db
: &DiagnosticBuilder
) {
33 let mut primary_span
= db
.span
.clone();
34 let mut children
= db
.children
.clone();
35 self.fix_multispans_in_std_macros(&mut primary_span
, &mut children
);
36 self.emit_messages_default(&db
.level
, &db
.message
, &db
.code
, &primary_span
, &children
);
40 /// maximum number of lines we will print for each error; arbitrary.
41 pub const MAX_HIGHLIGHT_LINES
: usize = 6;
43 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
44 pub enum ColorConfig
{
51 fn use_color(&self) -> bool
{
53 ColorConfig
::Always
=> true,
54 ColorConfig
::Never
=> false,
55 ColorConfig
::Auto
=> stderr_isatty(),
60 pub struct EmitterWriter
{
62 cm
: Option
<Rc
<CodeMapper
>>,
65 struct FileWithAnnotatedLines
{
71 /// Do not use this for messages that end in `\n` – use `println_maybe_styled` instead. See
72 /// `EmitterWriter::print_maybe_styled` for details.
73 macro_rules
! print_maybe_styled
{
74 ($dst
: expr
, $style
: expr
, $
($arg
: tt
)*) => {
75 $dst
.print_maybe_styled(format_args
!($
($arg
)*), $style
, false)
79 macro_rules
! println_maybe_styled
{
80 ($dst
: expr
, $style
: expr
, $
($arg
: tt
)*) => {
81 $dst
.print_maybe_styled(format_args
!($
($arg
)*), $style
, true)
86 pub fn stderr(color_config
: ColorConfig
,
87 code_map
: Option
<Rc
<CodeMapper
>>)
89 if color_config
.use_color() {
90 let dst
= Destination
::from_stderr();
91 EmitterWriter
{ dst
: dst
,
94 EmitterWriter
{ dst
: Raw(Box
::new(io
::stderr())),
99 pub fn new(dst
: Box
<Write
+ Send
>,
100 code_map
: Option
<Rc
<CodeMapper
>>)
102 EmitterWriter
{ dst
: Raw(dst
),
106 fn preprocess_annotations(&self, msp
: &MultiSpan
) -> Vec
<FileWithAnnotatedLines
> {
107 fn add_annotation_to_file(file_vec
: &mut Vec
<FileWithAnnotatedLines
>,
112 for slot
in file_vec
.iter_mut() {
113 // Look through each of our files for the one we're adding to
114 if slot
.file
.name
== file
.name
{
115 // See if we already have a line for it
116 for line_slot
in &mut slot
.lines
{
117 if line_slot
.line_index
== line_index
{
118 line_slot
.annotations
.push(ann
);
122 // We don't have a line yet, create one
123 slot
.lines
.push(Line
{
124 line_index
: line_index
,
125 annotations
: vec
![ann
],
131 // This is the first time we're seeing the file
132 file_vec
.push(FileWithAnnotatedLines
{
135 line_index
: line_index
,
136 annotations
: vec
![ann
],
141 let mut output
= vec
![];
143 if let Some(ref cm
) = self.cm
{
144 for span_label
in msp
.span_labels() {
145 if span_label
.span
== DUMMY_SP
|| span_label
.span
== COMMAND_LINE_SP
{
148 let lo
= cm
.lookup_char_pos(span_label
.span
.lo
);
149 let mut hi
= cm
.lookup_char_pos(span_label
.span
.hi
);
150 let mut is_minimized
= false;
152 // If the span is multi-line, simplify down to the span of one character
153 if lo
.line
!= hi
.line
{
155 hi
.col
= CharPos(lo
.col
.0 + 1);
159 // Watch out for "empty spans". If we get a span like 6..6, we
160 // want to just display a `^` at 6, so convert that to
161 // 6..7. This is degenerate input, but it's best to degrade
162 // gracefully -- and the parser likes to supply a span like
163 // that for EOF, in particular.
164 if lo
.col
== hi
.col
{
165 hi
.col
= CharPos(lo
.col
.0 + 1);
168 add_annotation_to_file(&mut output
,
174 is_primary
: span_label
.is_primary
,
175 is_minimized
: is_minimized
,
176 label
: span_label
.label
.clone(),
183 fn render_source_line(&self,
184 buffer
: &mut StyledBuffer
,
187 width_offset
: usize) {
188 let source_string
= file
.get_line(line
.line_index
- 1)
191 let line_offset
= buffer
.num_lines();
193 // First create the source line we will highlight.
194 buffer
.puts(line_offset
, width_offset
, &source_string
, Style
::Quotation
);
195 buffer
.puts(line_offset
,
197 &(line
.line_index
.to_string()),
200 draw_col_separator(buffer
, line_offset
, width_offset
- 2);
202 if line
.annotations
.is_empty() {
206 // We want to display like this:
208 // vec.push(vec.pop().unwrap());
209 // --- ^^^ _ previous borrow ends here
211 // | error occurs here
212 // previous borrow of `vec` occurs here
214 // But there are some weird edge cases to be aware of:
216 // vec.push(vec.pop().unwrap());
217 // -------- - previous borrow ends here
219 // |this makes no sense
220 // previous borrow of `vec` occurs here
222 // For this reason, we group the lines into "highlight lines"
223 // and "annotations lines", where the highlight lines have the `~`.
225 // Sort the annotations by (start, end col)
226 let mut annotations
= line
.annotations
.clone();
229 // Next, create the highlight line.
230 for annotation
in &annotations
{
231 for p
in annotation
.start_col
..annotation
.end_col
{
232 if annotation
.is_primary
{
233 buffer
.putc(line_offset
+ 1,
236 Style
::UnderlinePrimary
);
237 if !annotation
.is_minimized
{
238 buffer
.set_style(line_offset
,
240 Style
::UnderlinePrimary
);
243 buffer
.putc(line_offset
+ 1,
246 Style
::UnderlineSecondary
);
247 if !annotation
.is_minimized
{
248 buffer
.set_style(line_offset
,
250 Style
::UnderlineSecondary
);
255 draw_col_separator(buffer
, line_offset
+ 1, width_offset
- 2);
257 // Now we are going to write labels in. To start, we'll exclude
258 // the annotations with no labels.
259 let (labeled_annotations
, unlabeled_annotations
): (Vec
<_
>, _
) = annotations
.into_iter()
260 .partition(|a
| a
.label
.is_some());
262 // If there are no annotations that need text, we're done.
263 if labeled_annotations
.is_empty() {
266 // Now add the text labels. We try, when possible, to stick the rightmost
267 // annotation at the end of the highlight line:
269 // vec.push(vec.pop().unwrap());
270 // --- --- - previous borrow ends here
272 // But sometimes that's not possible because one of the other
273 // annotations overlaps it. For example, from the test
274 // `span_overlap_label`, we have the following annotations
275 // (written on distinct lines for clarity):
281 // In this case, we can't stick the rightmost-most label on
282 // the highlight line, or we would get:
289 // which is totally weird. Instead we want:
297 // which is...less weird, at least. In fact, in general, if
298 // the rightmost span overlaps with any other span, we should
299 // use the "hang below" version, so we can at least make it
300 // clear where the span *starts*.
301 let mut labeled_annotations
= &labeled_annotations
[..];
302 match labeled_annotations
.split_last().unwrap() {
303 (last
, previous
) => {
305 .chain(&unlabeled_annotations
)
306 .all(|a
| !overlaps(a
, last
)) {
307 // append the label afterwards; we keep it in a separate
309 let highlight_label
: String
= format
!(" {}", last
.label
.as_ref().unwrap());
311 buffer
.append(line_offset
+ 1, &highlight_label
, Style
::LabelPrimary
);
313 buffer
.append(line_offset
+ 1, &highlight_label
, Style
::LabelSecondary
);
315 labeled_annotations
= previous
;
320 // If that's the last annotation, we're done
321 if labeled_annotations
.is_empty() {
325 for (index
, annotation
) in labeled_annotations
.iter().enumerate() {
328 // - One line for each thing that comes after
329 let comes_after
= labeled_annotations
.len() - index
- 1;
330 let blank_lines
= 3 + comes_after
;
332 // For each blank line, draw a `|` at our column. The
333 // text ought to be long enough for this.
334 for index
in 2..blank_lines
{
335 if annotation
.is_primary
{
336 buffer
.putc(line_offset
+ index
,
337 width_offset
+ annotation
.start_col
,
339 Style
::UnderlinePrimary
);
341 buffer
.putc(line_offset
+ index
,
342 width_offset
+ annotation
.start_col
,
344 Style
::UnderlineSecondary
);
346 draw_col_separator(buffer
, line_offset
+ index
, width_offset
- 2);
349 if annotation
.is_primary
{
350 buffer
.puts(line_offset
+ blank_lines
,
351 width_offset
+ annotation
.start_col
,
352 annotation
.label
.as_ref().unwrap(),
353 Style
::LabelPrimary
);
355 buffer
.puts(line_offset
+ blank_lines
,
356 width_offset
+ annotation
.start_col
,
357 annotation
.label
.as_ref().unwrap(),
358 Style
::LabelSecondary
);
360 draw_col_separator(buffer
, line_offset
+ blank_lines
, width_offset
- 2);
364 fn get_multispan_max_line_num(&mut self, msp
: &MultiSpan
) -> usize {
366 if let Some(ref cm
) = self.cm
{
367 for primary_span
in msp
.primary_spans() {
368 if primary_span
!= &DUMMY_SP
&& primary_span
!= &COMMAND_LINE_SP
{
369 let hi
= cm
.lookup_char_pos(primary_span
.hi
);
375 for span_label
in msp
.span_labels() {
376 if span_label
.span
!= DUMMY_SP
&& span_label
.span
!= COMMAND_LINE_SP
{
377 let hi
= cm
.lookup_char_pos(span_label
.span
.hi
);
387 fn get_max_line_num(&mut self, span
: &MultiSpan
, children
: &Vec
<SubDiagnostic
>) -> usize {
390 let primary
= self.get_multispan_max_line_num(span
);
391 max
= if primary
> max { primary }
else { max }
;
393 for sub
in children
{
394 let sub_result
= self.get_multispan_max_line_num(&sub
.span
);
395 max
= if sub_result
> max { primary }
else { max }
;
400 // This "fixes" MultiSpans that contain Spans that are pointing to locations inside of
401 // <*macros>. Since these locations are often difficult to read, we move these Spans from
402 // <*macros> to their corresponding use site.
403 fn fix_multispan_in_std_macros(&mut self, span
: &mut MultiSpan
) -> bool
{
404 let mut spans_updated
= false;
406 if let Some(ref cm
) = self.cm
{
407 let mut before_after
: Vec
<(Span
, Span
)> = vec
![];
408 let mut new_labels
: Vec
<(Span
, String
)> = vec
![];
410 // First, find all the spans in <*macros> and point instead at their use site
411 for sp
in span
.primary_spans() {
412 if (*sp
== COMMAND_LINE_SP
) || (*sp
== DUMMY_SP
) {
415 if cm
.span_to_filename(sp
.clone()).contains("macros>") {
416 let v
= cm
.macro_backtrace(sp
.clone());
417 if let Some(use_site
) = v
.last() {
418 before_after
.push((sp
.clone(), use_site
.call_site
.clone()));
421 for trace
in cm
.macro_backtrace(sp
.clone()).iter().rev() {
422 // Only show macro locations that are local
423 // and display them like a span_note
424 if let Some(def_site
) = trace
.def_site_span
{
425 if (def_site
== COMMAND_LINE_SP
) || (def_site
== DUMMY_SP
) {
428 // Check to make sure we're not in any <*macros>
429 if !cm
.span_to_filename(def_site
).contains("macros>") &&
430 !trace
.macro_decl_name
.starts_with("#[")
432 new_labels
.push((trace
.call_site
,
433 "in this macro invocation".to_string()));
439 for (label_span
, label_text
) in new_labels
{
440 span
.push_span_label(label_span
, label_text
);
442 for sp_label
in span
.span_labels() {
443 if (sp_label
.span
== COMMAND_LINE_SP
) || (sp_label
.span
== DUMMY_SP
) {
446 if cm
.span_to_filename(sp_label
.span
.clone()).contains("macros>") {
447 let v
= cm
.macro_backtrace(sp_label
.span
.clone());
448 if let Some(use_site
) = v
.last() {
449 before_after
.push((sp_label
.span
.clone(), use_site
.call_site
.clone()));
453 // After we have them, make sure we replace these 'bad' def sites with their use sites
454 for (before
, after
) in before_after
{
455 span
.replace(before
, after
);
456 spans_updated
= true;
463 // This does a small "fix" for multispans by looking to see if it can find any that
464 // point directly at <*macros>. Since these are often difficult to read, this
465 // will change the span to point at the use site.
466 fn fix_multispans_in_std_macros(&mut self,
467 span
: &mut MultiSpan
,
468 children
: &mut Vec
<SubDiagnostic
>) {
469 let mut spans_updated
= self.fix_multispan_in_std_macros(span
);
470 for child
in children
.iter_mut() {
471 spans_updated
|= self.fix_multispan_in_std_macros(&mut child
.span
);
474 children
.push(SubDiagnostic
{
476 message
:"this error originates in a macro outside of the current \
478 span
: MultiSpan
::new(),
484 fn emit_message_default(&mut self,
487 code
: &Option
<String
>,
489 max_line_num_len
: usize,
492 let mut buffer
= StyledBuffer
::new();
494 if msp
.primary_spans().is_empty() && msp
.span_labels().is_empty() && is_secondary
{
495 // This is a secondary message with no span info
496 for _
in 0..max_line_num_len
{
497 buffer
.prepend(0, " ", Style
::NoStyle
);
499 draw_note_separator(&mut buffer
, 0, max_line_num_len
+ 1);
500 buffer
.append(0, &level
.to_string(), Style
::HeaderMsg
);
501 buffer
.append(0, ": ", Style
::NoStyle
);
502 buffer
.append(0, msg
, Style
::NoStyle
);
505 buffer
.append(0, &level
.to_string(), Style
::Level(level
.clone()));
508 buffer
.append(0, "[", Style
::Level(level
.clone()));
509 buffer
.append(0, &code
, Style
::Level(level
.clone()));
510 buffer
.append(0, "]", Style
::Level(level
.clone()));
514 buffer
.append(0, ": ", Style
::HeaderMsg
);
515 buffer
.append(0, msg
, Style
::HeaderMsg
);
518 // Preprocess all the annotations so that they are grouped by file and by line number
519 // This helps us quickly iterate over the whole message (including secondary file spans)
520 let mut annotated_files
= self.preprocess_annotations(msp
);
522 // Make sure our primary file comes first
524 if let (Some(ref cm
), Some(ref primary_span
)) = (self.cm
.as_ref(),
525 msp
.primary_span().as_ref()) {
526 if primary_span
!= &&DUMMY_SP
&& primary_span
!= &&COMMAND_LINE_SP
{
527 cm
.lookup_char_pos(primary_span
.lo
)
530 emit_to_destination(&buffer
.render(), level
, &mut self.dst
)?
;
534 // If we don't have span information, emit and exit
535 emit_to_destination(&buffer
.render(), level
, &mut self.dst
)?
;
539 annotated_files
.binary_search_by(|x
| x
.file
.name
.cmp(&primary_lo
.file
.name
)) {
540 annotated_files
.swap(0, pos
);
543 // Print out the annotate source lines that correspond with the error
544 for annotated_file
in annotated_files
{
545 // print out the span location and spacer before we print the annotated source
546 // to do this, we need to know if this span will be primary
547 let is_primary
= primary_lo
.file
.name
== annotated_file
.file
.name
;
549 // remember where we are in the output buffer for easy reference
550 let buffer_msg_line_offset
= buffer
.num_lines();
552 buffer
.prepend(buffer_msg_line_offset
, "--> ", Style
::LineNumber
);
553 let loc
= primary_lo
.clone();
554 buffer
.append(buffer_msg_line_offset
,
555 &format
!("{}:{}:{}", loc
.file
.name
, loc
.line
, loc
.col
.0 + 1),
556 Style
::LineAndColumn
);
557 for _
in 0..max_line_num_len
{
558 buffer
.prepend(buffer_msg_line_offset
, " ", Style
::NoStyle
);
561 // remember where we are in the output buffer for easy reference
562 let buffer_msg_line_offset
= buffer
.num_lines();
565 draw_col_separator(&mut buffer
, buffer_msg_line_offset
, max_line_num_len
+ 1);
567 // Then, the secondary file indicator
568 buffer
.prepend(buffer_msg_line_offset
+ 1, "::: ", Style
::LineNumber
);
569 buffer
.append(buffer_msg_line_offset
+ 1,
570 &annotated_file
.file
.name
,
571 Style
::LineAndColumn
);
572 for _
in 0..max_line_num_len
{
573 buffer
.prepend(buffer_msg_line_offset
+ 1, " ", Style
::NoStyle
);
577 // Put in the spacer between the location and annotated source
578 let buffer_msg_line_offset
= buffer
.num_lines();
579 draw_col_separator_no_space(&mut buffer
, buffer_msg_line_offset
, max_line_num_len
+ 1);
581 // Next, output the annotate source for this file
582 for line_idx
in 0..annotated_file
.lines
.len() {
583 self.render_source_line(&mut buffer
,
584 annotated_file
.file
.clone(),
585 &annotated_file
.lines
[line_idx
],
586 3 + max_line_num_len
);
588 // check to see if we need to print out or elide lines that come between
589 // this annotated line and the next one
590 if line_idx
< (annotated_file
.lines
.len() - 1) {
591 let line_idx_delta
= annotated_file
.lines
[line_idx
+ 1].line_index
-
592 annotated_file
.lines
[line_idx
].line_index
;
593 if line_idx_delta
> 2 {
594 let last_buffer_line_num
= buffer
.num_lines();
595 buffer
.puts(last_buffer_line_num
, 0, "...", Style
::LineNumber
);
596 } else if line_idx_delta
== 2 {
597 let unannotated_line
= annotated_file
.file
598 .get_line(annotated_file
.lines
[line_idx
].line_index
)
601 let last_buffer_line_num
= buffer
.num_lines();
603 buffer
.puts(last_buffer_line_num
,
605 &(annotated_file
.lines
[line_idx
+ 1].line_index
- 1)
608 draw_col_separator(&mut buffer
, last_buffer_line_num
, 1 + max_line_num_len
);
609 buffer
.puts(last_buffer_line_num
,
610 3 + max_line_num_len
,
618 // final step: take our styled buffer, render it, then output it
619 emit_to_destination(&buffer
.render(), level
, &mut self.dst
)?
;
623 fn emit_suggestion_default(&mut self,
624 suggestion
: &CodeSuggestion
,
627 max_line_num_len
: usize)
629 use std
::borrow
::Borrow
;
631 let primary_span
= suggestion
.msp
.primary_span().unwrap();
632 if let Some(ref cm
) = self.cm
{
633 let mut buffer
= StyledBuffer
::new();
635 buffer
.append(0, &level
.to_string(), Style
::Level(level
.clone()));
636 buffer
.append(0, ": ", Style
::HeaderMsg
);
637 buffer
.append(0, msg
, Style
::HeaderMsg
);
639 let lines
= cm
.span_to_lines(primary_span
).unwrap();
641 assert
!(!lines
.lines
.is_empty());
643 let complete
= suggestion
.splice_lines(cm
.borrow());
645 // print the suggestion without any line numbers, but leave
646 // space for them. This helps with lining up with previous
647 // snippets from the actual error being reported.
648 let mut lines
= complete
.lines();
650 for line
in lines
.by_ref().take(MAX_HIGHLIGHT_LINES
) {
651 draw_col_separator(&mut buffer
, row_num
, max_line_num_len
+ 1);
652 buffer
.append(row_num
, line
, Style
::NoStyle
);
656 // if we elided some lines, add an ellipsis
657 if let Some(_
) = lines
.next() {
658 buffer
.append(row_num
, "...", Style
::NoStyle
);
660 emit_to_destination(&buffer
.render(), level
, &mut self.dst
)?
;
664 fn emit_messages_default(&mut self,
667 code
: &Option
<String
>,
669 children
: &Vec
<SubDiagnostic
>) {
670 let max_line_num
= self.get_max_line_num(span
, children
);
671 let max_line_num_len
= max_line_num
.to_string().len();
673 match self.emit_message_default(span
,
680 if !children
.is_empty() {
681 let mut buffer
= StyledBuffer
::new();
682 draw_col_separator_no_space(&mut buffer
, 0, max_line_num_len
+ 1);
683 match emit_to_destination(&buffer
.render(), level
, &mut self.dst
) {
685 Err(e
) => panic
!("failed to emit error: {}", e
)
688 for child
in children
{
689 match child
.render_span
{
690 Some(FullSpan(ref msp
)) => {
691 match self.emit_message_default(msp
,
697 Err(e
) => panic
!("failed to emit error: {}", e
),
701 Some(Suggestion(ref cs
)) => {
702 match self.emit_suggestion_default(cs
,
706 Err(e
) => panic
!("failed to emit error: {}", e
),
711 match self.emit_message_default(&child
.span
,
717 Err(e
) => panic
!("failed to emit error: {}", e
),
724 Err(e
) => panic
!("failed to emit error: {}", e
)
726 match write
!(&mut self.dst
, "\n") {
727 Err(e
) => panic
!("failed to emit error: {}", e
),
728 _
=> match self.dst
.flush() {
729 Err(e
) => panic
!("failed to emit error: {}", e
),
736 fn draw_col_separator(buffer
: &mut StyledBuffer
, line
: usize, col
: usize) {
737 buffer
.puts(line
, col
, "| ", Style
::LineNumber
);
740 fn draw_col_separator_no_space(buffer
: &mut StyledBuffer
, line
: usize, col
: usize) {
741 buffer
.puts(line
, col
, "|", Style
::LineNumber
);
744 fn draw_note_separator(buffer
: &mut StyledBuffer
, line
: usize, col
: usize) {
745 buffer
.puts(line
, col
, "= ", Style
::LineNumber
);
748 fn overlaps(a1
: &Annotation
, a2
: &Annotation
) -> bool
{
749 (a2
.start_col
..a2
.end_col
).contains(a1
.start_col
) ||
750 (a1
.start_col
..a1
.end_col
).contains(a2
.start_col
)
753 fn emit_to_destination(rendered_buffer
: &Vec
<Vec
<StyledString
>>,
755 dst
: &mut Destination
) -> io
::Result
<()> {
758 // In order to prevent error message interleaving, where multiple error lines get intermixed
759 // when multiple compiler processes error simultaneously, we emit errors with additional
762 // On Unix systems, we write into a buffered terminal rather than directly to a terminal. When
763 // the .flush() is called we take the buffer created from the buffered writes and write it at
764 // one shot. Because the Unix systems use ANSI for the colors, which is a text-based styling
765 // scheme, this buffered approach works and maintains the styling.
767 // On Windows, styling happens through calls to a terminal API. This prevents us from using the
768 // same buffering approach. Instead, we use a global Windows mutex, which we acquire long
769 // enough to output the full error message, then we release.
770 let _buffer_lock
= lock
::acquire_global_lock("rustc_errors");
771 for line
in rendered_buffer
{
773 dst
.apply_style(lvl
.clone(), part
.style
)?
;
774 write
!(dst
, "{}", part
.text
)?
;
784 fn stderr_isatty() -> bool
{
786 unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
789 fn stderr_isatty() -> bool
{
792 type HANDLE
= *mut u8;
793 const STD_ERROR_HANDLE
: DWORD
= -12i32 as DWORD
;
795 fn GetStdHandle(which
: DWORD
) -> HANDLE
;
796 fn GetConsoleMode(hConsoleHandle
: HANDLE
,
797 lpMode
: *mut DWORD
) -> BOOL
;
800 let handle
= GetStdHandle(STD_ERROR_HANDLE
);
802 GetConsoleMode(handle
, &mut out
) != 0
806 pub type BufferedStderr
= term
::Terminal
<Output
= BufferedWriter
> + Send
;
808 pub enum Destination
{
809 Terminal(Box
<term
::StderrTerminal
>),
810 BufferedTerminal(Box
<BufferedStderr
>),
811 Raw(Box
<Write
+ Send
>),
814 /// Buffered writer gives us a way on Unix to buffer up an entire error message before we output
815 /// it. This helps to prevent interleaving of multiple error messages when multiple compiler
816 /// processes error simultaneously
817 pub struct BufferedWriter
{
821 impl BufferedWriter
{
822 // note: we use _new because the conditional compilation at its use site may make this
823 // this function unused on some platforms
824 fn _new() -> BufferedWriter
{
831 impl Write
for BufferedWriter
{
832 fn write(&mut self, buf
: &[u8]) -> io
::Result
<usize> {
834 self.buffer
.push(*b
);
838 fn flush(&mut self) -> io
::Result
<()> {
839 let mut stderr
= io
::stderr();
841 stderr
.write_all(&self.buffer
)?
;
851 /// When not on Windows, prefer the buffered terminal so that we can buffer an entire error
852 /// to be emitted at one time.
853 fn from_stderr() -> Destination
{
854 let stderr
: Option
<Box
<BufferedStderr
>> =
855 term
::TerminfoTerminal
::new(BufferedWriter
::_new())
856 .map(|t
| Box
::new(t
) as Box
<BufferedStderr
>);
859 Some(t
) => BufferedTerminal(t
),
860 None
=> Raw(Box
::new(io
::stderr())),
865 /// Return a normal, unbuffered terminal when on Windows.
866 fn from_stderr() -> Destination
{
867 let stderr
: Option
<Box
<term
::StderrTerminal
>> =
868 term
::TerminfoTerminal
::new(io
::stderr())
869 .map(|t
| Box
::new(t
) as Box
<term
::StderrTerminal
>)
870 .or_else(|| term
::WinConsole
::new(io
::stderr()).ok()
871 .map(|t
| Box
::new(t
) as Box
<term
::StderrTerminal
>));
874 Some(t
) => Terminal(t
),
875 None
=> Raw(Box
::new(io
::stderr())),
879 fn apply_style(&mut self,
884 Style
::FileNameStyle
| Style
::LineAndColumn
=> {}
885 Style
::LineNumber
=> {
886 self.start_attr(term
::Attr
::Bold
)?
;
888 self.start_attr(term
::Attr
::ForegroundColor(term
::color
::BRIGHT_CYAN
))?
;
890 self.start_attr(term
::Attr
::ForegroundColor(term
::color
::BRIGHT_BLUE
))?
;
893 Style
::ErrorCode
=> {
894 self.start_attr(term
::Attr
::Bold
)?
;
895 self.start_attr(term
::Attr
::ForegroundColor(term
::color
::BRIGHT_MAGENTA
))?
;
897 Style
::Quotation
=> {}
898 Style
::OldSchoolNote
=> {
899 self.start_attr(term
::Attr
::Bold
)?
;
900 self.start_attr(term
::Attr
::ForegroundColor(term
::color
::BRIGHT_GREEN
))?
;
902 Style
::OldSchoolNoteText
| Style
::HeaderMsg
=> {
903 self.start_attr(term
::Attr
::Bold
)?
;
905 self.start_attr(term
::Attr
::ForegroundColor(term
::color
::BRIGHT_WHITE
))?
;
908 Style
::UnderlinePrimary
| Style
::LabelPrimary
=> {
909 self.start_attr(term
::Attr
::Bold
)?
;
910 self.start_attr(term
::Attr
::ForegroundColor(lvl
.color()))?
;
912 Style
::UnderlineSecondary
|
913 Style
::LabelSecondary
=> {
914 self.start_attr(term
::Attr
::Bold
)?
;
916 self.start_attr(term
::Attr
::ForegroundColor(term
::color
::BRIGHT_CYAN
))?
;
918 self.start_attr(term
::Attr
::ForegroundColor(term
::color
::BRIGHT_BLUE
))?
;
923 self.start_attr(term
::Attr
::Bold
)?
;
924 self.start_attr(term
::Attr
::ForegroundColor(l
.color()))?
;
930 fn start_attr(&mut self, attr
: term
::Attr
) -> io
::Result
<()> {
932 Terminal(ref mut t
) => { t.attr(attr)?; }
933 BufferedTerminal(ref mut t
) => { t.attr(attr)?; }
939 fn reset_attrs(&mut self) -> io
::Result
<()> {
941 Terminal(ref mut t
) => { t.reset()?; }
942 BufferedTerminal(ref mut t
) => { t.reset()?; }
949 impl Write
for Destination
{
950 fn write(&mut self, bytes
: &[u8]) -> io
::Result
<usize> {
952 Terminal(ref mut t
) => t
.write(bytes
),
953 BufferedTerminal(ref mut t
) => t
.write(bytes
),
954 Raw(ref mut w
) => w
.write(bytes
),
957 fn flush(&mut self) -> io
::Result
<()> {
959 Terminal(ref mut t
) => t
.flush(),
960 BufferedTerminal(ref mut t
) => t
.flush(),
961 Raw(ref mut w
) => w
.flush(),