]> git.proxmox.com Git - rustc.git/blame - src/libsyntax/errors/emitter.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / libsyntax / errors / emitter.rs
CommitLineData
9cc50fc6 1// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
223e47cc
LB
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
1a4d82fc
JJ
11use self::Destination::*;
12
7453a54e 13use codemap::{self, COMMAND_LINE_SP, DUMMY_SP, Pos, Span, MultiSpan};
1a4d82fc 14use diagnostics;
223e47cc 15
7453a54e 16use errors::{Level, RenderSpan, CodeSuggestion, DiagnosticBuilder};
9cc50fc6
SL
17use errors::RenderSpan::*;
18use errors::Level::*;
19
20use std::{cmp, fmt};
c34b1796
AL
21use std::io::prelude::*;
22use std::io;
9cc50fc6 23use std::rc::Rc;
92a42be0 24use term;
1a4d82fc 25
9cc50fc6 26pub trait Emitter {
7453a54e
SL
27 fn emit(&mut self, span: Option<&MultiSpan>, msg: &str, code: Option<&str>, lvl: Level);
28 fn custom_emit(&mut self, sp: &RenderSpan, msg: &str, lvl: Level);
9cc50fc6
SL
29
30 /// Emit a structured diagnostic.
31 fn emit_struct(&mut self, db: &DiagnosticBuilder) {
7453a54e 32 self.emit(db.span.as_ref(), &db.message, db.code.as_ref().map(|s| &**s), db.level);
9cc50fc6
SL
33 for child in &db.children {
34 match child.render_span {
7453a54e
SL
35 Some(ref sp) => self.custom_emit(sp, &child.message, child.level),
36 None => self.emit(child.span.as_ref(), &child.message, None, child.level),
9cc50fc6 37 }
1a4d82fc
JJ
38 }
39 }
223e47cc
LB
40}
41
9cc50fc6 42/// maximum number of lines we will print for each error; arbitrary.
7453a54e
SL
43pub const MAX_HIGHLIGHT_LINES: usize = 6;
44
45/// maximum number of lines we will print for each span; arbitrary.
46const MAX_SP_LINES: usize = 6;
9cc50fc6
SL
47
48#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1a4d82fc
JJ
49pub enum ColorConfig {
50 Auto,
51 Always,
9cc50fc6 52 Never,
223e47cc
LB
53}
54
9cc50fc6
SL
55impl ColorConfig {
56 fn use_color(&self) -> bool {
57 match *self {
58 ColorConfig::Always => true,
59 ColorConfig::Never => false,
60 ColorConfig::Auto => stderr_isatty(),
61 }
d9579d0f
AL
62 }
63}
64
9cc50fc6
SL
65/// A basic emitter for when we don't have access to a codemap or registry. Used
66/// for reporting very early errors, etc.
67pub struct BasicEmitter {
68 dst: Destination,
d9579d0f
AL
69}
70
9cc50fc6
SL
71impl Emitter for BasicEmitter {
72 fn emit(&mut self,
7453a54e 73 msp: Option<&MultiSpan>,
9cc50fc6
SL
74 msg: &str,
75 code: Option<&str>,
76 lvl: Level) {
7453a54e 77 assert!(msp.is_none(), "BasicEmitter can't handle spans");
9cc50fc6
SL
78 if let Err(e) = print_diagnostic(&mut self.dst, "", lvl, msg, code) {
79 panic!("failed to print diagnostics: {:?}", e);
80 }
d9579d0f 81
d9579d0f 82 }
223e47cc 83
7453a54e 84 fn custom_emit(&mut self, _: &RenderSpan, _: &str, _: Level) {
9cc50fc6 85 panic!("BasicEmitter can't handle custom_emit");
223e47cc
LB
86 }
87}
88
9cc50fc6
SL
89impl BasicEmitter {
90 pub fn stderr(color_config: ColorConfig) -> BasicEmitter {
91 if color_config.use_color() {
92 let dst = Destination::from_stderr();
93 BasicEmitter { dst: dst }
94 } else {
95 BasicEmitter { dst: Raw(Box::new(io::stderr())) }
223e47cc 96 }
223e47cc
LB
97 }
98}
99
9cc50fc6
SL
100pub struct EmitterWriter {
101 dst: Destination,
102 registry: Option<diagnostics::registry::Registry>,
103 cm: Rc<codemap::CodeMap>,
223e47cc
LB
104}
105
9cc50fc6
SL
106impl Emitter for EmitterWriter {
107 fn emit(&mut self,
7453a54e 108 msp: Option<&MultiSpan>,
9cc50fc6
SL
109 msg: &str,
110 code: Option<&str>,
111 lvl: Level) {
7453a54e
SL
112 let error = match msp.map(|s|(s.to_span_bounds(), s)) {
113 Some((COMMAND_LINE_SP, msp)) => {
114 self.emit_(&FileLine(msp.clone()), msg, code, lvl)
115 },
116 Some((DUMMY_SP, _)) | None => print_diagnostic(&mut self.dst, "", lvl, msg, code),
117 Some((_, msp)) => self.emit_(&FullSpan(msp.clone()), msg, code, lvl),
9cc50fc6 118 };
1a4d82fc 119
9cc50fc6
SL
120 if let Err(e) = error {
121 panic!("failed to print diagnostics: {:?}", e);
1a4d82fc
JJ
122 }
123 }
223e47cc 124
9cc50fc6 125 fn custom_emit(&mut self,
7453a54e 126 rsp: &RenderSpan,
9cc50fc6
SL
127 msg: &str,
128 lvl: Level) {
7453a54e 129 if let Err(e) = self.emit_(rsp, msg, None, lvl) {
9cc50fc6 130 panic!("failed to print diagnostics: {:?}", e);
1a4d82fc 131 }
223e47cc
LB
132 }
133}
134
c1a9b12d
SL
135/// Do not use this for messages that end in `\n` – use `println_maybe_styled` instead. See
136/// `EmitterWriter::print_maybe_styled` for details.
137macro_rules! print_maybe_styled {
9cc50fc6
SL
138 ($dst: expr, $style: expr, $($arg: tt)*) => {
139 $dst.print_maybe_styled(format_args!($($arg)*), $style, false)
c1a9b12d
SL
140 }
141}
142
143macro_rules! println_maybe_styled {
9cc50fc6
SL
144 ($dst: expr, $style: expr, $($arg: tt)*) => {
145 $dst.print_maybe_styled(format_args!($($arg)*), $style, true)
c1a9b12d
SL
146 }
147}
148
1a4d82fc
JJ
149impl EmitterWriter {
150 pub fn stderr(color_config: ColorConfig,
9cc50fc6
SL
151 registry: Option<diagnostics::registry::Registry>,
152 code_map: Rc<codemap::CodeMap>)
153 -> EmitterWriter {
154 if color_config.use_color() {
155 let dst = Destination::from_stderr();
156 EmitterWriter { dst: dst, registry: registry, cm: code_map }
1a4d82fc 157 } else {
9cc50fc6 158 EmitterWriter { dst: Raw(Box::new(io::stderr())), registry: registry, cm: code_map }
1a4d82fc
JJ
159 }
160 }
161
c34b1796 162 pub fn new(dst: Box<Write + Send>,
9cc50fc6
SL
163 registry: Option<diagnostics::registry::Registry>,
164 code_map: Rc<codemap::CodeMap>)
165 -> EmitterWriter {
166 EmitterWriter { dst: Raw(dst), registry: registry, cm: code_map }
c1a9b12d
SL
167 }
168
9cc50fc6 169 fn emit_(&mut self,
7453a54e 170 rsp: &RenderSpan,
9cc50fc6
SL
171 msg: &str,
172 code: Option<&str>,
173 lvl: Level)
174 -> io::Result<()> {
7453a54e
SL
175 let msp = rsp.span();
176 let bounds = msp.to_span_bounds();
c1a9b12d 177
7453a54e 178 let ss = if bounds == COMMAND_LINE_SP {
c1a9b12d 179 "<command line option>".to_string()
7453a54e
SL
180 } else if let EndSpan(_) = *rsp {
181 let span_end = Span { lo: bounds.hi, hi: bounds.hi, expn_id: bounds.expn_id};
9cc50fc6 182 self.cm.span_to_string(span_end)
c1a9b12d 183 } else {
7453a54e 184 self.cm.span_to_string(bounds)
c1a9b12d
SL
185 };
186
54a0048b 187 print_diagnostic(&mut self.dst, &ss[..], lvl, msg, code)?;
c1a9b12d 188
7453a54e 189 match *rsp {
c1a9b12d 190 FullSpan(_) => {
54a0048b
SL
191 self.highlight_lines(msp, lvl)?;
192 self.print_macro_backtrace(bounds)?;
c1a9b12d
SL
193 }
194 EndSpan(_) => {
54a0048b
SL
195 self.end_highlight_lines(msp, lvl)?;
196 self.print_macro_backtrace(bounds)?;
c1a9b12d 197 }
7453a54e 198 Suggestion(ref suggestion) => {
54a0048b
SL
199 self.highlight_suggestion(suggestion)?;
200 self.print_macro_backtrace(bounds)?;
c1a9b12d
SL
201 }
202 FileLine(..) => {
203 // no source text in this case!
204 }
205 }
206
7453a54e
SL
207 if let Some(code) = code {
208 if let Some(_) = self.registry.as_ref()
209 .and_then(|registry| registry.find_description(code)) {
54a0048b
SL
210 print_diagnostic(&mut self.dst, &ss[..], Help,
211 &format!("run `rustc --explain {}` to see a \
212 detailed explanation", code), None)?;
7453a54e 213 }
c1a9b12d
SL
214 }
215 Ok(())
216 }
217
7453a54e 218 fn highlight_suggestion(&mut self, suggestion: &CodeSuggestion) -> io::Result<()>
c1a9b12d 219 {
7453a54e 220 let lines = self.cm.span_to_lines(suggestion.msp.to_span_bounds()).unwrap();
c1a9b12d
SL
221 assert!(!lines.lines.is_empty());
222
7453a54e
SL
223 let complete = suggestion.splice_lines(&self.cm);
224 let line_count = cmp::min(lines.lines.len(), MAX_HIGHLIGHT_LINES);
225 let display_lines = &lines.lines[..line_count];
c1a9b12d 226
7453a54e
SL
227 let fm = &*lines.file;
228 // Calculate the widest number to format evenly
229 let max_digits = line_num_max_digits(display_lines.last().unwrap());
c1a9b12d
SL
230
231 // print the suggestion without any line numbers, but leave
232 // space for them. This helps with lining up with previous
233 // snippets from the actual error being reported.
c1a9b12d 234 let mut lines = complete.lines();
7453a54e 235 for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) {
54a0048b
SL
236 write!(&mut self.dst, "{0}:{1:2$} {3}\n",
237 fm.name, "", max_digits, line)?;
c1a9b12d
SL
238 }
239
240 // if we elided some lines, add an ellipsis
7453a54e 241 if let Some(_) = lines.next() {
54a0048b
SL
242 write!(&mut self.dst, "{0:1$} {0:2$} ...\n",
243 "", fm.name.len(), max_digits)?;
c1a9b12d
SL
244 }
245
246 Ok(())
247 }
248
249 fn highlight_lines(&mut self,
7453a54e
SL
250 msp: &MultiSpan,
251 lvl: Level)
c1a9b12d
SL
252 -> io::Result<()>
253 {
7453a54e 254 let lines = match self.cm.span_to_lines(msp.to_span_bounds()) {
c1a9b12d
SL
255 Ok(lines) => lines,
256 Err(_) => {
54a0048b 257 write!(&mut self.dst, "(internal compiler error: unprintable span)\n")?;
c1a9b12d
SL
258 return Ok(());
259 }
260 };
261
262 let fm = &*lines.file;
7453a54e
SL
263 if let None = fm.src {
264 return Ok(());
265 }
c1a9b12d 266
7453a54e
SL
267 let display_line_infos = &lines.lines[..];
268 assert!(display_line_infos.len() > 0);
c1a9b12d
SL
269
270 // Calculate the widest number to format evenly and fix #11715
7453a54e
SL
271 let digits = line_num_max_digits(display_line_infos.last().unwrap());
272 let first_line_index = display_line_infos.first().unwrap().line_index;
c1a9b12d 273
7453a54e 274 let skip = fm.name.chars().count() + digits + 2;
c1a9b12d 275
7453a54e
SL
276 let mut spans = msp.spans.iter().peekable();
277 let mut lines = display_line_infos.iter();
278 let mut prev_line_index = first_line_index.wrapping_sub(1);
c1a9b12d 279
7453a54e
SL
280 // Display at most MAX_HIGHLIGHT_LINES lines.
281 let mut remaining_err_lines = MAX_HIGHLIGHT_LINES;
c1a9b12d 282
7453a54e
SL
283 // To emit a overflowed spans code-lines *AFTER* the rendered spans
284 let mut overflowed_buf = String::new();
285 let mut overflowed = false;
c1a9b12d 286
7453a54e
SL
287 // FIXME (#8706)
288 'l: loop {
289 if remaining_err_lines <= 0 {
290 break;
c1a9b12d 291 }
7453a54e
SL
292 let line = match lines.next() {
293 Some(l) => l,
294 None => break,
295 };
296
297 // Skip is the number of characters we need to skip because they are
298 // part of the 'filename:line ' part of the code line.
299 let mut s: String = ::std::iter::repeat(' ').take(skip).collect();
300 let mut col = skip;
301 let mut lastc = ' ';
302
303 let cur_line_str = fm.get_line(line.line_index).unwrap();
304 let mut line_chars = cur_line_str.chars().enumerate().peekable();
305 let mut line_spans = 0;
306
307 // Assemble spans for this line
308 loop {
309 // Peek here to preserve the span if it doesn't belong to this line
310 let sp = match spans.peek() {
311 Some(sp) => **sp,
312 None => break,
313 };
314 let lo = self.cm.lookup_char_pos(sp.lo);
315 let hi = self.cm.lookup_char_pos(sp.hi);
316 let line_num = line.line_index + 1;
317
318 if !(lo.line <= line_num && hi.line >= line_num) {
319 // This line is not contained in the span
320 if overflowed {
321 // Never elide the final line of an overflowed span
322 prev_line_index = line.line_index - 1;
323 overflowed = false;
324 break;
325 }
326
327 if line_spans == 0 {
328 continue 'l;
329 } else {
330 // This line is finished, now render the spans we've assembled
331 break;
332 }
333 }
334 spans.next();
335 line_spans += 1;
336
337 if lo.line != hi.line {
338 // Assemble extra code lines to be emitted after this lines spans
339 // (substract `2` because the first and last line are rendered normally)
340 let max_lines = cmp::min(remaining_err_lines, MAX_SP_LINES) - 2;
341 prev_line_index = line.line_index;
342 let count = cmp::min((hi.line - lo.line - 1), max_lines);
343 for _ in 0..count {
344 let line = match lines.next() {
345 Some(l) => l,
346 None => break,
347 };
348 let line_str = fm.get_line(line.line_index).unwrap();
349 overflowed_buf.push_str(&format!("{}:{:>width$} {}\n",
350 fm.name,
351 line.line_index + 1,
352 line_str,
353 width=digits));
354 remaining_err_lines -= 1;
355 prev_line_index += 1
356 }
357 // Remember that the span overflowed to ensure
358 // that we emit its last line exactly once
359 // (other spans may, or may not, start on it)
360 overflowed = true;
361 break;
362 }
363
364 for (pos, ch) in line_chars.by_ref() {
c1a9b12d
SL
365 lastc = ch;
366 if pos >= lo.col.to_usize() { break; }
7453a54e 367 // Whenever a tab occurs on the code line, we insert one on
c1a9b12d
SL
368 // the error-point-squiggly-line as well (instead of a space).
369 // That way the squiggly line will usually appear in the correct
370 // position.
371 match ch {
372 '\t' => {
373 col += 8 - col%8;
374 s.push('\t');
375 },
376 _ => {
377 col += 1;
378 s.push(' ');
379 },
380 }
381 }
382
7453a54e
SL
383 s.push('^');
384 let col_ptr = col;
c1a9b12d
SL
385 let count = match lastc {
386 // Most terminals have a tab stop every eight columns by default
387 '\t' => 8 - col%8,
388 _ => 1,
389 };
390 col += count;
391 s.extend(::std::iter::repeat('~').take(count));
392
9cc50fc6 393 let hi = self.cm.lookup_char_pos(sp.hi);
c1a9b12d 394 if hi.col != lo.col {
7453a54e
SL
395 let mut chars = line_chars.by_ref();
396 loop {
397 // We peek here to preserve the value for the next span
398 let (pos, ch) = match chars.peek() {
399 Some(elem) => *elem,
400 None => break,
401 };
c1a9b12d
SL
402 if pos >= hi.col.to_usize() { break; }
403 let count = match ch {
404 '\t' => 8 - col%8,
405 _ => 1,
406 };
407 col += count;
408 s.extend(::std::iter::repeat('~').take(count));
7453a54e
SL
409
410 chars.next();
c1a9b12d
SL
411 }
412 }
7453a54e 413 if (col - col_ptr) > 0 {
c1a9b12d
SL
414 // One extra squiggly is replaced by a "^"
415 s.pop();
416 }
7453a54e 417 }
c1a9b12d 418
7453a54e
SL
419 // If we elided something put an ellipsis.
420 if prev_line_index != line.line_index.wrapping_sub(1) && !overflowed {
54a0048b 421 write!(&mut self.dst, "{0:1$}...\n", "", skip)?;
7453a54e
SL
422 }
423
424 // Print offending code-line
425 remaining_err_lines -= 1;
54a0048b
SL
426 write!(&mut self.dst, "{}:{:>width$} {}\n",
427 fm.name,
428 line.line_index + 1,
429 cur_line_str,
430 width=digits)?;
7453a54e
SL
431
432 if s.len() > skip {
433 // Render the spans we assembled previously (if any).
54a0048b
SL
434 println_maybe_styled!(&mut self.dst, term::Attr::ForegroundColor(lvl.color()),
435 "{}", s)?;
c1a9b12d 436 }
7453a54e
SL
437
438 if !overflowed_buf.is_empty() {
439 // Print code-lines trailing the rendered spans (when a span overflows)
54a0048b 440 write!(&mut self.dst, "{}", &overflowed_buf)?;
7453a54e
SL
441 overflowed_buf.clear();
442 } else {
443 prev_line_index = line.line_index;
444 }
445 }
446
447 // If we elided something, put an ellipsis.
448 if lines.next().is_some() {
54a0048b 449 write!(&mut self.dst, "{0:1$}...\n", "", skip)?;
c1a9b12d
SL
450 }
451 Ok(())
452 }
453
454 /// Here are the differences between this and the normal `highlight_lines`:
7453a54e
SL
455 /// `end_highlight_lines` will always put arrow on the last byte of each
456 /// span (instead of the first byte). Also, when a span is too long (more
c1a9b12d
SL
457 /// than 6 lines), `end_highlight_lines` will print the first line, then
458 /// dot dot dot, then last line, whereas `highlight_lines` prints the first
459 /// six lines.
460 #[allow(deprecated)]
461 fn end_highlight_lines(&mut self,
7453a54e
SL
462 msp: &MultiSpan,
463 lvl: Level)
c1a9b12d 464 -> io::Result<()> {
7453a54e 465 let lines = match self.cm.span_to_lines(msp.to_span_bounds()) {
c1a9b12d
SL
466 Ok(lines) => lines,
467 Err(_) => {
54a0048b 468 write!(&mut self.dst, "(internal compiler error: unprintable span)\n")?;
c1a9b12d
SL
469 return Ok(());
470 }
471 };
472
473 let fm = &*lines.file;
7453a54e
SL
474 if let None = fm.src {
475 return Ok(());
476 }
c1a9b12d
SL
477
478 let lines = &lines.lines[..];
7453a54e
SL
479
480 // Calculate the widest number to format evenly
481 let first_line = lines.first().unwrap();
482 let last_line = lines.last().unwrap();
483 let digits = line_num_max_digits(last_line);
484
485 let skip = fm.name.chars().count() + digits + 2;
486
487 let mut spans = msp.spans.iter().peekable();
488 let mut lines = lines.iter();
489 let mut prev_line_index = first_line.line_index.wrapping_sub(1);
490
491 // Display at most MAX_HIGHLIGHT_LINES lines.
492 let mut remaining_err_lines = MAX_HIGHLIGHT_LINES;
493
494 'l: loop {
495 if remaining_err_lines <= 0 {
496 break;
c1a9b12d 497 }
7453a54e
SL
498 let line = match lines.next() {
499 Some(line) => line,
500 None => break,
501 };
502
503 // Skip is the number of characters we need to skip because they are
504 // part of the 'filename:line ' part of the previous line.
505 let mut s: String = ::std::iter::repeat(' ').take(skip).collect();
506
507 let line_str = fm.get_line(line.line_index).unwrap();
508 let mut line_chars = line_str.chars().enumerate();
509 let mut line_spans = 0;
510
511 loop {
512 // Peek here to preserve the span if it doesn't belong to this line
513 let sp = match spans.peek() {
514 Some(sp) => **sp,
515 None => break,
516 };
517 let lo = self.cm.lookup_char_pos(sp.lo);
518 let hi = self.cm.lookup_char_pos(sp.hi);
519 let elide_sp = (hi.line - lo.line) >= MAX_SP_LINES;
520
521 let line_num = line.line_index + 1;
522 if !(lo.line <= line_num && hi.line >= line_num) {
523 // This line is not contained in the span
524 if line_spans == 0 {
525 continue 'l;
526 } else {
527 // This line is finished, now render the spans we've assembled
528 break
529 }
530 } else if hi.line > line_num {
531 if elide_sp && lo.line < line_num {
532 // This line is inbetween the first and last line of the span,
533 // so we may want to elide it.
534 continue 'l;
535 } else {
536 break
537 }
c1a9b12d 538 }
7453a54e
SL
539 line_spans += 1;
540 spans.next();
541
542 for (pos, ch) in line_chars.by_ref() {
543 // Span seems to use half-opened interval, so subtract 1
544 if pos >= hi.col.to_usize() - 1 { break; }
545 // Whenever a tab occurs on the previous line, we insert one on
546 // the error-point-squiggly-line as well (instead of a space).
547 // That way the squiggly line will usually appear in the correct
548 // position.
549 match ch {
550 '\t' => s.push('\t'),
551 _ => s.push(' '),
552 }
c1a9b12d 553 }
7453a54e
SL
554 s.push('^');
555 }
556
557 if prev_line_index != line.line_index.wrapping_sub(1) {
558 // If we elided something, put an ellipsis.
54a0048b 559 write!(&mut self.dst, "{0:1$}...\n", "", skip)?;
c1a9b12d 560 }
7453a54e
SL
561
562 // Print offending code-lines
54a0048b
SL
563 write!(&mut self.dst, "{}:{:>width$} {}\n", fm.name,
564 line.line_index + 1, line_str, width=digits)?;
7453a54e
SL
565 remaining_err_lines -= 1;
566
567 if s.len() > skip {
568 // Render the spans we assembled previously (if any)
54a0048b
SL
569 println_maybe_styled!(&mut self.dst, term::Attr::ForegroundColor(lvl.color()),
570 "{}", s)?;
7453a54e
SL
571 }
572 prev_line_index = line.line_index;
c1a9b12d 573 }
7453a54e 574 Ok(())
c1a9b12d
SL
575 }
576
577 fn print_macro_backtrace(&mut self,
c1a9b12d
SL
578 sp: Span)
579 -> io::Result<()> {
e9174d1e 580 let mut last_span = codemap::DUMMY_SP;
9cc50fc6
SL
581 let mut span = sp;
582
583 loop {
584 let span_name_span = self.cm.with_expn_info(span.expn_id, |expn_info| {
585 expn_info.map(|ei| {
586 let (pre, post) = match ei.callee.format {
587 codemap::MacroAttribute(..) => ("#[", "]"),
588 codemap::MacroBang(..) => ("", "!"),
589 };
590 let macro_decl_name = format!("in this expansion of {}{}{}",
591 pre,
592 ei.callee.name(),
593 post);
594 let def_site_span = ei.callee.span;
595 (ei.call_site, macro_decl_name, def_site_span)
596 })
597 });
598 let (macro_decl_name, def_site_span) = match span_name_span {
599 None => break,
600 Some((sp, macro_decl_name, def_site_span)) => {
601 span = sp;
602 (macro_decl_name, def_site_span)
c1a9b12d 603 }
9cc50fc6
SL
604 };
605
606 // Don't print recursive invocations
7453a54e 607 if !span.source_equal(&last_span) {
9cc50fc6
SL
608 let mut diag_string = macro_decl_name;
609 if let Some(def_site_span) = def_site_span {
610 diag_string.push_str(&format!(" (defined in {})",
611 self.cm.span_to_filename(def_site_span)));
612 }
613
614 let snippet = self.cm.span_to_string(span);
54a0048b 615 print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)?;
9cc50fc6
SL
616 }
617 last_span = span;
c1a9b12d 618 }
e9174d1e
SL
619
620 Ok(())
c1a9b12d 621 }
1a4d82fc 622}
970d7e83 623
7453a54e
SL
624fn line_num_max_digits(line: &codemap::LineInfo) -> usize {
625 let mut max_line_num = line.line_index + 1;
626 let mut digits = 0;
627 while max_line_num > 0 {
628 max_line_num /= 10;
629 digits += 1;
630 }
631 digits
632}
633
9cc50fc6
SL
634fn print_diagnostic(dst: &mut Destination,
635 topic: &str,
636 lvl: Level,
637 msg: &str,
638 code: Option<&str>)
639 -> io::Result<()> {
640 if !topic.is_empty() {
54a0048b 641 write!(dst, "{} ", topic)?;
9cc50fc6
SL
642 }
643
54a0048b
SL
644 print_maybe_styled!(dst, term::Attr::ForegroundColor(lvl.color()),
645 "{}: ", lvl.to_string())?;
646 print_maybe_styled!(dst, term::Attr::Bold, "{}", msg)?;
9cc50fc6 647
7453a54e
SL
648 if let Some(code) = code {
649 let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA);
54a0048b 650 print_maybe_styled!(dst, style, " [{}]", code.clone())?;
9cc50fc6 651 }
54a0048b 652 write!(dst, "\n")?;
9cc50fc6
SL
653 Ok(())
654}
655
c34b1796
AL
656#[cfg(unix)]
657fn stderr_isatty() -> bool {
92a42be0 658 use libc;
c34b1796
AL
659 unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
660}
661#[cfg(windows)]
662fn stderr_isatty() -> bool {
92a42be0
SL
663 type DWORD = u32;
664 type BOOL = i32;
665 type HANDLE = *mut u8;
666 const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD;
c34b1796 667 extern "system" {
92a42be0
SL
668 fn GetStdHandle(which: DWORD) -> HANDLE;
669 fn GetConsoleMode(hConsoleHandle: HANDLE,
670 lpMode: *mut DWORD) -> BOOL;
c34b1796
AL
671 }
672 unsafe {
673 let handle = GetStdHandle(STD_ERROR_HANDLE);
674 let mut out = 0;
675 GetConsoleMode(handle, &mut out) != 0
676 }
677}
678
9cc50fc6
SL
679enum Destination {
680 Terminal(Box<term::StderrTerminal>),
681 Raw(Box<Write + Send>),
682}
683
684impl Destination {
685 fn from_stderr() -> Destination {
686 match term::stderr() {
687 Some(t) => Terminal(t),
688 None => Raw(Box::new(io::stderr())),
689 }
690 }
691
692 fn print_maybe_styled(&mut self,
693 args: fmt::Arguments,
694 color: term::Attr,
695 print_newline_at_end: bool)
696 -> io::Result<()> {
697 match *self {
698 Terminal(ref mut t) => {
54a0048b 699 t.attr(color)?;
9cc50fc6
SL
700 // If `msg` ends in a newline, we need to reset the color before
701 // the newline. We're making the assumption that we end up writing
702 // to a `LineBufferedWriter`, which means that emitting the reset
703 // after the newline ends up buffering the reset until we print
704 // another line or exit. Buffering the reset is a problem if we're
705 // sharing the terminal with any other programs (e.g. other rustc
706 // instances via `make -jN`).
707 //
708 // Note that if `msg` contains any internal newlines, this will
709 // result in the `LineBufferedWriter` flushing twice instead of
710 // once, which still leaves the opportunity for interleaved output
711 // to be miscolored. We assume this is rare enough that we don't
712 // have to worry about it.
54a0048b
SL
713 t.write_fmt(args)?;
714 t.reset()?;
9cc50fc6
SL
715 if print_newline_at_end {
716 t.write_all(b"\n")
717 } else {
718 Ok(())
719 }
720 }
721 Raw(ref mut w) => {
54a0048b 722 w.write_fmt(args)?;
9cc50fc6
SL
723 if print_newline_at_end {
724 w.write_all(b"\n")
725 } else {
726 Ok(())
727 }
728 }
729 }
730 }
731}
732
c34b1796
AL
733impl Write for Destination {
734 fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
735 match *self {
736 Terminal(ref mut t) => t.write(bytes),
737 Raw(ref mut w) => w.write(bytes),
738 }
739 }
740 fn flush(&mut self) -> io::Result<()> {
1a4d82fc 741 match *self {
c34b1796
AL
742 Terminal(ref mut t) => t.flush(),
743 Raw(ref mut w) => w.flush(),
1a4d82fc
JJ
744 }
745 }
223e47cc
LB
746}
747
c1a9b12d
SL
748
749#[cfg(test)]
750mod test {
7453a54e 751 use errors::{Level, CodeSuggestion};
9cc50fc6 752 use super::EmitterWriter;
7453a54e 753 use codemap::{mk_sp, CodeMap, Span, MultiSpan, BytePos, NO_EXPANSION};
c1a9b12d
SL
754 use std::sync::{Arc, Mutex};
755 use std::io::{self, Write};
756 use std::str::from_utf8;
9cc50fc6 757 use std::rc::Rc;
c1a9b12d 758
7453a54e
SL
759 struct Sink(Arc<Mutex<Vec<u8>>>);
760 impl Write for Sink {
761 fn write(&mut self, data: &[u8]) -> io::Result<usize> {
762 Write::write(&mut *self.0.lock().unwrap(), data)
763 }
764 fn flush(&mut self) -> io::Result<()> { Ok(()) }
765 }
766
767 /// Given a string like " ^~~~~~~~~~~~ ", produces a span
768 /// coverting that range. The idea is that the string has the same
769 /// length as the input, and we uncover the byte positions. Note
770 /// that this can span lines and so on.
771 fn span_from_selection(input: &str, selection: &str) -> Span {
772 assert_eq!(input.len(), selection.len());
773 let left_index = selection.find('^').unwrap() as u32;
774 let right_index = selection.rfind('~').map(|x|x as u32).unwrap_or(left_index);
775 Span { lo: BytePos(left_index), hi: BytePos(right_index + 1), expn_id: NO_EXPANSION }
776 }
777
c1a9b12d
SL
778 // Diagnostic doesn't align properly in span where line number increases by one digit
779 #[test]
780 fn test_hilight_suggestion_issue_11715() {
c1a9b12d 781 let data = Arc::new(Mutex::new(Vec::new()));
9cc50fc6
SL
782 let cm = Rc::new(CodeMap::new());
783 let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone());
c1a9b12d
SL
784 let content = "abcdefg
785 koksi
786 line3
787 line4
788 cinq
789 line6
790 line7
791 line8
792 line9
793 line10
794 e-lä-vän
795 tolv
796 dreizehn
797 ";
798 let file = cm.new_filemap_and_lines("dummy.txt", content);
799 let start = file.lines.borrow()[7];
800 let end = file.lines.borrow()[11];
801 let sp = mk_sp(start, end);
802 let lvl = Level::Error;
c1a9b12d 803 println!("highlight_lines");
7453a54e 804 ew.highlight_lines(&sp.into(), lvl).unwrap();
c1a9b12d
SL
805 println!("done");
806 let vec = data.lock().unwrap().clone();
807 let vec: &[u8] = &vec;
808 let str = from_utf8(vec).unwrap();
809 println!("{}", str);
810 assert_eq!(str, "dummy.txt: 8 line8\n\
811 dummy.txt: 9 line9\n\
812 dummy.txt:10 line10\n\
813 dummy.txt:11 e-lä-vän\n\
814 dummy.txt:12 tolv\n");
815 }
7453a54e
SL
816
817 #[test]
818 fn test_single_span_splice() {
819 // Test that a `MultiSpan` containing a single span splices a substition correctly
820 let cm = CodeMap::new();
821 let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
822 let selection = " \n ^~\n~~~\n~~~~~ \n \n";
823 cm.new_filemap_and_lines("blork.rs", inputtext);
824 let sp = span_from_selection(inputtext, selection);
825 let msp: MultiSpan = sp.into();
826
827 // check that we are extracting the text we thought we were extracting
828 assert_eq!(&cm.span_to_snippet(sp).unwrap(), "BB\nCCC\nDDDDD");
829
830 let substitute = "ZZZZZZ".to_owned();
831 let expected = "bbbbZZZZZZddddd";
832 let suggest = CodeSuggestion {
833 msp: msp,
834 substitutes: vec![substitute],
835 };
836 assert_eq!(suggest.splice_lines(&cm), expected);
837 }
838
839 #[test]
840 fn test_multiple_span_splice() {
841 // Test that a `MultiSpan` containing multiple spans splices substitions on
842 // several lines correctly
843 let cm = CodeMap::new();
844 let inp = "aaaaabbbbBB\nZZ\nZZ\nCCCDDDDDdddddeee";
845 let sp1 = " ^~~~~~\n \n \n ";
846 let sp2 = " \n \n \n^~~~~~ ";
847 let sp3 = " \n \n \n ^~~ ";
848 let sp4 = " \n \n \n ^~~~ ";
849
850 let span_eq = |sp, eq| assert_eq!(&cm.span_to_snippet(sp).unwrap(), eq);
851
852 cm.new_filemap_and_lines("blork.rs", inp);
853 let sp1 = span_from_selection(inp, sp1);
854 let sp2 = span_from_selection(inp, sp2);
855 let sp3 = span_from_selection(inp, sp3);
856 let sp4 = span_from_selection(inp, sp4);
857 span_eq(sp1, "bbbbBB");
858 span_eq(sp2, "CCCDDD");
859 span_eq(sp3, "ddd");
860 span_eq(sp4, "ddee");
861
862 let substitutes: Vec<String> = ["1", "2", "3", "4"].iter().map(|x|x.to_string()).collect();
863 let expected = "aaaaa1\nZZ\nZZ\n2DD34e";
864
865 let test = |msp| {
866 let suggest = CodeSuggestion {
867 msp: msp,
868 substitutes: substitutes.clone(),
869 };
870 let actual = suggest.splice_lines(&cm);
871 assert_eq!(actual, expected);
872 };
873 test(MultiSpan { spans: vec![sp1, sp2, sp3, sp4] });
874
875 // Test ordering and merging by `MultiSpan::push`
876 let mut msp = MultiSpan::new();
877 msp.push_merge(sp2);
878 msp.push_merge(sp1);
879 assert_eq!(&msp.spans, &[sp1, sp2]);
880 msp.push_merge(sp4);
881 assert_eq!(&msp.spans, &[sp1, sp2, sp4]);
882 msp.push_merge(sp3);
883 assert_eq!(&msp.spans, &[sp1, sp2, sp3, sp4]);
884 test(msp);
885 }
886
887 #[test]
888 fn test_multispan_highlight() {
889 let data = Arc::new(Mutex::new(Vec::new()));
890 let cm = Rc::new(CodeMap::new());
891 let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone());
892
893 let inp = "_____aaaaaa____bbbbbb__cccccdd_";
894 let sp1 = " ^~~~~~ ";
895 let sp2 = " ^~~~~~ ";
896 let sp3 = " ^~~~~ ";
897 let sp4 = " ^~~~ ";
898 let sp34 = " ^~~~~~~ ";
899 let sp4_end = " ^~ ";
900
901 let expect_start = "dummy.txt:1 _____aaaaaa____bbbbbb__cccccdd_\n\
902 \x20 ^~~~~~ ^~~~~~ ^~~~~~~\n";
903 let expect_end = "dummy.txt:1 _____aaaaaa____bbbbbb__cccccdd_\n\
904 \x20 ^ ^ ^ ^\n";
905
906 let span = |sp, expected| {
907 let sp = span_from_selection(inp, sp);
908 assert_eq!(&cm.span_to_snippet(sp).unwrap(), expected);
909 sp
910 };
911 cm.new_filemap_and_lines("dummy.txt", inp);
912 let sp1 = span(sp1, "aaaaaa");
913 let sp2 = span(sp2, "bbbbbb");
914 let sp3 = span(sp3, "ccccc");
915 let sp4 = span(sp4, "ccdd");
916 let sp34 = span(sp34, "cccccdd");
917 let sp4_end = span(sp4_end, "dd");
918
919 let spans = vec![sp1, sp2, sp3, sp4];
920
921 let test = |expected, highlight: &mut FnMut()| {
922 data.lock().unwrap().clear();
923 highlight();
924 let vec = data.lock().unwrap().clone();
925 let actual = from_utf8(&vec[..]).unwrap();
926 assert_eq!(actual, expected);
927 };
928
929 let msp = MultiSpan { spans: vec![sp1, sp2, sp34] };
930 let msp_end = MultiSpan { spans: vec![sp1, sp2, sp3, sp4_end] };
931 test(expect_start, &mut || {
932 diag.highlight_lines(&msp, Level::Error).unwrap();
933 });
934 test(expect_end, &mut || {
935 diag.end_highlight_lines(&msp_end, Level::Error).unwrap();
936 });
937 test(expect_start, &mut || {
938 for msp in cm.group_spans(spans.clone()) {
939 diag.highlight_lines(&msp, Level::Error).unwrap();
940 }
941 });
942 test(expect_end, &mut || {
943 for msp in cm.end_group_spans(spans.clone()) {
944 diag.end_highlight_lines(&msp, Level::Error).unwrap();
945 }
946 });
947 }
948
949 #[test]
950 fn test_huge_multispan_highlight() {
951 let data = Arc::new(Mutex::new(Vec::new()));
952 let cm = Rc::new(CodeMap::new());
953 let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone());
954
955 let inp = "aaaaa\n\
956 aaaaa\n\
957 aaaaa\n\
958 bbbbb\n\
959 ccccc\n\
960 xxxxx\n\
961 yyyyy\n\
962 _____\n\
963 ddd__eee_\n\
964 elided\n\
965 __f_gg";
966 let file = cm.new_filemap_and_lines("dummy.txt", inp);
967
968 let span = |lo, hi, (off_lo, off_hi)| {
969 let lines = file.lines.borrow();
970 let (mut lo, mut hi): (BytePos, BytePos) = (lines[lo], lines[hi]);
971 lo.0 += off_lo;
972 hi.0 += off_hi;
973 mk_sp(lo, hi)
974 };
975 let sp0 = span(4, 6, (0, 5));
976 let sp1 = span(0, 6, (0, 5));
977 let sp2 = span(8, 8, (0, 3));
978 let sp3 = span(8, 8, (5, 8));
979 let sp4 = span(10, 10, (2, 3));
980 let sp5 = span(10, 10, (4, 6));
981
982 let expect0 = "dummy.txt: 5 ccccc\n\
983 dummy.txt: 6 xxxxx\n\
984 dummy.txt: 7 yyyyy\n\
985 \x20 ...\n\
986 dummy.txt: 9 ddd__eee_\n\
987 \x20 ^~~ ^~~\n\
988 \x20 ...\n\
989 dummy.txt:11 __f_gg\n\
990 \x20 ^ ^~\n";
991
992 let expect = "dummy.txt: 1 aaaaa\n\
993 dummy.txt: 2 aaaaa\n\
994 dummy.txt: 3 aaaaa\n\
995 dummy.txt: 4 bbbbb\n\
996 dummy.txt: 5 ccccc\n\
997 dummy.txt: 6 xxxxx\n\
998 \x20 ...\n";
999
1000 let expect_g1 = "dummy.txt:1 aaaaa\n\
1001 dummy.txt:2 aaaaa\n\
1002 dummy.txt:3 aaaaa\n\
1003 dummy.txt:4 bbbbb\n\
1004 dummy.txt:5 ccccc\n\
1005 dummy.txt:6 xxxxx\n\
1006 \x20 ...\n";
1007
1008 let expect2 = "dummy.txt: 9 ddd__eee_\n\
1009 \x20 ^~~ ^~~\n\
1010 \x20 ...\n\
1011 dummy.txt:11 __f_gg\n\
1012 \x20 ^ ^~\n";
1013
1014
1015 let expect_end = "dummy.txt: 1 aaaaa\n\
1016 \x20 ...\n\
1017 dummy.txt: 7 yyyyy\n\
1018 \x20 ^\n\
1019 \x20 ...\n\
1020 dummy.txt: 9 ddd__eee_\n\
1021 \x20 ^ ^\n\
1022 \x20 ...\n\
1023 dummy.txt:11 __f_gg\n\
1024 \x20 ^ ^\n";
1025
1026 let expect0_end = "dummy.txt: 5 ccccc\n\
1027 dummy.txt: 6 xxxxx\n\
1028 dummy.txt: 7 yyyyy\n\
1029 \x20 ^\n\
1030 \x20 ...\n\
1031 dummy.txt: 9 ddd__eee_\n\
1032 \x20 ^ ^\n\
1033 \x20 ...\n\
1034 dummy.txt:11 __f_gg\n\
1035 \x20 ^ ^\n";
1036
1037 let expect_end_g1 = "dummy.txt:1 aaaaa\n\
1038 \x20 ...\n\
1039 dummy.txt:7 yyyyy\n\
1040 \x20 ^\n";
1041
1042 let expect2_end = "dummy.txt: 9 ddd__eee_\n\
1043 \x20 ^ ^\n\
1044 \x20 ...\n\
1045 dummy.txt:11 __f_gg\n\
1046 \x20 ^ ^\n";
1047
1048 let expect_groups = [expect2, expect_g1];
1049 let expect_end_groups = [expect2_end, expect_end_g1];
1050 let spans = vec![sp3, sp1, sp4, sp2, sp5];
1051
1052 macro_rules! test {
1053 ($expected: expr, $highlight: expr) => ({
1054 data.lock().unwrap().clear();
1055 $highlight();
1056 let vec = data.lock().unwrap().clone();
1057 let actual = from_utf8(&vec[..]).unwrap();
1058 println!("actual:");
1059 println!("{}", actual);
1060 println!("expected:");
1061 println!("{}", $expected);
1062 assert_eq!(&actual[..], &$expected[..]);
1063 });
1064 }
1065
1066 let msp0 = MultiSpan { spans: vec![sp0, sp2, sp3, sp4, sp5] };
1067 let msp = MultiSpan { spans: vec![sp1, sp2, sp3, sp4, sp5] };
1068 let msp2 = MultiSpan { spans: vec![sp2, sp3, sp4, sp5] };
1069
1070 test!(expect0, || {
1071 diag.highlight_lines(&msp0, Level::Error).unwrap();
1072 });
1073 test!(expect0_end, || {
1074 diag.end_highlight_lines(&msp0, Level::Error).unwrap();
1075 });
1076 test!(expect, || {
1077 diag.highlight_lines(&msp, Level::Error).unwrap();
1078 });
1079 test!(expect_end, || {
1080 diag.end_highlight_lines(&msp, Level::Error).unwrap();
1081 });
1082 test!(expect2, || {
1083 diag.highlight_lines(&msp2, Level::Error).unwrap();
1084 });
1085 test!(expect2_end, || {
1086 diag.end_highlight_lines(&msp2, Level::Error).unwrap();
1087 });
1088 for (msp, expect) in cm.group_spans(spans.clone()).iter().zip(expect_groups.iter()) {
1089 test!(expect, || {
1090 diag.highlight_lines(&msp, Level::Error).unwrap();
1091 });
1092 }
1093 for (msp, expect) in cm.group_spans(spans.clone()).iter().zip(expect_end_groups.iter()) {
1094 test!(expect, || {
1095 diag.end_highlight_lines(&msp, Level::Error).unwrap();
1096 });
1097 }
1098 }
c1a9b12d 1099}