From e08c5f751578520c013d4838f0e288af937caf0d Mon Sep 17 00:00:00 2001 From: P1start Date: Tue, 7 Jul 2015 11:11:20 +1200 Subject: [PATCH] Change some free functions into methods in libsyntax/diagnostic.rs --- src/libsyntax/diagnostic.rs | 788 ++++++++++++++++++------------------ 1 file changed, 392 insertions(+), 396 deletions(-) diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs index ec93d2c553627..fbf015169f858 100644 --- a/src/libsyntax/diagnostic.rs +++ b/src/libsyntax/diagnostic.rs @@ -308,63 +308,6 @@ impl Level { } } -fn print_maybe_styled(w: &mut EmitterWriter, - msg: &str, - color: term::attr::Attr) -> io::Result<()> { - match w.dst { - Terminal(ref mut t) => { - try!(t.attr(color)); - // If `msg` ends in a newline, we need to reset the color before - // the newline. We're making the assumption that we end up writing - // to a `LineBufferedWriter`, which means that emitting the reset - // after the newline ends up buffering the reset until we print - // another line or exit. Buffering the reset is a problem if we're - // sharing the terminal with any other programs (e.g. other rustc - // instances via `make -jN`). - // - // Note that if `msg` contains any internal newlines, this will - // result in the `LineBufferedWriter` flushing twice instead of - // once, which still leaves the opportunity for interleaved output - // to be miscolored. We assume this is rare enough that we don't - // have to worry about it. - if msg.ends_with("\n") { - try!(t.write_all(msg[..msg.len()-1].as_bytes())); - try!(t.reset()); - try!(t.write_all(b"\n")); - } else { - try!(t.write_all(msg.as_bytes())); - try!(t.reset()); - } - Ok(()) - } - Raw(ref mut w) => w.write_all(msg.as_bytes()), - } -} - -fn print_diagnostic(dst: &mut EmitterWriter, topic: &str, lvl: Level, - msg: &str, code: Option<&str>) -> io::Result<()> { - if !topic.is_empty() { - try!(write!(&mut dst.dst, "{} ", topic)); - } - - try!(print_maybe_styled(dst, - &format!("{}: ", lvl.to_string()), - term::attr::ForegroundColor(lvl.color()))); - try!(print_maybe_styled(dst, - &format!("{}", msg), - term::attr::Bold)); - - match code { - Some(code) => { - let style = term::attr::ForegroundColor(term::color::BRIGHT_MAGENTA); - try!(print_maybe_styled(dst, &format!(" [{}]", code.clone()), style)); - } - None => () - } - try!(write!(&mut dst.dst, "\n")); - Ok(()) -} - pub struct EmitterWriter { dst: Destination, registry: Option @@ -401,6 +344,392 @@ impl EmitterWriter { registry: Option) -> EmitterWriter { EmitterWriter { dst: Raw(dst), registry: registry } } + + fn print_maybe_styled(&mut self, + msg: &str, + color: term::attr::Attr) -> io::Result<()> { + match self.dst { + Terminal(ref mut t) => { + try!(t.attr(color)); + // If `msg` ends in a newline, we need to reset the color before + // the newline. We're making the assumption that we end up writing + // to a `LineBufferedWriter`, which means that emitting the reset + // after the newline ends up buffering the reset until we print + // another line or exit. Buffering the reset is a problem if we're + // sharing the terminal with any other programs (e.g. other rustc + // instances via `make -jN`). + // + // Note that if `msg` contains any internal newlines, this will + // result in the `LineBufferedWriter` flushing twice instead of + // once, which still leaves the opportunity for interleaved output + // to be miscolored. We assume this is rare enough that we don't + // have to worry about it. + if msg.ends_with("\n") { + try!(t.write_all(msg[..msg.len()-1].as_bytes())); + try!(t.reset()); + try!(t.write_all(b"\n")); + } else { + try!(t.write_all(msg.as_bytes())); + try!(t.reset()); + } + Ok(()) + } + Raw(ref mut w) => w.write_all(msg.as_bytes()), + } + } + + fn print_diagnostic(&mut self, topic: &str, lvl: Level, + msg: &str, code: Option<&str>) -> io::Result<()> { + if !topic.is_empty() { + try!(write!(&mut self.dst, "{} ", topic)); + } + + try!(self.print_maybe_styled(&format!("{}: ", lvl.to_string()), + term::attr::ForegroundColor(lvl.color()))); + try!(self.print_maybe_styled(&format!("{}", msg), + term::attr::Bold)); + + match code { + Some(code) => { + let style = term::attr::ForegroundColor(term::color::BRIGHT_MAGENTA); + try!(self.print_maybe_styled(&format!(" [{}]", code.clone()), style)); + } + None => () + } + try!(write!(&mut self.dst, "\n")); + Ok(()) + } + + fn emit_(&mut self, cm: &codemap::CodeMap, rsp: RenderSpan, + msg: &str, code: Option<&str>, lvl: Level) -> io::Result<()> { + let sp = rsp.span(); + + // We cannot check equality directly with COMMAND_LINE_SP + // since PartialEq is manually implemented to ignore the ExpnId + let ss = if sp.expn_id == COMMAND_LINE_EXPN { + "".to_string() + } else if let EndSpan(_) = rsp { + let span_end = Span { lo: sp.hi, hi: sp.hi, expn_id: sp.expn_id}; + cm.span_to_string(span_end) + } else { + cm.span_to_string(sp) + }; + + try!(self.print_diagnostic(&ss[..], lvl, msg, code)); + + match rsp { + FullSpan(_) => { + try!(self.highlight_lines(cm, sp, lvl, cm.span_to_lines(sp))); + try!(self.print_macro_backtrace(cm, sp)); + } + EndSpan(_) => { + try!(self.end_highlight_lines(cm, sp, lvl, cm.span_to_lines(sp))); + try!(self.print_macro_backtrace(cm, sp)); + } + Suggestion(_, ref suggestion) => { + try!(self.highlight_suggestion(cm, sp, suggestion)); + try!(self.print_macro_backtrace(cm, sp)); + } + FileLine(..) => { + // no source text in this case! + } + } + + match code { + Some(code) => + match self.registry.as_ref().and_then(|registry| registry.find_description(code)) { + Some(_) => { + try!(self.print_diagnostic(&ss[..], Help, + &format!("run `rustc --explain {}` to see a \ + detailed explanation", code), None)); + } + None => () + }, + None => (), + } + Ok(()) + } + + fn highlight_suggestion(&mut self, + cm: &codemap::CodeMap, + sp: Span, + suggestion: &str) + -> io::Result<()> + { + let lines = cm.span_to_lines(sp).unwrap(); + assert!(!lines.lines.is_empty()); + + // To build up the result, we want to take the snippet from the first + // line that precedes the span, prepend that with the suggestion, and + // then append the snippet from the last line that trails the span. + let fm = &lines.file; + + let first_line = &lines.lines[0]; + let prefix = fm.get_line(first_line.line_index) + .map(|l| &l[..first_line.start_col.0]) + .unwrap_or(""); + + let last_line = lines.lines.last().unwrap(); + let suffix = fm.get_line(last_line.line_index) + .map(|l| &l[last_line.end_col.0..]) + .unwrap_or(""); + + let complete = format!("{}{}{}", prefix, suggestion, suffix); + + // print the suggestion without any line numbers, but leave + // space for them. This helps with lining up with previous + // snippets from the actual error being reported. + let fm = &*lines.file; + let mut lines = complete.lines(); + for (line, line_index) in lines.by_ref().take(MAX_LINES).zip(first_line.line_index..) { + let elided_line_num = format!("{}", line_index+1); + try!(write!(&mut self.dst, "{0}:{1:2$} {3}\n", + fm.name, "", elided_line_num.len(), line)); + } + + // if we elided some lines, add an ellipsis + if lines.next().is_some() { + let elided_line_num = format!("{}", first_line.line_index + MAX_LINES + 1); + try!(write!(&mut self.dst, "{0:1$} {0:2$} ...\n", + "", fm.name.len(), elided_line_num.len())); + } + + Ok(()) + } + + fn highlight_lines(&mut self, + cm: &codemap::CodeMap, + sp: Span, + lvl: Level, + lines: codemap::FileLinesResult) + -> io::Result<()> + { + let lines = match lines { + Ok(lines) => lines, + Err(_) => { + try!(write!(&mut self.dst, "(internal compiler error: unprintable span)\n")); + return Ok(()); + } + }; + + let fm = &*lines.file; + + let line_strings: Option> = + lines.lines.iter() + .map(|info| fm.get_line(info.line_index)) + .collect(); + + let line_strings = match line_strings { + None => { return Ok(()); } + Some(line_strings) => line_strings + }; + + // Display only the first MAX_LINES lines. + let all_lines = lines.lines.len(); + let display_lines = cmp::min(all_lines, MAX_LINES); + let display_line_infos = &lines.lines[..display_lines]; + let display_line_strings = &line_strings[..display_lines]; + + // Calculate the widest number to format evenly and fix #11715 + assert!(display_line_infos.len() > 0); + let mut max_line_num = display_line_infos[display_line_infos.len() - 1].line_index + 1; + let mut digits = 0; + while max_line_num > 0 { + max_line_num /= 10; + digits += 1; + } + + // Print the offending lines + for (line_info, line) in display_line_infos.iter().zip(display_line_strings) { + try!(write!(&mut self.dst, "{}:{:>width$} {}\n", + fm.name, + line_info.line_index + 1, + line, + width=digits)); + } + + // If we elided something, put an ellipsis. + if display_lines < all_lines { + let last_line_index = display_line_infos.last().unwrap().line_index; + let s = format!("{}:{} ", fm.name, last_line_index + 1); + try!(write!(&mut self.dst, "{0:1$}...\n", "", s.len())); + } + + // FIXME (#3260) + // If there's one line at fault we can easily point to the problem + if lines.lines.len() == 1 { + let lo = cm.lookup_char_pos(sp.lo); + let mut digits = 0; + let mut num = (lines.lines[0].line_index + 1) / 10; + + // how many digits must be indent past? + while num > 0 { num /= 10; digits += 1; } + + let mut s = String::new(); + // Skip is the number of characters we need to skip because they are + // part of the 'filename:line ' part of the previous line. + let skip = fm.name.chars().count() + digits + 3; + for _ in 0..skip { + s.push(' '); + } + if let Some(orig) = fm.get_line(lines.lines[0].line_index) { + let mut col = skip; + let mut lastc = ' '; + let mut iter = orig.chars().enumerate(); + for (pos, ch) in iter.by_ref() { + lastc = ch; + if pos >= lo.col.to_usize() { break; } + // Whenever a tab occurs on the previous line, we insert one on + // the error-point-squiggly-line as well (instead of a space). + // That way the squiggly line will usually appear in the correct + // position. + match ch { + '\t' => { + col += 8 - col%8; + s.push('\t'); + }, + _ => { + col += 1; + s.push(' '); + }, + } + } + + try!(write!(&mut self.dst, "{}", s)); + let mut s = String::from("^"); + let count = match lastc { + // Most terminals have a tab stop every eight columns by default + '\t' => 8 - col%8, + _ => 1, + }; + col += count; + s.extend(::std::iter::repeat('~').take(count)); + + let hi = cm.lookup_char_pos(sp.hi); + if hi.col != lo.col { + for (pos, ch) in iter { + if pos >= hi.col.to_usize() { break; } + let count = match ch { + '\t' => 8 - col%8, + _ => 1, + }; + col += count; + s.extend(::std::iter::repeat('~').take(count)); + } + } + + if s.len() > 1 { + // One extra squiggly is replaced by a "^" + s.pop(); + } + + try!(self.print_maybe_styled(&format!("{}\n", s), + term::attr::ForegroundColor(lvl.color()))); + } + } + Ok(()) + } + + /// Here are the differences between this and the normal `highlight_lines`: + /// `end_highlight_lines` will always put arrow on the last byte of the + /// span (instead of the first byte). Also, when the span is too long (more + /// than 6 lines), `end_highlight_lines` will print the first line, then + /// dot dot dot, then last line, whereas `highlight_lines` prints the first + /// six lines. + #[allow(deprecated)] + fn end_highlight_lines(&mut self, + cm: &codemap::CodeMap, + sp: Span, + lvl: Level, + lines: codemap::FileLinesResult) + -> io::Result<()> { + let lines = match lines { + Ok(lines) => lines, + Err(_) => { + try!(write!(&mut self.dst, "(internal compiler error: unprintable span)\n")); + return Ok(()); + } + }; + + let fm = &*lines.file; + + let lines = &lines.lines[..]; + if lines.len() > MAX_LINES { + if let Some(line) = fm.get_line(lines[0].line_index) { + try!(write!(&mut self.dst, "{}:{} {}\n", fm.name, + lines[0].line_index + 1, line)); + } + try!(write!(&mut self.dst, "...\n")); + let last_line_index = lines[lines.len() - 1].line_index; + if let Some(last_line) = fm.get_line(last_line_index) { + try!(write!(&mut self.dst, "{}:{} {}\n", fm.name, + last_line_index + 1, last_line)); + } + } else { + for line_info in lines { + if let Some(line) = fm.get_line(line_info.line_index) { + try!(write!(&mut self.dst, "{}:{} {}\n", fm.name, + line_info.line_index + 1, line)); + } + } + } + let last_line_start = format!("{}:{} ", fm.name, lines[lines.len()-1].line_index + 1); + let hi = cm.lookup_char_pos(sp.hi); + let skip = last_line_start.chars().count(); + let mut s = String::new(); + for _ in 0..skip { + s.push(' '); + } + if let Some(orig) = fm.get_line(lines[0].line_index) { + let iter = orig.chars().enumerate(); + for (pos, ch) in iter { + // Span seems to use half-opened interval, so subtract 1 + if pos >= hi.col.to_usize() - 1 { break; } + // Whenever a tab occurs on the previous line, we insert one on + // the error-point-squiggly-line as well (instead of a space). + // That way the squiggly line will usually appear in the correct + // position. + match ch { + '\t' => s.push('\t'), + _ => s.push(' '), + } + } + } + s.push('^'); + s.push('\n'); + self.print_maybe_styled(&s[..], + term::attr::ForegroundColor(lvl.color())) + } + + fn print_macro_backtrace(&mut self, + cm: &codemap::CodeMap, + sp: Span) + -> io::Result<()> { + let cs = try!(cm.with_expn_info(sp.expn_id, |expn_info| -> io::Result<_> { + match expn_info { + Some(ei) => { + let ss = ei.callee.span.map_or(String::new(), + |span| cm.span_to_string(span)); + let (pre, post) = match ei.callee.format { + codemap::MacroAttribute => ("#[", "]"), + codemap::MacroBang => ("", "!"), + codemap::CompilerExpansion => ("", ""), + }; + try!(self.print_diagnostic(&ss, Note, + &format!("in expansion of {}{}{}", + pre, + ei.callee.name, + post), + None)); + let ss = cm.span_to_string(ei.call_site); + try!(self.print_diagnostic(&ss, Note, "expansion site", None)); + Ok(Some(ei.call_site)) + } + None => Ok(None) + } + })); + cs.map_or(Ok(()), |call_site| self.print_macro_backtrace(cm, call_site)) + } } #[cfg(unix)] @@ -442,11 +771,11 @@ impl Emitter for EmitterWriter { cmsp: Option<(&codemap::CodeMap, Span)>, msg: &str, code: Option<&str>, lvl: Level) { let error = match cmsp { - Some((cm, COMMAND_LINE_SP)) => emit(self, cm, + Some((cm, COMMAND_LINE_SP)) => self.emit_(cm, FileLine(COMMAND_LINE_SP), msg, code, lvl), - Some((cm, sp)) => emit(self, cm, FullSpan(sp), msg, code, lvl), - None => print_diagnostic(self, "", lvl, msg, code), + Some((cm, sp)) => self.emit_(cm, FullSpan(sp), msg, code, lvl), + None => self.print_diagnostic("", lvl, msg, code), }; match error { @@ -457,346 +786,13 @@ impl Emitter for EmitterWriter { fn custom_emit(&mut self, cm: &codemap::CodeMap, sp: RenderSpan, msg: &str, lvl: Level) { - match emit(self, cm, sp, msg, None, lvl) { + match self.emit_(cm, sp, msg, None, lvl) { Ok(()) => {} Err(e) => panic!("failed to print diagnostics: {:?}", e), } } } -fn emit(dst: &mut EmitterWriter, cm: &codemap::CodeMap, rsp: RenderSpan, - msg: &str, code: Option<&str>, lvl: Level) -> io::Result<()> { - let sp = rsp.span(); - - // We cannot check equality directly with COMMAND_LINE_SP - // since PartialEq is manually implemented to ignore the ExpnId - let ss = if sp.expn_id == COMMAND_LINE_EXPN { - "".to_string() - } else if let EndSpan(_) = rsp { - let span_end = Span { lo: sp.hi, hi: sp.hi, expn_id: sp.expn_id}; - cm.span_to_string(span_end) - } else { - cm.span_to_string(sp) - }; - - try!(print_diagnostic(dst, &ss[..], lvl, msg, code)); - - match rsp { - FullSpan(_) => { - try!(highlight_lines(dst, cm, sp, lvl, cm.span_to_lines(sp))); - try!(print_macro_backtrace(dst, cm, sp)); - } - EndSpan(_) => { - try!(end_highlight_lines(dst, cm, sp, lvl, cm.span_to_lines(sp))); - try!(print_macro_backtrace(dst, cm, sp)); - } - Suggestion(_, ref suggestion) => { - try!(highlight_suggestion(dst, cm, sp, suggestion)); - try!(print_macro_backtrace(dst, cm, sp)); - } - FileLine(..) => { - // no source text in this case! - } - } - - match code { - Some(code) => - match dst.registry.as_ref().and_then(|registry| registry.find_description(code)) { - Some(_) => { - try!(print_diagnostic(dst, &ss[..], Help, - &format!("run `rustc --explain {}` to see a detailed \ - explanation", code), None)); - } - None => () - }, - None => (), - } - Ok(()) -} - -fn highlight_suggestion(err: &mut EmitterWriter, - cm: &codemap::CodeMap, - sp: Span, - suggestion: &str) - -> io::Result<()> -{ - let lines = cm.span_to_lines(sp).unwrap(); - assert!(!lines.lines.is_empty()); - - // To build up the result, we want to take the snippet from the first - // line that precedes the span, prepend that with the suggestion, and - // then append the snippet from the last line that trails the span. - let fm = &lines.file; - - let first_line = &lines.lines[0]; - let prefix = fm.get_line(first_line.line_index) - .map(|l| &l[..first_line.start_col.0]) - .unwrap_or(""); - - let last_line = lines.lines.last().unwrap(); - let suffix = fm.get_line(last_line.line_index) - .map(|l| &l[last_line.end_col.0..]) - .unwrap_or(""); - - let complete = format!("{}{}{}", prefix, suggestion, suffix); - - // print the suggestion without any line numbers, but leave - // space for them. This helps with lining up with previous - // snippets from the actual error being reported. - let fm = &*lines.file; - let mut lines = complete.lines(); - for (line, line_index) in lines.by_ref().take(MAX_LINES).zip(first_line.line_index..) { - let elided_line_num = format!("{}", line_index+1); - try!(write!(&mut err.dst, "{0}:{1:2$} {3}\n", - fm.name, "", elided_line_num.len(), line)); - } - - // if we elided some lines, add an ellipsis - if lines.next().is_some() { - let elided_line_num = format!("{}", first_line.line_index + MAX_LINES + 1); - try!(write!(&mut err.dst, "{0:1$} {0:2$} ...\n", - "", fm.name.len(), elided_line_num.len())); - } - - Ok(()) -} - -fn highlight_lines(err: &mut EmitterWriter, - cm: &codemap::CodeMap, - sp: Span, - lvl: Level, - lines: codemap::FileLinesResult) - -> io::Result<()> -{ - let lines = match lines { - Ok(lines) => lines, - Err(_) => { - try!(write!(&mut err.dst, "(internal compiler error: unprintable span)\n")); - return Ok(()); - } - }; - - let fm = &*lines.file; - - let line_strings: Option> = - lines.lines.iter() - .map(|info| fm.get_line(info.line_index)) - .collect(); - - let line_strings = match line_strings { - None => { return Ok(()); } - Some(line_strings) => line_strings - }; - - // Display only the first MAX_LINES lines. - let all_lines = lines.lines.len(); - let display_lines = cmp::min(all_lines, MAX_LINES); - let display_line_infos = &lines.lines[..display_lines]; - let display_line_strings = &line_strings[..display_lines]; - - // Calculate the widest number to format evenly and fix #11715 - assert!(display_line_infos.len() > 0); - let mut max_line_num = display_line_infos[display_line_infos.len() - 1].line_index + 1; - let mut digits = 0; - while max_line_num > 0 { - max_line_num /= 10; - digits += 1; - } - - // Print the offending lines - for (line_info, line) in display_line_infos.iter().zip(display_line_strings) { - try!(write!(&mut err.dst, "{}:{:>width$} {}\n", - fm.name, - line_info.line_index + 1, - line, - width=digits)); - } - - // If we elided something, put an ellipsis. - if display_lines < all_lines { - let last_line_index = display_line_infos.last().unwrap().line_index; - let s = format!("{}:{} ", fm.name, last_line_index + 1); - try!(write!(&mut err.dst, "{0:1$}...\n", "", s.len())); - } - - // FIXME (#3260) - // If there's one line at fault we can easily point to the problem - if lines.lines.len() == 1 { - let lo = cm.lookup_char_pos(sp.lo); - let mut digits = 0; - let mut num = (lines.lines[0].line_index + 1) / 10; - - // how many digits must be indent past? - while num > 0 { num /= 10; digits += 1; } - - let mut s = String::new(); - // Skip is the number of characters we need to skip because they are - // part of the 'filename:line ' part of the previous line. - let skip = fm.name.chars().count() + digits + 3; - for _ in 0..skip { - s.push(' '); - } - if let Some(orig) = fm.get_line(lines.lines[0].line_index) { - let mut col = skip; - let mut lastc = ' '; - let mut iter = orig.chars().enumerate(); - for (pos, ch) in iter.by_ref() { - lastc = ch; - if pos >= lo.col.to_usize() { break; } - // Whenever a tab occurs on the previous line, we insert one on - // the error-point-squiggly-line as well (instead of a space). - // That way the squiggly line will usually appear in the correct - // position. - match ch { - '\t' => { - col += 8 - col%8; - s.push('\t'); - }, - _ => { - col += 1; - s.push(' '); - }, - } - } - - try!(write!(&mut err.dst, "{}", s)); - let mut s = String::from("^"); - let count = match lastc { - // Most terminals have a tab stop every eight columns by default - '\t' => 8 - col%8, - _ => 1, - }; - col += count; - s.extend(::std::iter::repeat('~').take(count)); - - let hi = cm.lookup_char_pos(sp.hi); - if hi.col != lo.col { - for (pos, ch) in iter { - if pos >= hi.col.to_usize() { break; } - let count = match ch { - '\t' => 8 - col%8, - _ => 1, - }; - col += count; - s.extend(::std::iter::repeat('~').take(count)); - } - } - - if s.len() > 1 { - // One extra squiggly is replaced by a "^" - s.pop(); - } - - try!(print_maybe_styled(err, - &format!("{}\n", s), - term::attr::ForegroundColor(lvl.color()))); - } - } - Ok(()) -} - -/// Here are the differences between this and the normal `highlight_lines`: -/// `end_highlight_lines` will always put arrow on the last byte of the -/// span (instead of the first byte). Also, when the span is too long (more -/// than 6 lines), `end_highlight_lines` will print the first line, then -/// dot dot dot, then last line, whereas `highlight_lines` prints the first -/// six lines. -#[allow(deprecated)] -fn end_highlight_lines(w: &mut EmitterWriter, - cm: &codemap::CodeMap, - sp: Span, - lvl: Level, - lines: codemap::FileLinesResult) - -> io::Result<()> { - let lines = match lines { - Ok(lines) => lines, - Err(_) => { - try!(write!(&mut w.dst, "(internal compiler error: unprintable span)\n")); - return Ok(()); - } - }; - - let fm = &*lines.file; - - let lines = &lines.lines[..]; - if lines.len() > MAX_LINES { - if let Some(line) = fm.get_line(lines[0].line_index) { - try!(write!(&mut w.dst, "{}:{} {}\n", fm.name, - lines[0].line_index + 1, line)); - } - try!(write!(&mut w.dst, "...\n")); - let last_line_index = lines[lines.len() - 1].line_index; - if let Some(last_line) = fm.get_line(last_line_index) { - try!(write!(&mut w.dst, "{}:{} {}\n", fm.name, - last_line_index + 1, last_line)); - } - } else { - for line_info in lines { - if let Some(line) = fm.get_line(line_info.line_index) { - try!(write!(&mut w.dst, "{}:{} {}\n", fm.name, - line_info.line_index + 1, line)); - } - } - } - let last_line_start = format!("{}:{} ", fm.name, lines[lines.len()-1].line_index + 1); - let hi = cm.lookup_char_pos(sp.hi); - let skip = last_line_start.chars().count(); - let mut s = String::new(); - for _ in 0..skip { - s.push(' '); - } - if let Some(orig) = fm.get_line(lines[0].line_index) { - let iter = orig.chars().enumerate(); - for (pos, ch) in iter { - // Span seems to use half-opened interval, so subtract 1 - if pos >= hi.col.to_usize() - 1 { break; } - // Whenever a tab occurs on the previous line, we insert one on - // the error-point-squiggly-line as well (instead of a space). - // That way the squiggly line will usually appear in the correct - // position. - match ch { - '\t' => s.push('\t'), - _ => s.push(' '), - } - } - } - s.push('^'); - s.push('\n'); - print_maybe_styled(w, - &s[..], - term::attr::ForegroundColor(lvl.color())) -} - -fn print_macro_backtrace(w: &mut EmitterWriter, - cm: &codemap::CodeMap, - sp: Span) - -> io::Result<()> { - let cs = try!(cm.with_expn_info(sp.expn_id, |expn_info| -> io::Result<_> { - match expn_info { - Some(ei) => { - let ss = ei.callee.span.map_or(String::new(), - |span| cm.span_to_string(span)); - let (pre, post) = match ei.callee.format { - codemap::MacroAttribute => ("#[", "]"), - codemap::MacroBang => ("", "!"), - codemap::CompilerExpansion => ("", ""), - }; - try!(print_diagnostic(w, &ss, Note, - &format!("in expansion of {}{}{}", - pre, - ei.callee.name, - post), - None)); - let ss = cm.span_to_string(ei.call_site); - try!(print_diagnostic(w, &ss, Note, "expansion site", None)); - Ok(Some(ei.call_site)) - } - None => Ok(None) - } - })); - cs.map_or(Ok(()), |call_site| print_macro_backtrace(w, cm, call_site)) -} - pub fn expect(diag: &SpanHandler, opt: Option, msg: M) -> T where M: FnOnce() -> String, { @@ -808,7 +804,7 @@ pub fn expect(diag: &SpanHandler, opt: Option, msg: M) -> T where #[cfg(test)] mod test { - use super::{EmitterWriter, highlight_lines, Level}; + use super::{EmitterWriter, Level}; use codemap::{mk_sp, CodeMap, BytePos}; use std::sync::{Arc, Mutex}; use std::io::{self, Write}; @@ -854,7 +850,7 @@ mod test { println!("span_to_lines"); let lines = cm.span_to_lines(sp); println!("highlight_lines"); - highlight_lines(&mut ew, &cm, sp, lvl, lines).unwrap(); + ew.highlight_lines(&cm, sp, lvl, lines).unwrap(); println!("done"); let vec = data.lock().unwrap().clone(); let vec: &[u8] = &vec;