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, LineInfo, CharPos};
17 use {Level, CodeSuggestion, DiagnosticBuilder, CodeMapper};
19 use snippet::{StyledString, Style, FormatMode, Annotation, Line};
20 use styled_buffer::StyledBuffer;
23 use std::io::prelude::*;
28 /// Emitter trait for emitting errors.
30 /// Emit a structured diagnostic.
31 fn emit(&mut self, db: &DiagnosticBuilder);
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()
44 self.emit_messages_old_school(db);
46 self.emit_messages_default(db);
51 /// maximum number of lines we will print for each error; arbitrary.
52 pub const MAX_HIGHLIGHT_LINES: usize = 6;
54 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
55 pub enum ColorConfig {
62 fn use_color(&self) -> bool {
64 ColorConfig::Always => true,
65 ColorConfig::Never => false,
66 ColorConfig::Auto => stderr_isatty(),
71 pub struct EmitterWriter {
73 registry: Option<registry::Registry>,
74 cm: Option<Rc<CodeMapper>>,
76 // For now, allow an old-school mode while we transition
77 format_mode: FormatMode
80 struct FileWithAnnotatedLines {
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)
94 macro_rules! println_maybe_styled {
95 ($dst: expr, $style: expr, $($arg: tt)*) => {
96 $dst.print_maybe_styled(format_args!($($arg)*), $style, true)
101 pub fn stderr(color_config: ColorConfig,
102 registry: Option<registry::Registry>,
103 code_map: Option<Rc<CodeMapper>>,
104 format_mode: FormatMode)
106 if color_config.use_color() {
107 let dst = Destination::from_stderr();
108 EmitterWriter { dst: dst,
111 format_mode: format_mode.clone() }
113 EmitterWriter { dst: Raw(Box::new(io::stderr())),
116 format_mode: format_mode.clone() }
120 pub fn new(dst: Box<Write + Send>,
121 registry: Option<registry::Registry>,
122 code_map: Option<Rc<CodeMapper>>,
123 format_mode: FormatMode)
125 EmitterWriter { dst: Raw(dst),
128 format_mode: format_mode.clone() }
131 fn preprocess_annotations(&self, msp: &MultiSpan) -> Vec<FileWithAnnotatedLines> {
132 fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>,
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);
147 // We don't have a line yet, create one
148 slot.lines.push(Line {
149 line_index: line_index,
150 annotations: vec![ann],
156 // This is the first time we're seeing the file
157 file_vec.push(FileWithAnnotatedLines {
160 line_index: line_index,
161 annotations: vec![ann],
166 let mut output = vec![];
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 {
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;
177 // If the span is multi-line, simplify down to the span of one character
178 if lo.line != hi.line {
180 hi.col = CharPos(lo.col.0 + 1);
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);
193 add_annotation_to_file(&mut output,
199 is_primary: span_label.is_primary,
200 is_minimized: is_minimized,
201 label: span_label.label.clone(),
208 fn render_source_line(&self,
209 buffer: &mut StyledBuffer,
212 width_offset: usize) {
213 let source_string = file.get_line(line.line_index - 1)
216 let line_offset = buffer.num_lines();
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,
222 &(line.line_index.to_string()),
225 draw_col_separator(buffer, line_offset, width_offset - 2);
227 if line.annotations.is_empty() {
231 // We want to display like this:
233 // vec.push(vec.pop().unwrap());
234 // --- ^^^ _ previous borrow ends here
236 // | error occurs here
237 // previous borrow of `vec` occurs here
239 // But there are some weird edge cases to be aware of:
241 // vec.push(vec.pop().unwrap());
242 // -------- - previous borrow ends here
244 // |this makes no sense
245 // previous borrow of `vec` occurs here
247 // For this reason, we group the lines into "highlight lines"
248 // and "annotations lines", where the highlight lines have the `~`.
250 // Sort the annotations by (start, end col)
251 let mut annotations = line.annotations.clone();
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,
261 Style::UnderlinePrimary);
262 if !annotation.is_minimized {
263 buffer.set_style(line_offset,
265 Style::UnderlinePrimary);
268 buffer.putc(line_offset + 1,
271 Style::UnderlineSecondary);
272 if !annotation.is_minimized {
273 buffer.set_style(line_offset,
275 Style::UnderlineSecondary);
280 draw_col_separator(buffer, line_offset + 1, width_offset - 2);
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());
287 // If there are no annotations that need text, we're done.
288 if labeled_annotations.is_empty() {
291 // Now add the text labels. We try, when possible, to stick the rightmost
292 // annotation at the end of the highlight line:
294 // vec.push(vec.pop().unwrap());
295 // --- --- - previous borrow ends here
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):
306 // In this case, we can't stick the rightmost-most label on
307 // the highlight line, or we would get:
314 // which is totally weird. Instead we want:
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) => {
330 .chain(&unlabeled_annotations)
331 .all(|a| !overlaps(a, last)) {
332 // append the label afterwards; we keep it in a separate
334 let highlight_label: String = format!(" {}", last.label.as_ref().unwrap());
336 buffer.append(line_offset + 1, &highlight_label, Style::LabelPrimary);
338 buffer.append(line_offset + 1, &highlight_label, Style::LabelSecondary);
340 labeled_annotations = previous;
345 // If that's the last annotation, we're done
346 if labeled_annotations.is_empty() {
350 for (index, annotation) in labeled_annotations.iter().enumerate() {
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;
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,
364 Style::UnderlinePrimary);
366 buffer.putc(line_offset + index,
367 width_offset + annotation.start_col,
369 Style::UnderlineSecondary);
371 draw_col_separator(buffer, line_offset + index, width_offset - 2);
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);
380 buffer.puts(line_offset + blank_lines,
381 width_offset + annotation.start_col,
382 annotation.label.as_ref().unwrap(),
383 Style::LabelSecondary);
385 draw_col_separator(buffer, line_offset + blank_lines, width_offset - 2);
389 fn get_multispan_max_line_num(&mut self, msp: &MultiSpan) -> usize {
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);
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);
412 fn get_max_line_num(&mut self, db: &DiagnosticBuilder) -> usize {
415 let primary = self.get_multispan_max_line_num(&db.span);
416 max = if primary > max { primary } else { max };
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 };
425 fn emit_message_default(&mut self,
428 code: &Option<String>,
430 max_line_num_len: usize,
433 let mut buffer = StyledBuffer::new();
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);
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);
446 buffer.append(0, &level.to_string(), Style::Level(level.clone()));
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()));
455 buffer.append(0, ": ", Style::HeaderMsg);
456 buffer.append(0, msg, Style::HeaderMsg);
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);
463 // Make sure our primary file comes first
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)
471 emit_to_destination(&buffer.render(), level, &mut self.dst)?;
475 // If we don't have span information, emit and exit
476 emit_to_destination(&buffer.render(), level, &mut self.dst)?;
480 annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name)) {
481 annotated_files.swap(0, pos);
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;
490 // remember where we are in the output buffer for easy reference
491 let buffer_msg_line_offset = buffer.num_lines();
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);
502 // remember where we are in the output buffer for easy reference
503 let buffer_msg_line_offset = buffer.num_lines();
506 draw_col_separator(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1);
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);
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);
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);
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)
542 let last_buffer_line_num = buffer.num_lines();
544 buffer.puts(last_buffer_line_num,
546 &(annotated_file.lines[line_idx + 1].line_index - 1)
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,
559 if let Some(ref primary_span) = msp.primary_span().as_ref() {
560 self.render_macro_backtrace_old_school(primary_span, &mut buffer)?;
563 // final step: take our styled buffer, render it, then output it
564 emit_to_destination(&buffer.render(), level, &mut self.dst)?;
568 fn emit_suggestion_default(&mut self,
569 suggestion: &CodeSuggestion,
572 max_line_num_len: usize)
574 use std::borrow::Borrow;
576 let primary_span = suggestion.msp.primary_span().unwrap();
577 if let Some(ref cm) = self.cm {
578 let mut buffer = StyledBuffer::new();
580 buffer.append(0, &level.to_string(), Style::Level(level.clone()));
581 buffer.append(0, ": ", Style::HeaderMsg);
582 buffer.append(0, msg, Style::HeaderMsg);
584 let lines = cm.span_to_lines(primary_span).unwrap();
586 assert!(!lines.lines.is_empty());
588 let complete = suggestion.splice_lines(cm.borrow());
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();
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);
601 // if we elided some lines, add an ellipsis
602 if let Some(_) = lines.next() {
603 buffer.append(row_num, "...", Style::NoStyle);
605 emit_to_destination(&buffer.render(), level, &mut self.dst)?;
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();
613 match self.emit_message_default(&db.span,
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) {
625 Err(e) => panic!("failed to emit error: {}", e)
628 for child in &db.children {
629 match child.render_span {
630 Some(FullSpan(ref msp)) => {
631 match self.emit_message_default(msp,
637 Err(e) => panic!("failed to emit error: {}", e),
641 Some(Suggestion(ref cs)) => {
642 match self.emit_suggestion_default(cs,
646 Err(e) => panic!("failed to emit error: {}", e),
651 match self.emit_message_default(&child.span,
657 Err(e) => panic!("failed to emit error: {}", e),
664 Err(e) => panic!("failed to emit error: {}", e)
666 match write!(&mut self.dst, "\n") {
667 Err(e) => panic!("failed to emit error: {}", e),
671 fn emit_message_old_school(&mut self,
674 code: &Option<String>,
678 let mut buffer = StyledBuffer::new();
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)
692 buffer.append(0, &loc, Style::NoStyle);
693 buffer.append(0, " ", Style::NoStyle);
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);
701 buffer.append(0, "[", Style::ErrorCode);
702 buffer.append(0, &code, Style::ErrorCode);
703 buffer.append(0, "]", Style::ErrorCode);
709 emit_to_destination(&buffer.render(), level, &mut self.dst)?;
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)?;
723 let annotated_files = self.preprocess_annotations(msp);
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()) {
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)
734 let line_offset = buffer.num_lines();
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();
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();
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,
755 if annotation.is_primary {
756 Style::UnderlinePrimary
761 buffer.putc(line_offset + 1,
764 if annotation.is_primary {
765 Style::UnderlinePrimary
773 if let Some(ref primary_span) = msp.primary_span().as_ref() {
774 self.render_macro_backtrace_old_school(primary_span, &mut buffer)?;
778 &Some(ref code) if self.registry.as_ref()
779 .and_then(|registry| registry.find_description(code))
781 let msg = "run `rustc --explain ".to_string() + &code.to_string() +
782 "` to see a detailed explanation";
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);
794 // final step: take our styled buffer, render it, then output it
795 emit_to_destination(&buffer.render(), level, &mut self.dst)?;
798 fn emit_suggestion_old_school(&mut self,
799 suggestion: &CodeSuggestion,
803 use std::borrow::Borrow;
805 let primary_span = suggestion.msp.primary_span().unwrap();
806 if let Some(ref cm) = self.cm {
807 let mut buffer = StyledBuffer::new();
809 let loc = cm.span_to_string(primary_span);
812 buffer.append(0, &loc, Style::NoStyle);
813 buffer.append(0, " ", Style::NoStyle);
816 buffer.append(0, &level.to_string(), Style::Level(level.clone()));
817 buffer.append(0, ": ", Style::HeaderMsg);
818 buffer.append(0, msg, Style::HeaderMsg);
820 let lines = cm.span_to_lines(primary_span).unwrap();
822 assert!(!lines.lines.is_empty());
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];
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());
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();
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);
842 buffer.append(row_num, line, Style::NoStyle);
846 // if we elided some lines, add an ellipsis
847 if let Some(_) = lines.next() {
848 buffer.append(row_num, "...", Style::NoStyle);
850 emit_to_destination(&buffer.render(), level, &mut self.dst)?;
855 fn emit_messages_old_school(&mut self, db: &DiagnosticBuilder) {
856 match self.emit_message_old_school(&db.span,
862 for child in &db.children {
863 let (span, show_snippet) = if child.span.primary_spans().is_empty() {
864 (db.span.clone(), false)
866 (child.span.clone(), true)
869 match child.render_span {
870 Some(FullSpan(_)) => {
871 match self.emit_message_old_school(&span,
876 Err(e) => panic!("failed to emit error: {}", e),
880 Some(Suggestion(ref cs)) => {
881 match self.emit_suggestion_old_school(cs,
884 Err(e) => panic!("failed to emit error: {}", e),
889 match self.emit_message_old_school(&span,
894 Err(e) => panic!("failed to emit error: {}", e),
901 Err(e) => panic!("failed to emit error: {}", e)
905 fn render_macro_backtrace_old_school(&mut self,
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();
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)));
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);
930 fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
931 buffer.puts(line, col, "| ", Style::LineNumber);
934 fn draw_col_separator_no_space(buffer: &mut StyledBuffer, line: usize, col: usize) {
935 buffer.puts(line, col, "|", Style::LineNumber);
938 fn draw_note_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
939 buffer.puts(line, col, "= ", Style::LineNumber);
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)
947 fn emit_to_destination(rendered_buffer: &Vec<Vec<StyledString>>,
949 dst: &mut Destination) -> io::Result<()> {
950 for line in rendered_buffer {
952 dst.apply_style(lvl.clone(), part.style)?;
953 write!(dst, "{}", part.text)?;
961 fn line_num_max_digits(line: &LineInfo) -> usize {
962 let mut max_line_num = line.line_index + 1;
964 while max_line_num > 0 {
972 fn stderr_isatty() -> bool {
974 unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
977 fn stderr_isatty() -> bool {
980 type HANDLE = *mut u8;
981 const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD;
983 fn GetStdHandle(which: DWORD) -> HANDLE;
984 fn GetConsoleMode(hConsoleHandle: HANDLE,
985 lpMode: *mut DWORD) -> BOOL;
988 let handle = GetStdHandle(STD_ERROR_HANDLE);
990 GetConsoleMode(handle, &mut out) != 0
994 pub enum Destination {
995 Terminal(Box<term::StderrTerminal>),
996 Raw(Box<Write + Send>),
1000 fn from_stderr() -> Destination {
1001 match term::stderr() {
1002 Some(t) => Terminal(t),
1003 None => Raw(Box::new(io::stderr())),
1007 fn apply_style(&mut self,
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)));
1017 Style::ErrorCode => {
1018 try!(self.start_attr(term::Attr::Bold));
1019 try!(self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA)));
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)));
1026 Style::OldSchoolNoteText | Style::HeaderMsg => {
1027 try!(self.start_attr(term::Attr::Bold));
1029 Style::UnderlinePrimary | Style::LabelPrimary => {
1030 try!(self.start_attr(term::Attr::Bold));
1031 try!(self.start_attr(term::Attr::ForegroundColor(lvl.color())));
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)));
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())));
1047 fn start_attr(&mut self, attr: term::Attr) -> io::Result<()> {
1049 Terminal(ref mut t) => { t.attr(attr)?; }
1055 fn reset_attrs(&mut self) -> io::Result<()> {
1057 Terminal(ref mut t) => { t.reset()?; }
1064 impl Write for Destination {
1065 fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
1067 Terminal(ref mut t) => t.write(bytes),
1068 Raw(ref mut w) => w.write(bytes),
1071 fn flush(&mut self) -> io::Result<()> {
1073 Terminal(ref mut t) => t.flush(),
1074 Raw(ref mut w) => w.flush(),