From 11dc974a38fd533aa692cea213305056cd3a6902 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 20 Apr 2016 14:56:01 -0400 Subject: [PATCH] refactor to use new snippet code and model Major changes: - Remove old snippet rendering code and use the new stuff. - Introduce `span_label` method to add a label - Remove EndSpan mode and replace with a fn to get the last character of a span. - Stop using `Option` and just use an empty `MultiSpan` - and probably a bunch of other stuff :) --- src/librustc/session/mod.rs | 4 +- src/librustc_driver/lib.rs | 16 +- src/librustc_driver/test.rs | 4 - src/librustc_trans/back/write.rs | 19 +- src/libsyntax/codemap.rs | 87 +--- src/libsyntax/errors/emitter.rs | 796 +++++++++---------------------- src/libsyntax/errors/mod.rs | 175 ++++--- 7 files changed, 357 insertions(+), 744 deletions(-) diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 5b78e4de18b57..edb1c4530c240 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -567,7 +567,7 @@ pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! { } config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()), }; - emitter.emit(None, msg, None, errors::Level::Fatal); + emitter.emit(&MultiSpan::new(), msg, None, errors::Level::Fatal); panic!(errors::FatalError); } @@ -578,7 +578,7 @@ pub fn early_warn(output: config::ErrorOutputType, msg: &str) { } config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()), }; - emitter.emit(None, msg, None, errors::Level::Warning); + emitter.emit(&MultiSpan::new(), msg, None, errors::Level::Warning); } // Err(0) means compilation was stopped, but no errors were found. diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 2d3363507d06c..52306e388e235 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -91,8 +91,9 @@ use std::thread; use rustc::session::early_error; -use syntax::{ast, errors, diagnostics}; -use syntax::codemap::{CodeMap, FileLoader, RealFileLoader}; +use syntax::{ast, errors, diagnostic}; +use syntax::codemap::MultiSpan; +use syntax::parse::{self, PResult}; use syntax::errors::emitter::Emitter; use syntax::feature_gate::{GatedCfg, UnstableFeatures}; use syntax::parse::{self, PResult, token}; @@ -136,7 +137,8 @@ pub fn run(args: Vec) -> isize { None => { let mut emitter = errors::emitter::BasicEmitter::stderr(errors::ColorConfig::Auto); - emitter.emit(None, &abort_msg(err_count), None, errors::Level::Fatal); + emitter.emit(&MultiSpan::new(), &abort_msg(err_count), None, + errors::Level::Fatal); exit_on_err(); } } @@ -379,7 +381,7 @@ fn check_cfg(sopts: &config::Options, match item.node { ast::MetaItemKind::List(ref pred, _) => { saw_invalid_predicate = true; - emitter.emit(None, + emitter.emit(&MultiSpan::new(), &format!("invalid predicate in --cfg command line argument: `{}`", pred), None, @@ -1028,19 +1030,19 @@ pub fn monitor(f: F) { // a .span_bug or .bug call has already printed what // it wants to print. if !value.is::() { - emitter.emit(None, "unexpected panic", None, errors::Level::Bug); + emitter.emit(&MultiSpan::new(), "unexpected panic", None, errors::Level::Bug); } let xs = ["the compiler unexpectedly panicked. this is a bug.".to_string(), format!("we would appreciate a bug report: {}", BUG_REPORT_URL)]; for note in &xs { - emitter.emit(None, ¬e[..], None, errors::Level::Note) + emitter.emit(&MultiSpan::new(), ¬e[..], None, errors::Level::Note) } if match env::var_os("RUST_BACKTRACE") { Some(val) => &val != "0", None => false, } { - emitter.emit(None, + emitter.emit(&MultiSpan::new(), "run with `RUST_BACKTRACE=1` for a backtrace", None, errors::Level::Note); diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index ce92dd158c969..60f4ab1c95f2b 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -86,10 +86,6 @@ impl Emitter for ExpectErrorEmitter { lvl: Level) { remove_message(self, msg, lvl); } - - fn custom_emit(&mut self, _sp: &RenderSpan, msg: &str, lvl: Level) { - remove_message(self, msg, lvl); - } } fn errors(msgs: &[&str]) -> (Box, usize) { diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 8a915f044053a..50fd039276253 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -19,7 +19,7 @@ use llvm::SMDiagnosticRef; use {CrateTranslation, ModuleTranslation}; use util::common::time; use util::common::path2cstr; -use syntax::codemap; +use syntax::codemap::{self, MultiSpan}; use syntax::errors::{self, Handler, Level}; use syntax::errors::emitter::Emitter; @@ -84,13 +84,13 @@ impl SharedEmitter { for diag in &*buffer { match diag.code { Some(ref code) => { - handler.emit_with_code(None, + handler.emit_with_code(&MultiSpan::new(), &diag.msg, &code[..], diag.lvl); }, None => { - handler.emit(None, + handler.emit(&MultiSpan::new(), &diag.msg, diag.lvl); }, @@ -101,9 +101,12 @@ impl SharedEmitter { } impl Emitter for SharedEmitter { - fn emit(&mut self, sp: Option<&codemap::MultiSpan>, - msg: &str, code: Option<&str>, lvl: Level) { - assert!(sp.is_none(), "SharedEmitter doesn't support spans"); + fn emit(&mut self, + sp: &codemap::MultiSpan, + msg: &str, + code: Option<&str>, + lvl: Level) { + assert!(sp.primary_span().is_none(), "SharedEmitter doesn't support spans"); self.buffer.lock().unwrap().push(Diagnostic { msg: msg.to_string(), @@ -112,8 +115,8 @@ impl Emitter for SharedEmitter { }); } - fn custom_emit(&mut self, _sp: &errors::RenderSpan, _msg: &str, _lvl: Level) { - bug!("SharedEmitter doesn't support custom_emit"); + fn emit_struct(&mut self, _db: &errors::DiagnosticBuilder) { + bug!("SharedEmitter doesn't support emit_struct"); } } diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 228af27f4b10a..5862538de2e04 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -163,6 +163,12 @@ pub const COMMAND_LINE_SP: Span = Span { lo: BytePos(0), expn_id: COMMAND_LINE_EXPN }; impl Span { + /// Returns a new span representing just the end-point of this span + pub fn end_point(self) -> Span { + let lo = cmp::max(self.hi.0 - 1, self.lo.0); + Span { lo: BytePos(lo), hi: self.hi, expn_id: self.expn_id} + } + /// Returns `self` if `self` is not the dummy span, and `other` otherwise. pub fn substitute_dummy(self, other: Span) -> Span { if self.source_equal(&DUMMY_SP) { other } else { self } @@ -794,7 +800,7 @@ impl CodeMap { /// Creates a new filemap and sets its line information. pub fn new_filemap_and_lines(&self, filename: &str, src: &str) -> Rc { let fm = self.new_filemap(filename.to_string(), src.to_owned()); - let mut byte_pos: u32 = 0; + let mut byte_pos: u32 = fm.start_pos.0; for line in src.lines() { // register the start of this line fm.next_line(BytePos(byte_pos)); @@ -1126,7 +1132,9 @@ impl CodeMap { // numbers in Loc are 1-based, so we subtract 1 to get 0-based // lines. for line_index in lo.line-1 .. hi.line-1 { - let line_len = lo.file.get_line(line_index).map(|s| s.len()).unwrap_or(0); + let line_len = lo.file.get_line(line_index) + .map(|s| s.chars().count()) + .unwrap_or(0); lines.push(LineInfo { line_index: line_index, start_col: start_col, end_col: CharPos::from_usize(line_len) }); @@ -1584,13 +1592,13 @@ mod tests { assert_eq!(file_lines.lines[0].line_index, 1); } - /// Given a string like " ^~~~~~~~~~~~ ", produces a span + /// Given a string like " ~~~~~~~~~~~~ ", produces a span /// coverting that range. The idea is that the string has the same /// length as the input, and we uncover the byte positions. Note /// that this can span lines and so on. fn span_from_selection(input: &str, selection: &str) -> Span { assert_eq!(input.len(), selection.len()); - let left_index = selection.find('^').unwrap() as u32; + let left_index = selection.find('~').unwrap() as u32; let right_index = selection.rfind('~').map(|x|x as u32).unwrap_or(left_index); Span { lo: BytePos(left_index), hi: BytePos(right_index + 1), expn_id: NO_EXPANSION } } @@ -1601,7 +1609,7 @@ mod tests { fn span_to_snippet_and_lines_spanning_multiple_lines() { let cm = CodeMap::new(); let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n"; - let selection = " \n ^~\n~~~\n~~~~~ \n \n"; + let selection = " \n ~~\n~~~\n~~~~~ \n \n"; cm.new_filemap_and_lines("blork.rs", inputtext); let span = span_from_selection(inputtext, selection); @@ -1751,73 +1759,4 @@ r"blork2.rs:2:1: 2:12 "; assert_eq!(sstr, res_str); } - - #[test] - fn t13() { - // Test that collecting multiple spans into line-groups works correctly - let cm = CodeMap::new(); - let inp = "_aaaaa__bbb\nvv\nw\nx\ny\nz\ncccccc__ddddee__"; - let sp1 = " ^~~~~ \n \n \n \n \n \n "; - let sp2 = " \n \n \n \n \n^\n "; - let sp3 = " ^~~\n~~\n \n \n \n \n "; - let sp4 = " \n \n \n \n \n \n^~~~~~ "; - let sp5 = " \n \n \n \n \n \n ^~~~ "; - let sp6 = " \n \n \n \n \n \n ^~~~ "; - let sp_trim = " \n \n \n \n \n \n ^~ "; - let sp_merge = " \n \n \n \n \n \n ^~~~~~ "; - let sp7 = " \n ^\n \n \n \n \n "; - let sp8 = " \n \n^\n \n \n \n "; - let sp9 = " \n \n \n^\n \n \n "; - let sp10 = " \n \n \n \n^\n \n "; - - let span = |sp, expected| { - let sp = span_from_selection(inp, sp); - assert_eq!(&cm.span_to_snippet(sp).unwrap(), expected); - sp - }; - - cm.new_filemap_and_lines("blork.rs", inp); - let sp1 = span(sp1, "aaaaa"); - let sp2 = span(sp2, "z"); - let sp3 = span(sp3, "bbb\nvv"); - let sp4 = span(sp4, "cccccc"); - let sp5 = span(sp5, "dddd"); - let sp6 = span(sp6, "ddee"); - let sp7 = span(sp7, "v"); - let sp8 = span(sp8, "w"); - let sp9 = span(sp9, "x"); - let sp10 = span(sp10, "y"); - let sp_trim = span(sp_trim, "ee"); - let sp_merge = span(sp_merge, "ddddee"); - - let spans = vec![sp5, sp2, sp4, sp9, sp10, sp7, sp3, sp8, sp1, sp6]; - - macro_rules! check_next { - ($groups: expr, $expected: expr) => ({ - let actual = $groups.next().map(|g|&g.spans[..]); - let expected = $expected; - println!("actual:\n{:?}\n", actual); - println!("expected:\n{:?}\n", expected); - assert_eq!(actual, expected.as_ref().map(|x|&x[..])); - }); - } - - let _groups = cm.group_spans(spans.clone()); - let it = &mut _groups.iter(); - - check_next!(it, Some([sp1, sp7, sp8, sp9, sp10, sp2])); - // New group because we're exceeding MAX_HIGHLIGHT_LINES - check_next!(it, Some([sp4, sp_merge])); - check_next!(it, Some([sp3])); - check_next!(it, None::<[Span; 0]>); - - let _groups = cm.end_group_spans(spans); - let it = &mut _groups.iter(); - - check_next!(it, Some([sp1, sp7, sp8, sp9, sp10, sp2])); - // New group because we're exceeding MAX_HIGHLIGHT_LINES - check_next!(it, Some([sp4, sp5, sp_trim])); - check_next!(it, Some([sp3])); - check_next!(it, None::<[Span; 0]>); - } } diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs index 0b5234769b219..e963a5f794cb6 100644 --- a/src/libsyntax/errors/emitter.rs +++ b/src/libsyntax/errors/emitter.rs @@ -16,6 +16,7 @@ use diagnostics; use errors::{Level, RenderSpan, CodeSuggestion, DiagnosticBuilder}; use errors::RenderSpan::*; use errors::Level::*; +use errors::snippet::{RenderedLineKind, SnippetData, Style}; use std::{cmp, fmt}; use std::io::prelude::*; @@ -24,27 +25,15 @@ use std::rc::Rc; use term; pub trait Emitter { - fn emit(&mut self, span: Option<&MultiSpan>, msg: &str, code: Option<&str>, lvl: Level); - fn custom_emit(&mut self, sp: &RenderSpan, msg: &str, lvl: Level); + fn emit(&mut self, span: &MultiSpan, msg: &str, code: Option<&str>, lvl: Level); /// Emit a structured diagnostic. - fn emit_struct(&mut self, db: &DiagnosticBuilder) { - self.emit(db.span.as_ref(), &db.message, db.code.as_ref().map(|s| &**s), db.level); - for child in &db.children { - match child.render_span { - Some(ref sp) => self.custom_emit(sp, &child.message, child.level), - None => self.emit(child.span.as_ref(), &child.message, None, child.level), - } - } - } + fn emit_struct(&mut self, db: &DiagnosticBuilder); } /// maximum number of lines we will print for each error; arbitrary. pub const MAX_HIGHLIGHT_LINES: usize = 6; -/// maximum number of lines we will print for each span; arbitrary. -const MAX_SP_LINES: usize = 6; - #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ColorConfig { Auto, @@ -70,19 +59,23 @@ pub struct BasicEmitter { impl Emitter for BasicEmitter { fn emit(&mut self, - msp: Option<&MultiSpan>, + msp: &MultiSpan, msg: &str, code: Option<&str>, lvl: Level) { - assert!(msp.is_none(), "BasicEmitter can't handle spans"); + assert!(msp.primary_span().is_none(), "BasicEmitter can't handle spans"); + if let Err(e) = print_diagnostic(&mut self.dst, "", lvl, msg, code) { panic!("failed to print diagnostics: {:?}", e); } - } - fn custom_emit(&mut self, _: &RenderSpan, _: &str, _: Level) { - panic!("BasicEmitter can't handle custom_emit"); + fn emit_struct(&mut self, db: &DiagnosticBuilder) { + self.emit(&db.span, &db.message, db.code.as_ref().map(|s| &**s), db.level); + for child in &db.children { + assert!(child.render_span.is_none(), "BasicEmitter can't handle spans"); + self.emit(&child.span, &child.message, None, child.level); + } } } @@ -101,33 +94,31 @@ pub struct EmitterWriter { dst: Destination, registry: Option, cm: Rc, + first: bool, } impl Emitter for EmitterWriter { fn emit(&mut self, - msp: Option<&MultiSpan>, + msp: &MultiSpan, msg: &str, code: Option<&str>, lvl: Level) { - let error = match msp.map(|s|(s.to_span_bounds(), s)) { - Some((COMMAND_LINE_SP, msp)) => { - self.emit_(&FileLine(msp.clone()), msg, code, lvl) - }, - Some((DUMMY_SP, _)) | None => print_diagnostic(&mut self.dst, "", lvl, msg, code), - Some((_, msp)) => self.emit_(&FullSpan(msp.clone()), msg, code, lvl), - }; - - if let Err(e) = error { - panic!("failed to print diagnostics: {:?}", e); - } + self.emit_multispan(msp, msg, code, lvl, true); } - fn custom_emit(&mut self, - rsp: &RenderSpan, - msg: &str, - lvl: Level) { - if let Err(e) = self.emit_(rsp, msg, None, lvl) { - panic!("failed to print diagnostics: {:?}", e); + fn emit_struct(&mut self, db: &DiagnosticBuilder) { + self.emit_multispan(&db.span, &db.message, + db.code.as_ref().map(|s| &**s), db.level, true); + + for child in &db.children { + match child.render_span { + Some(ref sp) => + self.emit_renderspan(sp, &child.message, + child.level), + None => + self.emit_multispan(&child.span, + &child.message, None, child.level, false), + } } } } @@ -153,9 +144,10 @@ impl EmitterWriter { -> EmitterWriter { if color_config.use_color() { let dst = Destination::from_stderr(); - EmitterWriter { dst: dst, registry: registry, cm: code_map } + EmitterWriter { dst: dst, registry: registry, cm: code_map, first: true } } else { - EmitterWriter { dst: Raw(Box::new(io::stderr())), registry: registry, cm: code_map } + EmitterWriter { dst: Raw(Box::new(io::stderr())), + registry: registry, cm: code_map, first: true } } } @@ -163,7 +155,49 @@ impl EmitterWriter { registry: Option, code_map: Rc) -> EmitterWriter { - EmitterWriter { dst: Raw(dst), registry: registry, cm: code_map } + EmitterWriter { dst: Raw(dst), registry: registry, cm: code_map, first: true } + } + + fn emit_multispan(&mut self, + span: &MultiSpan, + msg: &str, + code: Option<&str>, + lvl: Level, + is_header: bool) { + if is_header { + if self.first { + self.first = false; + } else { + match write!(self.dst, "\n") { + Ok(_) => { } + Err(e) => { + panic!("failed to print diagnostics: {:?}", e) + } + } + } + } + + let error = match span.primary_span() { + Some(COMMAND_LINE_SP) => { + self.emit_(&FileLine(span.clone()), msg, code, lvl) + } + Some(DUMMY_SP) | None => { + print_diagnostic(&mut self.dst, "", lvl, msg, code) + } + Some(_) => { + self.emit_(&FullSpan(span.clone()), msg, code, lvl) + } + }; + + if let Err(e) = error { + panic!("failed to print diagnostics: {:?}", e); + } + } + + fn emit_renderspan(&mut self, sp: &RenderSpan, msg: &str, lvl: Level) { + if let Err(e) = self.emit_(sp, msg, None, lvl) { + panic!("failed to print diagnostics: {:?}", e); + } } fn emit_(&mut self, @@ -173,51 +207,43 @@ impl EmitterWriter { lvl: Level) -> io::Result<()> { let msp = rsp.span(); - let bounds = msp.to_span_bounds(); - - let ss = if bounds == COMMAND_LINE_SP { - "".to_string() - } else if let EndSpan(_) = *rsp { - let span_end = Span { lo: bounds.hi, hi: bounds.hi, expn_id: bounds.expn_id}; - self.cm.span_to_string(span_end) - } else { - self.cm.span_to_string(bounds) - }; - - print_diagnostic(&mut self.dst, &ss[..], lvl, msg, code)?; + let primary_span = msp.primary_span(); + + match code { + Some(code) if self.registry.as_ref() + .and_then(|registry| registry.find_description(code)).is_some() => + { + let code_with_explain = String::from("--explain ") + code; + print_diagnostic(&mut self.dst, "", lvl, msg, Some(&code_with_explain))? + } + _ => print_diagnostic(&mut self.dst, "", lvl, msg, code)? + } match *rsp { FullSpan(_) => { self.highlight_lines(msp, lvl)?; - self.print_macro_backtrace(bounds)?; - } - EndSpan(_) => { - self.end_highlight_lines(msp, lvl)?; - self.print_macro_backtrace(bounds)?; + if let Some(primary_span) = primary_span { + self.print_macro_backtrace(primary_span)?; + } } Suggestion(ref suggestion) => { self.highlight_suggestion(suggestion)?; - self.print_macro_backtrace(bounds)?; + if let Some(primary_span) = primary_span { + self.print_macro_backtrace(primary_span)?; + } } FileLine(..) => { // no source text in this case! } } - if let Some(code) = code { - if let Some(_) = self.registry.as_ref() - .and_then(|registry| registry.find_description(code)) { - print_diagnostic(&mut self.dst, &ss[..], Help, - &format!("run `rustc --explain {}` to see a \ - detailed explanation", code), None)?; - } - } Ok(()) } fn highlight_suggestion(&mut self, suggestion: &CodeSuggestion) -> io::Result<()> { - let lines = self.cm.span_to_lines(suggestion.msp.to_span_bounds()).unwrap(); + let primary_span = suggestion.msp.primary_span().unwrap(); + let lines = self.cm.span_to_lines(primary_span).unwrap(); assert!(!lines.lines.is_empty()); let complete = suggestion.splice_lines(&self.cm); @@ -251,325 +277,21 @@ impl EmitterWriter { lvl: Level) -> io::Result<()> { - let lines = match self.cm.span_to_lines(msp.to_span_bounds()) { - Ok(lines) => lines, - Err(_) => { - write!(&mut self.dst, "(internal compiler error: unprintable span)\n")?; - return Ok(()); - } - }; - - let fm = &*lines.file; - if let None = fm.src { - return Ok(()); - } - - let display_line_infos = &lines.lines[..]; - assert!(display_line_infos.len() > 0); - - // Calculate the widest number to format evenly and fix #11715 - let digits = line_num_max_digits(display_line_infos.last().unwrap()); - let first_line_index = display_line_infos.first().unwrap().line_index; - - let skip = fm.name.chars().count() + digits + 2; - - let mut spans = msp.spans.iter().peekable(); - let mut lines = display_line_infos.iter(); - let mut prev_line_index = first_line_index.wrapping_sub(1); - - // Display at most MAX_HIGHLIGHT_LINES lines. - let mut remaining_err_lines = MAX_HIGHLIGHT_LINES; - - // To emit a overflowed spans code-lines *AFTER* the rendered spans - let mut overflowed_buf = String::new(); - let mut overflowed = false; - - // FIXME (#8706) - 'l: loop { - if remaining_err_lines <= 0 { - break; - } - let line = match lines.next() { - Some(l) => l, - None => break, - }; - - // Skip is the number of characters we need to skip because they are - // part of the 'filename:line ' part of the code line. - let mut s: String = ::std::iter::repeat(' ').take(skip).collect(); - let mut col = skip; - let mut lastc = ' '; - - let cur_line_str = fm.get_line(line.line_index).unwrap(); - let mut line_chars = cur_line_str.chars().enumerate().peekable(); - let mut line_spans = 0; - - // Assemble spans for this line - loop { - // Peek here to preserve the span if it doesn't belong to this line - let sp = match spans.peek() { - Some(sp) => **sp, - None => break, - }; - let lo = self.cm.lookup_char_pos(sp.lo); - let hi = self.cm.lookup_char_pos(sp.hi); - let line_num = line.line_index + 1; - - if !(lo.line <= line_num && hi.line >= line_num) { - // This line is not contained in the span - if overflowed { - // Never elide the final line of an overflowed span - prev_line_index = line.line_index - 1; - overflowed = false; - break; - } - - if line_spans == 0 { - continue 'l; - } else { - // This line is finished, now render the spans we've assembled - break; - } - } - spans.next(); - line_spans += 1; - - if lo.line != hi.line { - // Assemble extra code lines to be emitted after this lines spans - // (substract `2` because the first and last line are rendered normally) - let max_lines = cmp::min(remaining_err_lines, MAX_SP_LINES) - 2; - prev_line_index = line.line_index; - let count = cmp::min((hi.line - lo.line - 1), max_lines); - for _ in 0..count { - let line = match lines.next() { - Some(l) => l, - None => break, - }; - let line_str = fm.get_line(line.line_index).unwrap(); - overflowed_buf.push_str(&format!("{}:{:>width$} {}\n", - fm.name, - line.line_index + 1, - line_str, - width=digits)); - remaining_err_lines -= 1; - prev_line_index += 1 - } - // Remember that the span overflowed to ensure - // that we emit its last line exactly once - // (other spans may, or may not, start on it) - overflowed = true; - break; - } - - for (pos, ch) in line_chars.by_ref() { - lastc = ch; - if pos >= lo.col.to_usize() { break; } - // Whenever a tab occurs on the code 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(' '); - }, - } - } - - s.push('^'); - let col_ptr = col; - 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 = self.cm.lookup_char_pos(sp.hi); - if hi.col != lo.col { - let mut chars = line_chars.by_ref(); - loop { - // We peek here to preserve the value for the next span - let (pos, ch) = match chars.peek() { - Some(elem) => *elem, - None => break, - }; - 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)); - - chars.next(); - } - } - if (col - col_ptr) > 0 { - // One extra squiggly is replaced by a "^" - s.pop(); - } - } - - // If we elided something put an ellipsis. - if prev_line_index != line.line_index.wrapping_sub(1) && !overflowed { - write!(&mut self.dst, "{0:1$}...\n", "", skip)?; - } - - // Print offending code-line - remaining_err_lines -= 1; - write!(&mut self.dst, "{}:{:>width$} {}\n", - fm.name, - line.line_index + 1, - cur_line_str, - width=digits)?; - - if s.len() > skip { - // Render the spans we assembled previously (if any). - println_maybe_styled!(&mut self.dst, term::Attr::ForegroundColor(lvl.color()), - "{}", s)?; - } - - if !overflowed_buf.is_empty() { - // Print code-lines trailing the rendered spans (when a span overflows) - write!(&mut self.dst, "{}", &overflowed_buf)?; - overflowed_buf.clear(); - } else { - prev_line_index = line.line_index; - } - } - - // If we elided something, put an ellipsis. - if lines.next().is_some() { - write!(&mut self.dst, "{0:1$}...\n", "", skip)?; - } - Ok(()) - } - - /// Here are the differences between this and the normal `highlight_lines`: - /// `end_highlight_lines` will always put arrow on the last byte of each - /// span (instead of the first byte). Also, when a 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, - msp: &MultiSpan, - lvl: Level) - -> io::Result<()> { - let lines = match self.cm.span_to_lines(msp.to_span_bounds()) { - Ok(lines) => lines, - Err(_) => { - write!(&mut self.dst, "(internal compiler error: unprintable span)\n")?; - return Ok(()); - } - }; - - let fm = &*lines.file; - if let None = fm.src { - return Ok(()); + let mut snippet_data = SnippetData::new(self.cm.clone(), + msp.primary_span()); + for span_label in msp.span_labels() { + snippet_data.push(span_label.span, + span_label.is_primary, + span_label.label); } - - let lines = &lines.lines[..]; - - // Calculate the widest number to format evenly - let first_line = lines.first().unwrap(); - let last_line = lines.last().unwrap(); - let digits = line_num_max_digits(last_line); - - let skip = fm.name.chars().count() + digits + 2; - - let mut spans = msp.spans.iter().peekable(); - let mut lines = lines.iter(); - let mut prev_line_index = first_line.line_index.wrapping_sub(1); - - // Display at most MAX_HIGHLIGHT_LINES lines. - let mut remaining_err_lines = MAX_HIGHLIGHT_LINES; - - 'l: loop { - if remaining_err_lines <= 0 { - break; - } - let line = match lines.next() { - Some(line) => line, - None => break, - }; - - // Skip is the number of characters we need to skip because they are - // part of the 'filename:line ' part of the previous line. - let mut s: String = ::std::iter::repeat(' ').take(skip).collect(); - - let line_str = fm.get_line(line.line_index).unwrap(); - let mut line_chars = line_str.chars().enumerate(); - let mut line_spans = 0; - - loop { - // Peek here to preserve the span if it doesn't belong to this line - let sp = match spans.peek() { - Some(sp) => **sp, - None => break, - }; - let lo = self.cm.lookup_char_pos(sp.lo); - let hi = self.cm.lookup_char_pos(sp.hi); - let elide_sp = (hi.line - lo.line) >= MAX_SP_LINES; - - let line_num = line.line_index + 1; - if !(lo.line <= line_num && hi.line >= line_num) { - // This line is not contained in the span - if line_spans == 0 { - continue 'l; - } else { - // This line is finished, now render the spans we've assembled - break - } - } else if hi.line > line_num { - if elide_sp && lo.line < line_num { - // This line is inbetween the first and last line of the span, - // so we may want to elide it. - continue 'l; - } else { - break - } - } - line_spans += 1; - spans.next(); - - for (pos, ch) in line_chars.by_ref() { - // 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('^'); + let rendered_lines = snippet_data.render_lines(); + for rendered_line in &rendered_lines { + for styled_string in &rendered_line.text { + self.dst.apply_style(lvl, &rendered_line.kind, styled_string.style)?; + write!(&mut self.dst, "{}", styled_string.text)?; + self.dst.reset_attrs()?; } - - if prev_line_index != line.line_index.wrapping_sub(1) { - // If we elided something, put an ellipsis. - write!(&mut self.dst, "{0:1$}...\n", "", skip)?; - } - - // Print offending code-lines - write!(&mut self.dst, "{}:{:>width$} {}\n", fm.name, - line.line_index + 1, line_str, width=digits)?; - remaining_err_lines -= 1; - - if s.len() > skip { - // Render the spans we assembled previously (if any) - println_maybe_styled!(&mut self.dst, term::Attr::ForegroundColor(lvl.color()), - "{}", s)?; - } - prev_line_index = line.line_index; + write!(&mut self.dst, "\n")?; } Ok(()) } @@ -602,6 +324,7 @@ fn line_num_max_digits(line: &codemap::LineInfo) -> usize { digits } + fn print_diagnostic(dst: &mut Destination, topic: &str, lvl: Level, @@ -609,17 +332,22 @@ fn print_diagnostic(dst: &mut Destination, code: Option<&str>) -> io::Result<()> { if !topic.is_empty() { - write!(dst, "{} ", topic)?; - } - - print_maybe_styled!(dst, term::Attr::ForegroundColor(lvl.color()), - "{}: ", lvl.to_string())?; - print_maybe_styled!(dst, term::Attr::Bold, "{}", msg)?; - + dst.start_attr(term::Attr::ForegroundColor(lvl.color()))?; + write!(dst, "{}: ", topic)?; + dst.reset_attrs()?; + } + dst.start_attr(term::Attr::Bold)?; + dst.start_attr(term::Attr::ForegroundColor(lvl.color()))?; + write!(dst, "{}", lvl.to_string())?; + dst.reset_attrs()?; + write!(dst, ": ")?; + dst.start_attr(term::Attr::Bold)?; + write!(dst, "{}", msg)?; if let Some(code) = code { let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA); print_maybe_styled!(dst, style, " [{}]", code.clone())?; } + dst.reset_attrs()?; write!(dst, "\n")?; Ok(()) } @@ -660,6 +388,52 @@ impl Destination { } } + fn apply_style(&mut self, + lvl: Level, + _kind: &RenderedLineKind, + style: Style) + -> io::Result<()> { + match style { + Style::FileNameStyle => { + } + Style::LineAndColumn => { + } + Style::LineNumber => { + self.start_attr(term::Attr::Bold)?; + self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?; + } + Style::Quotation => { + } + Style::UnderlinePrimary | Style::LabelPrimary => { + self.start_attr(term::Attr::Bold)?; + self.start_attr(term::Attr::ForegroundColor(lvl.color()))?; + } + Style::UnderlineSecondary | Style::LabelSecondary => { + self.start_attr(term::Attr::Bold)?; + self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?; + } + Style::NoStyle => { + } + } + Ok(()) + } + + fn start_attr(&mut self, attr: term::Attr) -> io::Result<()> { + match *self { + Terminal(ref mut t) => { t.attr(attr)?; } + Raw(_) => { } + } + Ok(()) + } + + fn reset_attrs(&mut self) -> io::Result<()> { + match *self { + Terminal(ref mut t) => { t.reset()?; } + Raw(_) => { } + } + Ok(()) + } + fn print_maybe_styled(&mut self, args: fmt::Arguments, color: term::Attr, @@ -741,7 +515,7 @@ mod test { /// that this can span lines and so on. fn span_from_selection(input: &str, selection: &str) -> Span { assert_eq!(input.len(), selection.len()); - let left_index = selection.find('^').unwrap() as u32; + let left_index = selection.find('~').unwrap() as u32; let right_index = selection.rfind('~').map(|x|x as u32).unwrap_or(left_index); Span { lo: BytePos(left_index), hi: BytePos(right_index + 1), expn_id: NO_EXPANSION } } @@ -777,12 +551,15 @@ mod test { let vec = data.lock().unwrap().clone(); let vec: &[u8] = &vec; let str = from_utf8(vec).unwrap(); - println!("{}", str); - assert_eq!(str, "dummy.txt: 8 line8\n\ - dummy.txt: 9 line9\n\ - dummy.txt:10 line10\n\ - dummy.txt:11 e-lä-vän\n\ - dummy.txt:12 tolv\n"); + println!("r#\"\n{}\"#", str); + assert_eq!(str, &r#" + --> dummy.txt:8:1 +8 |> line8 + |> ^^^^^^^^^^^^^ +... +11 |> e-lä-vän + |> ^^^^^^^^^^^^^^^^ +"#[1..]); } #[test] @@ -790,7 +567,7 @@ mod test { // Test that a `MultiSpan` containing a single span splices a substition correctly let cm = CodeMap::new(); let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n"; - let selection = " \n ^~\n~~~\n~~~~~ \n \n"; + let selection = " \n ~~\n~~~\n~~~~~ \n \n"; cm.new_filemap_and_lines("blork.rs", inputtext); let sp = span_from_selection(inputtext, selection); let msp: MultiSpan = sp.into(); @@ -808,51 +585,25 @@ mod test { } #[test] - fn test_multiple_span_splice() { - // Test that a `MultiSpan` containing multiple spans splices substitions on - // several lines correctly + fn test_multi_span_splice() { + // Test that a `MultiSpan` containing multiple spans splices a substition correctly let cm = CodeMap::new(); - let inp = "aaaaabbbbBB\nZZ\nZZ\nCCCDDDDDdddddeee"; - let sp1 = " ^~~~~~\n \n \n "; - let sp2 = " \n \n \n^~~~~~ "; - let sp3 = " \n \n \n ^~~ "; - let sp4 = " \n \n \n ^~~~ "; - - let span_eq = |sp, eq| assert_eq!(&cm.span_to_snippet(sp).unwrap(), eq); - - cm.new_filemap_and_lines("blork.rs", inp); - let sp1 = span_from_selection(inp, sp1); - let sp2 = span_from_selection(inp, sp2); - let sp3 = span_from_selection(inp, sp3); - let sp4 = span_from_selection(inp, sp4); - span_eq(sp1, "bbbbBB"); - span_eq(sp2, "CCCDDD"); - span_eq(sp3, "ddd"); - span_eq(sp4, "ddee"); - - let substitutes: Vec = ["1", "2", "3", "4"].iter().map(|x|x.to_string()).collect(); - let expected = "aaaaa1\nZZ\nZZ\n2DD34e"; - - let test = |msp| { - let suggest = CodeSuggestion { - msp: msp, - substitutes: substitutes.clone(), - }; - let actual = suggest.splice_lines(&cm); - assert_eq!(actual, expected); + let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n"; + let selection1 = " \n \n \n \n ~ \n"; // intentionally out of order + let selection2 = " \n ~~\n~~~\n~~~~~ \n \n"; + cm.new_filemap_and_lines("blork.rs", inputtext); + let sp1 = span_from_selection(inputtext, selection1); + let sp2 = span_from_selection(inputtext, selection2); + let msp: MultiSpan = MultiSpan::from_spans(vec![sp1, sp2]); + + let expected = "bbbbZZZZZZddddd\neXYZe"; + let suggest = CodeSuggestion { + msp: msp, + substitutes: vec!["ZZZZZZ".to_owned(), + "XYZ".to_owned()] }; - test(MultiSpan { spans: vec![sp1, sp2, sp3, sp4] }); - - // Test ordering and merging by `MultiSpan::push` - let mut msp = MultiSpan::new(); - msp.push_merge(sp2); - msp.push_merge(sp1); - assert_eq!(&msp.spans, &[sp1, sp2]); - msp.push_merge(sp4); - assert_eq!(&msp.spans, &[sp1, sp2, sp4]); - msp.push_merge(sp3); - assert_eq!(&msp.spans, &[sp1, sp2, sp3, sp4]); - test(msp); + + assert_eq!(suggest.splice_lines(&cm), expected); } #[test] @@ -862,17 +613,17 @@ mod test { let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone()); let inp = "_____aaaaaa____bbbbbb__cccccdd_"; - let sp1 = " ^~~~~~ "; - let sp2 = " ^~~~~~ "; - let sp3 = " ^~~~~ "; - let sp4 = " ^~~~ "; - let sp34 = " ^~~~~~~ "; - let sp4_end = " ^~ "; - - let expect_start = "dummy.txt:1 _____aaaaaa____bbbbbb__cccccdd_\n\ - \x20 ^~~~~~ ^~~~~~ ^~~~~~~\n"; - let expect_end = "dummy.txt:1 _____aaaaaa____bbbbbb__cccccdd_\n\ - \x20 ^ ^ ^ ^\n"; + let sp1 = " ~~~~~~ "; + let sp2 = " ~~~~~~ "; + let sp3 = " ~~~~~ "; + let sp4 = " ~~~~ "; + let sp34 = " ~~~~~~~ "; + + let expect_start = &r#" + --> dummy.txt:1:6 +1 |> _____aaaaaa____bbbbbb__cccccdd_ + |> ^^^^^^ ^^^^^^ ^^^^^^^ +"#[1..]; let span = |sp, expected| { let sp = span_from_selection(inp, sp); @@ -885,7 +636,6 @@ mod test { let sp3 = span(sp3, "ccccc"); let sp4 = span(sp4, "ccdd"); let sp34 = span(sp34, "cccccdd"); - let sp4_end = span(sp4_end, "dd"); let spans = vec![sp1, sp2, sp3, sp4]; @@ -894,26 +644,17 @@ mod test { highlight(); let vec = data.lock().unwrap().clone(); let actual = from_utf8(&vec[..]).unwrap(); + println!("actual=\n{}", actual); assert_eq!(actual, expected); }; - let msp = MultiSpan { spans: vec![sp1, sp2, sp34] }; - let msp_end = MultiSpan { spans: vec![sp1, sp2, sp3, sp4_end] }; + let msp = MultiSpan::from_spans(vec![sp1, sp2, sp34]); test(expect_start, &mut || { diag.highlight_lines(&msp, Level::Error).unwrap(); }); - test(expect_end, &mut || { - diag.end_highlight_lines(&msp_end, Level::Error).unwrap(); - }); test(expect_start, &mut || { - for msp in cm.group_spans(spans.clone()) { - diag.highlight_lines(&msp, Level::Error).unwrap(); - } - }); - test(expect_end, &mut || { - for msp in cm.end_group_spans(spans.clone()) { - diag.end_highlight_lines(&msp, Level::Error).unwrap(); - } + let msp = MultiSpan::from_spans(spans.clone()); + diag.highlight_lines(&msp, Level::Error).unwrap(); }); } @@ -950,75 +691,31 @@ mod test { let sp4 = span(10, 10, (2, 3)); let sp5 = span(10, 10, (4, 6)); - let expect0 = "dummy.txt: 5 ccccc\n\ - dummy.txt: 6 xxxxx\n\ - dummy.txt: 7 yyyyy\n\ - \x20 ...\n\ - dummy.txt: 9 ddd__eee_\n\ - \x20 ^~~ ^~~\n\ - \x20 ...\n\ - dummy.txt:11 __f_gg\n\ - \x20 ^ ^~\n"; - - let expect = "dummy.txt: 1 aaaaa\n\ - dummy.txt: 2 aaaaa\n\ - dummy.txt: 3 aaaaa\n\ - dummy.txt: 4 bbbbb\n\ - dummy.txt: 5 ccccc\n\ - dummy.txt: 6 xxxxx\n\ - \x20 ...\n"; - - let expect_g1 = "dummy.txt:1 aaaaa\n\ - dummy.txt:2 aaaaa\n\ - dummy.txt:3 aaaaa\n\ - dummy.txt:4 bbbbb\n\ - dummy.txt:5 ccccc\n\ - dummy.txt:6 xxxxx\n\ - \x20 ...\n"; - - let expect2 = "dummy.txt: 9 ddd__eee_\n\ - \x20 ^~~ ^~~\n\ - \x20 ...\n\ - dummy.txt:11 __f_gg\n\ - \x20 ^ ^~\n"; - - - let expect_end = "dummy.txt: 1 aaaaa\n\ - \x20 ...\n\ - dummy.txt: 7 yyyyy\n\ - \x20 ^\n\ - \x20 ...\n\ - dummy.txt: 9 ddd__eee_\n\ - \x20 ^ ^\n\ - \x20 ...\n\ - dummy.txt:11 __f_gg\n\ - \x20 ^ ^\n"; - - let expect0_end = "dummy.txt: 5 ccccc\n\ - dummy.txt: 6 xxxxx\n\ - dummy.txt: 7 yyyyy\n\ - \x20 ^\n\ - \x20 ...\n\ - dummy.txt: 9 ddd__eee_\n\ - \x20 ^ ^\n\ - \x20 ...\n\ - dummy.txt:11 __f_gg\n\ - \x20 ^ ^\n"; - - let expect_end_g1 = "dummy.txt:1 aaaaa\n\ - \x20 ...\n\ - dummy.txt:7 yyyyy\n\ - \x20 ^\n"; - - let expect2_end = "dummy.txt: 9 ddd__eee_\n\ - \x20 ^ ^\n\ - \x20 ...\n\ - dummy.txt:11 __f_gg\n\ - \x20 ^ ^\n"; - - let expect_groups = [expect2, expect_g1]; - let expect_end_groups = [expect2_end, expect_end_g1]; - let spans = vec![sp3, sp1, sp4, sp2, sp5]; + let expect0 = &r#" + --> dummy.txt:5:1 +5 |> ccccc + |> ^^^^^ +... +8 |> _____ +9 |> ddd__eee_ + |> ^^^ ^^^ +10 |> elided +11 |> __f_gg + |> ^ ^^ +"#[1..]; + + let expect = &r#" + --> dummy.txt:1:1 +1 |> aaaaa + |> ^^^^^ +... +8 |> _____ +9 |> ddd__eee_ + |> ^^^ ^^^ +10 |> elided +11 |> __f_gg + |> ^ ^^ +"#[1..]; macro_rules! test { ($expected: expr, $highlight: expr) => ({ @@ -1034,37 +731,14 @@ mod test { }); } - let msp0 = MultiSpan { spans: vec![sp0, sp2, sp3, sp4, sp5] }; - let msp = MultiSpan { spans: vec![sp1, sp2, sp3, sp4, sp5] }; - let msp2 = MultiSpan { spans: vec![sp2, sp3, sp4, sp5] }; + let msp0 = MultiSpan::from_spans(vec![sp0, sp2, sp3, sp4, sp5]); + let msp = MultiSpan::from_spans(vec![sp1, sp2, sp3, sp4, sp5]); test!(expect0, || { diag.highlight_lines(&msp0, Level::Error).unwrap(); }); - test!(expect0_end, || { - diag.end_highlight_lines(&msp0, Level::Error).unwrap(); - }); test!(expect, || { diag.highlight_lines(&msp, Level::Error).unwrap(); }); - test!(expect_end, || { - diag.end_highlight_lines(&msp, Level::Error).unwrap(); - }); - test!(expect2, || { - diag.highlight_lines(&msp2, Level::Error).unwrap(); - }); - test!(expect2_end, || { - diag.end_highlight_lines(&msp2, Level::Error).unwrap(); - }); - for (msp, expect) in cm.group_spans(spans.clone()).iter().zip(expect_groups.iter()) { - test!(expect, || { - diag.highlight_lines(&msp, Level::Error).unwrap(); - }); - } - for (msp, expect) in cm.group_spans(spans.clone()).iter().zip(expect_end_groups.iter()) { - test!(expect, || { - diag.end_highlight_lines(&msp, Level::Error).unwrap(); - }); - } } } diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs index 792828b3054e9..abbc4eef7bf81 100644 --- a/src/libsyntax/errors/mod.rs +++ b/src/libsyntax/errors/mod.rs @@ -13,7 +13,7 @@ pub use errors::emitter::ColorConfig; use self::Level::*; use self::RenderSpan::*; -use codemap::{self, CodeMap, MultiSpan}; +use codemap::{self, CodeMap, MultiSpan, NO_EXPANSION, Span}; use diagnostics; use errors::emitter::{Emitter, EmitterWriter}; @@ -24,6 +24,7 @@ use term; pub mod emitter; pub mod json; +pub mod snippet; #[derive(Clone)] pub enum RenderSpan { @@ -32,13 +33,6 @@ pub enum RenderSpan { /// the source code covered by the span. FullSpan(MultiSpan), - /// Similar to a FullSpan, but the cited position is the end of - /// the span, instead of the start. Used, at least, for telling - /// compiletest/runtest to look at the last line of the span - /// (since `end_highlight_lines` displays an arrow to the end - /// of the span). - EndSpan(MultiSpan), - /// A suggestion renders with both with an initial line for the /// message, prefixed by file:linenum, followed by a summary /// of hypothetical source code, where each `String` is spliced @@ -61,7 +55,6 @@ impl RenderSpan { match *self { FullSpan(ref msp) | Suggestion(CodeSuggestion { ref msp, .. }) | - EndSpan(ref msp) | FileLine(ref msp) => msp } @@ -88,12 +81,24 @@ impl CodeSuggestion { } } } - let bounds = self.msp.to_span_bounds(); - let lines = cm.span_to_lines(bounds).unwrap(); - assert!(!lines.lines.is_empty()); - // This isn't strictly necessary, but would in all likelyhood be an error - assert_eq!(self.msp.spans.len(), self.substitutes.len()); + let mut primary_spans = self.msp.primary_spans().to_owned(); + + assert_eq!(primary_spans.len(), self.substitutes.len()); + if primary_spans.is_empty() { + return format!(""); + } + + // Assumption: all spans are in the same file, and all spans + // are disjoint. Sort in ascending order. + primary_spans.sort_by_key(|sp| sp.lo); + + // Find the bounding span. + let lo = primary_spans.iter().map(|sp| sp.lo).min().unwrap(); + let hi = primary_spans.iter().map(|sp| sp.hi).min().unwrap(); + let bounding_span = Span { lo: lo, hi: hi, expn_id: NO_EXPANSION }; + let lines = cm.span_to_lines(bounding_span).unwrap(); + assert!(!lines.lines.is_empty()); // To build up the result, we do this for each span: // - push the line segment trailing the previous span @@ -105,13 +110,13 @@ impl CodeSuggestion { // // Finally push the trailing line segment of the last span let fm = &lines.file; - let mut prev_hi = cm.lookup_char_pos(bounds.lo); + let mut prev_hi = cm.lookup_char_pos(bounding_span.lo); prev_hi.col = CharPos::from_usize(0); let mut prev_line = fm.get_line(lines.lines[0].line_index); let mut buf = String::new(); - for (sp, substitute) in self.msp.spans.iter().zip(self.substitutes.iter()) { + for (sp, substitute) in primary_spans.iter().zip(self.substitutes.iter()) { let cur_lo = cm.lookup_char_pos(sp.lo); if prev_hi.line == cur_lo.line { push_trailing(&mut buf, prev_line, &prev_hi, Some(&cur_lo)); @@ -183,7 +188,7 @@ pub struct DiagnosticBuilder<'a> { level: Level, message: String, code: Option, - span: Option, + span: MultiSpan, children: Vec, } @@ -192,7 +197,7 @@ pub struct DiagnosticBuilder<'a> { struct SubDiagnostic { level: Level, message: String, - span: Option, + span: MultiSpan, render_span: Option, } @@ -228,37 +233,61 @@ impl<'a> DiagnosticBuilder<'a> { self.level == Level::Fatal } - pub fn note(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Note, msg, None, None); + /// Add a span/label to be included in the resulting snippet. + /// This is pushed onto the `MultiSpan` that was created when the + /// diagnostic was first built. If you don't call this function at + /// all, and you just supplied a `Span` to create the diagnostic, + /// then the snippet will just include that `Span`, which is + /// called the primary span. + pub fn span_label(mut self, span: Span, label: &fmt::Display) + -> DiagnosticBuilder<'a> { + self.span.push_span_label(span, format!("{}", label)); + self + } + + pub fn note_expected_found(mut self, + label: &fmt::Display, + expected: &fmt::Display, + found: &fmt::Display) + -> DiagnosticBuilder<'a> + { + // For now, just attach these as notes + self.note(&format!("expected {} `{}`", label, expected)); + self.note(&format!(" found {} `{}`", label, found)); + self + } + + pub fn note(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Note, msg, MultiSpan::new(), None); self } pub fn span_note>(&mut self, sp: S, msg: &str) -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Note, msg, Some(sp.into()), None); + self.sub(Level::Note, msg, sp.into(), None); self } pub fn warn(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Warning, msg, None, None); + self.sub(Level::Warning, msg, MultiSpan::new(), None); self } pub fn span_warn>(&mut self, sp: S, msg: &str) -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Warning, msg, Some(sp.into()), None); + self.sub(Level::Warning, msg, sp.into(), None); self } pub fn help(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Help, msg, None, None); + self.sub(Level::Help, msg, MultiSpan::new(), None); self } pub fn span_help>(&mut self, sp: S, msg: &str) -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Help, msg, Some(sp.into()), None); + self.sub(Level::Help, msg, sp.into(), None); self } /// Prints out a message with a suggested edit of the code. @@ -269,43 +298,15 @@ impl<'a> DiagnosticBuilder<'a> { msg: &str, suggestion: String) -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Help, msg, None, Some(Suggestion(CodeSuggestion { + self.sub(Level::Help, msg, MultiSpan::new(), Some(Suggestion(CodeSuggestion { msp: sp.into(), substitutes: vec![suggestion], }))); self } - pub fn span_end_note>(&mut self, - sp: S, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Note, msg, None, Some(EndSpan(sp.into()))); - self - } - pub fn fileline_warn>(&mut self, - sp: S, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Warning, msg, None, Some(FileLine(sp.into()))); - self - } - pub fn fileline_note>(&mut self, - sp: S, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Note, msg, None, Some(FileLine(sp.into()))); - self - } - pub fn fileline_help>(&mut self, - sp: S, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Help, msg, None, Some(FileLine(sp.into()))); - self - } - pub fn span>(&mut self, sp: S) -> &mut Self { - self.span = Some(sp.into()); + pub fn set_span>(&mut self, sp: S) -> &mut Self { + self.span = sp.into(); self } @@ -324,7 +325,7 @@ impl<'a> DiagnosticBuilder<'a> { level: level, message: message.to_owned(), code: None, - span: None, + span: MultiSpan::new(), children: vec![], } } @@ -334,7 +335,7 @@ impl<'a> DiagnosticBuilder<'a> { fn sub(&mut self, level: Level, message: &str, - span: Option, + span: MultiSpan, render_span: Option) { let sub = SubDiagnostic { level: level, @@ -357,7 +358,10 @@ impl<'a> fmt::Debug for DiagnosticBuilder<'a> { impl<'a> Drop for DiagnosticBuilder<'a> { fn drop(&mut self) { if !self.cancelled() { - self.emitter.borrow_mut().emit(None, "Error constructed but not emitted", None, Bug); + self.emitter.borrow_mut().emit(&MultiSpan::new(), + "Error constructed but not emitted", + None, + Bug); panic!(); } } @@ -412,7 +416,7 @@ impl Handler { msg: &str) -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg); - result.span(sp); + result.set_span(sp); if !self.can_emit_warnings { result.cancel(); } @@ -424,7 +428,7 @@ impl Handler { code: &str) -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg); - result.span(sp); + result.set_span(sp); result.code(code.to_owned()); if !self.can_emit_warnings { result.cancel(); @@ -444,7 +448,7 @@ impl Handler { -> DiagnosticBuilder<'a> { self.bump_err_count(); let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg); - result.span(sp); + result.set_span(sp); result } pub fn struct_span_err_with_code<'a, S: Into>(&'a self, @@ -454,7 +458,7 @@ impl Handler { -> DiagnosticBuilder<'a> { self.bump_err_count(); let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg); - result.span(sp); + result.set_span(sp); result.code(code.to_owned()); result } @@ -468,7 +472,7 @@ impl Handler { -> DiagnosticBuilder<'a> { self.bump_err_count(); let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg); - result.span(sp); + result.set_span(sp); result } pub fn struct_span_fatal_with_code<'a, S: Into>(&'a self, @@ -478,7 +482,7 @@ impl Handler { -> DiagnosticBuilder<'a> { self.bump_err_count(); let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg); - result.span(sp); + result.set_span(sp); result.code(code.to_owned()); result } @@ -499,7 +503,7 @@ impl Handler { if self.treat_err_as_bug { self.span_bug(sp, msg); } - self.emit(Some(&sp.into()), msg, Fatal); + self.emit(&sp.into(), msg, Fatal); self.bump_err_count(); return FatalError; } @@ -508,7 +512,7 @@ impl Handler { if self.treat_err_as_bug { self.span_bug(sp, msg); } - self.emit_with_code(Some(&sp.into()), msg, code, Fatal); + self.emit_with_code(&sp.into(), msg, code, Fatal); self.bump_err_count(); return FatalError; } @@ -516,24 +520,24 @@ impl Handler { if self.treat_err_as_bug { self.span_bug(sp, msg); } - self.emit(Some(&sp.into()), msg, Error); + self.emit(&sp.into(), msg, Error); self.bump_err_count(); } pub fn span_err_with_code>(&self, sp: S, msg: &str, code: &str) { if self.treat_err_as_bug { self.span_bug(sp, msg); } - self.emit_with_code(Some(&sp.into()), msg, code, Error); + self.emit_with_code(&sp.into(), msg, code, Error); self.bump_err_count(); } pub fn span_warn>(&self, sp: S, msg: &str) { - self.emit(Some(&sp.into()), msg, Warning); + self.emit(&sp.into(), msg, Warning); } pub fn span_warn_with_code>(&self, sp: S, msg: &str, code: &str) { - self.emit_with_code(Some(&sp.into()), msg, code, Warning); + self.emit_with_code(&sp.into(), msg, code, Warning); } pub fn span_bug>(&self, sp: S, msg: &str) -> ! { - self.emit(Some(&sp.into()), msg, Bug); + self.emit(&sp.into(), msg, Bug); panic!(ExplicitBug); } pub fn delay_span_bug>(&self, sp: S, msg: &str) { @@ -541,11 +545,11 @@ impl Handler { *delayed = Some((sp.into(), msg.to_string())); } pub fn span_bug_no_panic>(&self, sp: S, msg: &str) { - self.emit(Some(&sp.into()), msg, Bug); + self.emit(&sp.into(), msg, Bug); self.bump_err_count(); } pub fn span_note_without_error>(&self, sp: S, msg: &str) { - self.emit.borrow_mut().emit(Some(&sp.into()), msg, None, Note); + self.emit.borrow_mut().emit(&sp.into(), msg, None, Note); } pub fn span_unimpl>(&self, sp: S, msg: &str) -> ! { self.span_bug(sp, &format!("unimplemented {}", msg)); @@ -554,7 +558,7 @@ impl Handler { if self.treat_err_as_bug { self.bug(msg); } - self.emit.borrow_mut().emit(None, msg, None, Fatal); + self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Fatal); self.bump_err_count(); FatalError } @@ -562,17 +566,17 @@ impl Handler { if self.treat_err_as_bug { self.bug(msg); } - self.emit.borrow_mut().emit(None, msg, None, Error); + self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Error); self.bump_err_count(); } pub fn warn(&self, msg: &str) { - self.emit.borrow_mut().emit(None, msg, None, Warning); + self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Warning); } pub fn note_without_error(&self, msg: &str) { - self.emit.borrow_mut().emit(None, msg, None, Note); + self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Note); } pub fn bug(&self, msg: &str) -> ! { - self.emit.borrow_mut().emit(None, msg, None, Bug); + self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Bug); panic!(ExplicitBug); } pub fn unimpl(&self, msg: &str) -> ! { @@ -614,25 +618,20 @@ impl Handler { panic!(self.fatal(&s)); } pub fn emit(&self, - msp: Option<&MultiSpan>, + msp: &MultiSpan, msg: &str, lvl: Level) { if lvl == Warning && !self.can_emit_warnings { return } - self.emit.borrow_mut().emit(msp, msg, None, lvl); + self.emit.borrow_mut().emit(&msp, msg, None, lvl); if !self.continue_after_error.get() { self.abort_if_errors(); } } pub fn emit_with_code(&self, - msp: Option<&MultiSpan>, + msp: &MultiSpan, msg: &str, code: &str, lvl: Level) { if lvl == Warning && !self.can_emit_warnings { return } - self.emit.borrow_mut().emit(msp, msg, Some(code), lvl); - if !self.continue_after_error.get() { self.abort_if_errors(); } - } - pub fn custom_emit(&self, rsp: RenderSpan, msg: &str, lvl: Level) { - if lvl == Warning && !self.can_emit_warnings { return } - self.emit.borrow_mut().custom_emit(&rsp, msg, lvl); + self.emit.borrow_mut().emit(&msp, msg, Some(code), lvl); if !self.continue_after_error.get() { self.abort_if_errors(); } } }