Auto merge of #30411 - mitaa:multispan, r=nrc

This allows to render multiple spans on one line, or to splice multiple replacements into a code suggestion.

fixes #28124
This commit is contained in:
bors 2016-01-28 22:13:25 +00:00
commit 142214d1f2
10 changed files with 1187 additions and 434 deletions

View file

@ -15,7 +15,7 @@ use session::search_paths::PathKind;
use util::nodemap::{NodeMap, FnvHashMap};
use syntax::ast::{NodeId, NodeIdAssigner, Name};
use syntax::codemap::Span;
use syntax::codemap::{Span, MultiSpan};
use syntax::errors::{self, DiagnosticBuilder};
use syntax::errors::emitter::{Emitter, BasicEmitter, EmitterWriter};
use syntax::errors::json::JsonEmitter;
@ -47,7 +47,7 @@ pub struct Session {
pub cstore: Rc<for<'a> CrateStore<'a>>,
pub parse_sess: ParseSess,
// For a library crate, this is always none
pub entry_fn: RefCell<Option<(NodeId, codemap::Span)>>,
pub entry_fn: RefCell<Option<(NodeId, Span)>>,
pub entry_type: Cell<Option<config::EntryFnType>>,
pub plugin_registrar_fn: Cell<Option<ast::NodeId>>,
pub default_sysroot: Option<PathBuf>,
@ -57,7 +57,7 @@ pub struct Session {
pub local_crate_source_file: Option<PathBuf>,
pub working_dir: PathBuf,
pub lint_store: RefCell<lint::LintStore>,
pub lints: RefCell<NodeMap<Vec<(lint::LintId, codemap::Span, String)>>>,
pub lints: RefCell<NodeMap<Vec<(lint::LintId, Span, String)>>>,
pub plugin_llvm_passes: RefCell<Vec<String>>,
pub plugin_attributes: RefCell<Vec<(String, AttributeType)>>,
pub crate_types: RefCell<Vec<config::CrateType>>,
@ -81,36 +81,36 @@ pub struct Session {
}
impl Session {
pub fn struct_span_warn<'a>(&'a self,
sp: Span,
msg: &str)
-> DiagnosticBuilder<'a> {
pub fn struct_span_warn<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str)
-> DiagnosticBuilder<'a> {
self.diagnostic().struct_span_warn(sp, msg)
}
pub fn struct_span_warn_with_code<'a>(&'a self,
sp: Span,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a> {
pub fn struct_span_warn_with_code<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a> {
self.diagnostic().struct_span_warn_with_code(sp, msg, code)
}
pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
self.diagnostic().struct_warn(msg)
}
pub fn struct_span_err<'a>(&'a self,
sp: Span,
msg: &str)
-> DiagnosticBuilder<'a> {
pub fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str)
-> DiagnosticBuilder<'a> {
match split_msg_into_multilines(msg) {
Some(ref msg) => self.diagnostic().struct_span_err(sp, msg),
None => self.diagnostic().struct_span_err(sp, msg),
}
}
pub fn struct_span_err_with_code<'a>(&'a self,
sp: Span,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a> {
pub fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a> {
match split_msg_into_multilines(msg) {
Some(ref msg) => self.diagnostic().struct_span_err_with_code(sp, msg, code),
None => self.diagnostic().struct_span_err_with_code(sp, msg, code),
@ -119,46 +119,46 @@ impl Session {
pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
self.diagnostic().struct_err(msg)
}
pub fn struct_span_fatal<'a>(&'a self,
sp: Span,
msg: &str)
-> DiagnosticBuilder<'a> {
pub fn struct_span_fatal<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str)
-> DiagnosticBuilder<'a> {
self.diagnostic().struct_span_fatal(sp, msg)
}
pub fn struct_span_fatal_with_code<'a>(&'a self,
sp: Span,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a> {
pub fn struct_span_fatal_with_code<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a> {
self.diagnostic().struct_span_fatal_with_code(sp, msg, code)
}
pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
self.diagnostic().struct_fatal(msg)
}
pub fn span_fatal(&self, sp: Span, msg: &str) -> ! {
pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
panic!(self.diagnostic().span_fatal(sp, msg))
}
pub fn span_fatal_with_code(&self, sp: Span, msg: &str, code: &str) -> ! {
pub fn span_fatal_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) -> ! {
panic!(self.diagnostic().span_fatal_with_code(sp, msg, code))
}
pub fn fatal(&self, msg: &str) -> ! {
panic!(self.diagnostic().fatal(msg))
}
pub fn span_err_or_warn(&self, is_warning: bool, sp: Span, msg: &str) {
pub fn span_err_or_warn<S: Into<MultiSpan>>(&self, is_warning: bool, sp: S, msg: &str) {
if is_warning {
self.span_warn(sp, msg);
} else {
self.span_err(sp, msg);
}
}
pub fn span_err(&self, sp: Span, msg: &str) {
pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
match split_msg_into_multilines(msg) {
Some(msg) => self.diagnostic().span_err(sp, &msg),
None => self.diagnostic().span_err(sp, msg)
}
}
pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) {
pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
match split_msg_into_multilines(msg) {
Some(msg) => self.diagnostic().span_err_with_code(sp, &msg, code),
None => self.diagnostic().span_err_with_code(sp, msg, code)
@ -199,32 +199,32 @@ impl Session {
}
}
}
pub fn span_warn(&self, sp: Span, msg: &str) {
pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.diagnostic().span_warn(sp, msg)
}
pub fn span_warn_with_code(&self, sp: Span, msg: &str, code: &str) {
pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
self.diagnostic().span_warn_with_code(sp, msg, code)
}
pub fn warn(&self, msg: &str) {
self.diagnostic().warn(msg)
}
pub fn opt_span_warn(&self, opt_sp: Option<Span>, msg: &str) {
pub fn opt_span_warn<S: Into<MultiSpan>>(&self, opt_sp: Option<S>, msg: &str) {
match opt_sp {
Some(sp) => self.span_warn(sp, msg),
None => self.warn(msg),
}
}
pub fn opt_span_bug(&self, opt_sp: Option<Span>, msg: &str) -> ! {
pub fn opt_span_bug<S: Into<MultiSpan>>(&self, opt_sp: Option<S>, msg: &str) -> ! {
match opt_sp {
Some(sp) => self.span_bug(sp, msg),
None => self.bug(msg),
}
}
/// Delay a span_bug() call until abort_if_errors()
pub fn delay_span_bug(&self, sp: Span, msg: &str) {
pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.diagnostic().delay_span_bug(sp, msg)
}
pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
self.diagnostic().span_bug(sp, msg)
}
pub fn bug(&self, msg: &str) -> ! {
@ -233,10 +233,10 @@ impl Session {
pub fn note_without_error(&self, msg: &str) {
self.diagnostic().note_without_error(msg)
}
pub fn span_note_without_error(&self, sp: Span, msg: &str) {
pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.diagnostic().span_note_without_error(sp, msg)
}
pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! {
pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
self.diagnostic().span_unimpl(sp, msg)
}
pub fn unimpl(&self, msg: &str) -> ! {
@ -273,7 +273,7 @@ impl Session {
}
// This exists to help with refactoring to eliminate impossible
// cases later on
pub fn impossible_case(&self, sp: Span, msg: &str) -> ! {
pub fn impossible_case<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
self.span_bug(sp, &format!("impossible case reached: {}", msg));
}
pub fn verbose(&self) -> bool { self.opts.debugging_opts.verbose }

View file

@ -991,7 +991,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
&format!("to force the closure to take ownership of {} \
(and any other referenced variables), \
use the `move` keyword, as shown:",
cmt_path_or_string),
cmt_path_or_string),
suggestion)
.emit();
}

View file

@ -32,7 +32,7 @@ use rustc::front::map as hir_map;
use rustc::session::{self, config};
use std::rc::Rc;
use syntax::{abi, ast};
use syntax::codemap::{Span, CodeMap, DUMMY_SP};
use syntax::codemap::{MultiSpan, CodeMap, DUMMY_SP};
use syntax::errors;
use syntax::errors::emitter::Emitter;
use syntax::errors::{Level, RenderSpan};
@ -78,14 +78,14 @@ fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) {
impl Emitter for ExpectErrorEmitter {
fn emit(&mut self,
_sp: Option<Span>,
_sp: Option<&MultiSpan>,
msg: &str,
_: Option<&str>,
lvl: Level) {
remove_message(self, msg, lvl);
}
fn custom_emit(&mut self, _sp: RenderSpan, msg: &str, lvl: Level) {
fn custom_emit(&mut self, _sp: &RenderSpan, msg: &str, lvl: Level) {
remove_message(self, msg, lvl);
}
}

View file

@ -109,7 +109,7 @@ impl SharedEmitter {
}
impl Emitter for SharedEmitter {
fn emit(&mut self, sp: Option<codemap::Span>,
fn emit(&mut self, sp: Option<&codemap::MultiSpan>,
msg: &str, code: Option<&str>, lvl: Level) {
assert!(sp.is_none(), "SharedEmitter doesn't support spans");
@ -120,7 +120,7 @@ impl Emitter for SharedEmitter {
});
}
fn custom_emit(&mut self, _sp: errors::RenderSpan, _msg: &str, _lvl: Level) {
fn custom_emit(&mut self, _sp: &errors::RenderSpan, _msg: &str, _lvl: Level) {
panic!("SharedEmitter doesn't support custom_emit");
}
}

View file

@ -1072,8 +1072,8 @@ fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
match fcx.tcx().sess.codemap().span_to_snippet(t_span) {
Ok(s) => {
err.span_suggestion(t_span,
"try casting to a `Box` instead:",
format!("Box<{}>", s));
"try casting to a `Box` instead:",
format!("Box<{}>", s));
},
Err(_) =>
span_help!(err, t_span, "did you mean `Box<{}>`?", tstr),

View file

@ -23,6 +23,7 @@ use std::cell::{Cell, RefCell};
use std::ops::{Add, Sub};
use std::path::Path;
use std::rc::Rc;
use std::cmp;
use std::{fmt, fs};
use std::io::{self, Read};
@ -31,6 +32,8 @@ use serialize::{Encodable, Decodable, Encoder, Decoder};
use ast::Name;
use errors::emitter::MAX_HIGHLIGHT_LINES;
// _____________________________________________________________________________
// Pos, BytePos, CharPos
//
@ -42,7 +45,7 @@ pub trait Pos {
/// A byte offset. Keep this small (currently 32-bits), as AST contains
/// a lot of them.
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Debug)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct BytePos(pub u32);
/// A character offset. Because of multibyte utf8 characters, a byte offset
@ -109,7 +112,7 @@ impl Sub for CharPos {
}
// _____________________________________________________________________________
// Span, Spanned
// Span, MultiSpan, Spanned
//
/// Spans represent a region of code, used for error reporting. Positions in spans
@ -129,6 +132,15 @@ pub struct Span {
pub expn_id: ExpnId
}
/// Spans are converted to MultiSpans just before error reporting, either automatically,
/// generated by line grouping, or manually constructed.
/// In the latter case care should be taken to ensure that spans are ordered, disjoint,
/// and point into the same FileMap.
#[derive(Clone)]
pub struct MultiSpan {
pub spans: Vec<Span>
}
pub const DUMMY_SP: Span = Span { lo: BytePos(0), hi: BytePos(0), expn_id: NO_EXPANSION };
// Generic span to be used for code originating from the command line
@ -145,6 +157,33 @@ impl Span {
pub fn contains(self, other: Span) -> bool {
self.lo <= other.lo && other.hi <= self.hi
}
/// Returns `Some(span)`, a union of `self` and `other`, on overlap.
pub fn merge(self, other: Span) -> Option<Span> {
if self.expn_id != other.expn_id {
return None;
}
if (self.lo <= other.lo && self.hi > other.lo) ||
(self.lo >= other.lo && self.lo < other.hi) {
Some(Span {
lo: cmp::min(self.lo, other.lo),
hi: cmp::max(self.hi, other.hi),
expn_id: self.expn_id,
})
} else {
None
}
}
/// Returns `Some(span)`, where the start is trimmed by the end of `other`
pub fn trim_start(self, other: Span) -> Option<Span> {
if self.hi > other.hi {
Some(Span { lo: cmp::max(self.lo, other.hi), .. self })
} else {
None
}
}
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
@ -236,6 +275,102 @@ pub fn original_sp(cm: &CodeMap, sp: Span, enclosing_sp: Span) -> Span {
}
}
impl MultiSpan {
pub fn new() -> MultiSpan {
MultiSpan { spans: Vec::new() }
}
pub fn to_span_bounds(&self) -> Span {
assert!(!self.spans.is_empty());
let Span { lo, expn_id, .. } = *self.spans.first().unwrap();
let Span { hi, .. } = *self.spans.last().unwrap();
Span { lo: lo, hi: hi, expn_id: expn_id }
}
/// Merges or inserts the given span into itself.
pub fn push_merge(&mut self, mut sp: Span) {
let mut idx_merged = None;
for idx in 0.. {
let cur = match self.spans.get(idx) {
Some(s) => *s,
None => break,
};
// Try to merge with a contained Span
if let Some(union) = cur.merge(sp) {
self.spans[idx] = union;
sp = union;
idx_merged = Some(idx);
break;
}
// Or insert into the first sorted position
if sp.hi <= cur.lo {
self.spans.insert(idx, sp);
idx_merged = Some(idx);
break;
}
}
if let Some(idx) = idx_merged {
// Merge with spans trailing the insertion/merging position
while (idx + 1) < self.spans.len() {
if let Some(union) = self.spans[idx + 1].merge(sp) {
self.spans[idx] = union;
self.spans.remove(idx + 1);
} else {
break;
}
}
} else {
self.spans.push(sp);
}
}
/// Inserts the given span into itself, for use with `end_highlight_lines`.
pub fn push_trim(&mut self, mut sp: Span) {
let mut prev = mk_sp(BytePos(0), BytePos(0));
if let Some(first) = self.spans.get_mut(0) {
if first.lo > sp.lo {
// Prevent us here from spanning fewer lines
// because of trimming the start of the span
// (this should not be visible, because this method ought
// to not be used in conjunction with `highlight_lines`)
first.lo = sp.lo;
}
}
for idx in 0.. {
if let Some(sp_trim) = sp.trim_start(prev) {
// Implies `sp.hi > prev.hi`
let cur = match self.spans.as_slice().get(idx) {
Some(s) => *s,
None => {
sp = sp_trim;
break;
}
};
// `cur` may overlap with `sp_trim`
if let Some(cur_trim) = cur.trim_start(sp_trim) {
// Implies `sp.hi < cur.hi`
self.spans.insert(idx, sp_trim);
self.spans[idx + 1] = cur_trim;
return;
} else if sp.hi == cur.hi {
return;
}
prev = cur;
}
}
self.spans.push(sp);
}
}
impl From<Span> for MultiSpan {
fn from(span: Span) -> MultiSpan {
MultiSpan { spans: vec![span] }
}
}
// _____________________________________________________________________________
// Loc, LocWithOpt, FileMapAndLine, FileMapAndBytePos
//
@ -1020,6 +1155,59 @@ impl CodeMap {
}
}
/// Groups and sorts spans by lines into `MultiSpan`s, where `push` adds them to their group,
/// specifying the unification behaviour for overlapping spans.
/// Spans overflowing a line are put into their own one-element-group.
pub fn custom_group_spans<F>(&self, mut spans: Vec<Span>, push: F) -> Vec<MultiSpan>
where F: Fn(&mut MultiSpan, Span)
{
spans.sort_by(|a, b| a.lo.cmp(&b.lo));
let mut groups = Vec::<MultiSpan>::new();
let mut overflowing = vec![];
let mut prev_expn = ExpnId(!2u32);
let mut prev_file = !0usize;
let mut prev_line = !0usize;
let mut err_size = 0;
for sp in spans {
let line = self.lookup_char_pos(sp.lo).line;
let line_hi = self.lookup_char_pos(sp.hi).line;
if line != line_hi {
overflowing.push(sp.into());
continue
}
let file = self.lookup_filemap_idx(sp.lo);
if err_size < MAX_HIGHLIGHT_LINES && sp.expn_id == prev_expn && file == prev_file {
// `push` takes care of sorting, trimming, and merging
push(&mut groups.last_mut().unwrap(), sp);
if line != prev_line {
err_size += 1;
}
} else {
groups.push(sp.into());
err_size = 1;
}
prev_expn = sp.expn_id;
prev_file = file;
prev_line = line;
}
groups.extend(overflowing);
groups
}
/// Groups and sorts spans by lines into `MultiSpan`s, merging overlapping spans.
/// Spans overflowing a line are put into their own one-element-group.
pub fn group_spans(&self, spans: Vec<Span>) -> Vec<MultiSpan> {
self.custom_group_spans(spans, |msp, sp| msp.push_merge(sp))
}
/// Like `group_spans`, but trims overlapping spans instead of
/// merging them (for use with `end_highlight_lines`)
pub fn end_group_spans(&self, spans: Vec<Span>) -> Vec<MultiSpan> {
self.custom_group_spans(spans, |msp, sp| msp.push_trim(sp))
}
pub fn get_filemap(&self, filename: &str) -> Rc<FileMap> {
for fm in self.files.borrow().iter() {
if filename == fm.name {
@ -1351,7 +1539,7 @@ mod tests {
fn span_from_selection(input: &str, selection: &str) -> Span {
assert_eq!(input.len(), selection.len());
let left_index = selection.find('^').unwrap() as u32;
let right_index = selection.rfind('~').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 }
}
@ -1511,4 +1699,73 @@ 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]>);
}
}

File diff suppressed because it is too large Load diff

View file

@ -20,9 +20,9 @@
// FIXME spec the JSON output properly.
use codemap::{Span, CodeMap};
use codemap::{MultiSpan, CodeMap};
use diagnostics::registry::Registry;
use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan};
use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion};
use errors::emitter::Emitter;
use std::rc::Rc;
@ -52,15 +52,15 @@ impl JsonEmitter {
}
impl Emitter for JsonEmitter {
fn emit(&mut self, span: Option<Span>, msg: &str, code: Option<&str>, level: Level) {
fn emit(&mut self, span: Option<&MultiSpan>, msg: &str, code: Option<&str>, level: Level) {
let data = Diagnostic::new(span, msg, code, level, self);
if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
panic!("failed to print diagnostics: {:?}", e);
}
}
fn custom_emit(&mut self, sp: RenderSpan, msg: &str, level: Level) {
let data = Diagnostic::from_render_span(&sp, msg, level, self);
fn custom_emit(&mut self, sp: &RenderSpan, msg: &str, level: Level) {
let data = Diagnostic::from_render_span(sp, msg, level, self);
if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
panic!("failed to print diagnostics: {:?}", e);
}
@ -83,7 +83,7 @@ struct Diagnostic<'a> {
code: Option<DiagnosticCode>,
/// "error: internal compiler error", "error", "warning", "note", "help".
level: &'static str,
span: Option<DiagnosticSpan>,
spans: Vec<DiagnosticSpan>,
/// Assocaited diagnostic messages.
children: Vec<Diagnostic<'a>>,
}
@ -110,7 +110,7 @@ struct DiagnosticCode {
}
impl<'a> Diagnostic<'a> {
fn new(span: Option<Span>,
fn new(msp: Option<&MultiSpan>,
msg: &'a str,
code: Option<&str>,
level: Level,
@ -120,7 +120,7 @@ impl<'a> Diagnostic<'a> {
message: msg,
code: DiagnosticCode::map_opt_string(code.map(|c| c.to_owned()), je),
level: level.to_str(),
span: span.map(|sp| DiagnosticSpan::from_span(sp, je)),
spans: msp.map_or(vec![], |msp| DiagnosticSpan::from_multispan(msp, je)),
children: vec![],
}
}
@ -134,7 +134,7 @@ impl<'a> Diagnostic<'a> {
message: msg,
code: None,
level: level.to_str(),
span: Some(DiagnosticSpan::from_render_span(span, je)),
spans: DiagnosticSpan::from_render_span(span, je),
children: vec![],
}
}
@ -146,7 +146,7 @@ impl<'a> Diagnostic<'a> {
message: &db.message,
code: DiagnosticCode::map_opt_string(db.code.clone(), je),
level: db.level.to_str(),
span: db.span.map(|sp| DiagnosticSpan::from_span(sp, je)),
spans: db.span.as_ref().map_or(vec![], |sp| DiagnosticSpan::from_multispan(sp, je)),
children: db.children.iter().map(|c| {
Diagnostic::from_sub_diagnostic(c, je)
}).collect(),
@ -158,59 +158,67 @@ impl<'a> Diagnostic<'a> {
message: &db.message,
code: None,
level: db.level.to_str(),
span: db.render_span.as_ref()
.map(|sp| DiagnosticSpan::from_render_span(sp, je))
.or_else(|| db.span.map(|sp| DiagnosticSpan::from_span(sp, je))),
spans: db.render_span.as_ref()
.map(|sp| DiagnosticSpan::from_render_span(sp, je))
.or_else(|| db.span.as_ref().map(|s| DiagnosticSpan::from_multispan(s, je)))
.unwrap_or(vec![]),
children: vec![],
}
}
}
impl DiagnosticSpan {
fn from_span(span: Span, je: &JsonEmitter) -> DiagnosticSpan {
let start = je.cm.lookup_char_pos(span.lo);
let end = je.cm.lookup_char_pos(span.hi);
DiagnosticSpan {
file_name: start.file.name.clone(),
byte_start: span.lo.0,
byte_end: span.hi.0,
line_start: start.line,
line_end: end.line,
column_start: start.col.0 + 1,
column_end: end.col.0 + 1,
}
fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
msp.spans.iter().map(|span| {
let start = je.cm.lookup_char_pos(span.lo);
let end = je.cm.lookup_char_pos(span.hi);
DiagnosticSpan {
file_name: start.file.name.clone(),
byte_start: span.lo.0,
byte_end: span.hi.0,
line_start: start.line,
line_end: end.line,
column_start: start.col.0 + 1,
column_end: end.col.0 + 1,
}
}).collect()
}
fn from_render_span(span: &RenderSpan, je: &JsonEmitter) -> DiagnosticSpan {
match *span {
fn from_render_span(rsp: &RenderSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
match *rsp {
// FIXME(#30701) handle Suggestion properly
RenderSpan::FullSpan(sp) | RenderSpan::Suggestion(sp, _) => {
DiagnosticSpan::from_span(sp, je)
RenderSpan::FullSpan(ref msp) |
RenderSpan::Suggestion(CodeSuggestion { ref msp, .. }) => {
DiagnosticSpan::from_multispan(msp, je)
}
RenderSpan::EndSpan(span) => {
let end = je.cm.lookup_char_pos(span.hi);
DiagnosticSpan {
file_name: end.file.name.clone(),
byte_start: span.lo.0,
byte_end: span.hi.0,
line_start: 0,
line_end: end.line,
column_start: 0,
column_end: end.col.0 + 1,
}
RenderSpan::EndSpan(ref msp) => {
msp.spans.iter().map(|span| {
let end = je.cm.lookup_char_pos(span.hi);
DiagnosticSpan {
file_name: end.file.name.clone(),
byte_start: span.lo.0,
byte_end: span.hi.0,
line_start: 0,
line_end: end.line,
column_start: 0,
column_end: end.col.0 + 1,
}
}).collect()
}
RenderSpan::FileLine(span) => {
let start = je.cm.lookup_char_pos(span.lo);
let end = je.cm.lookup_char_pos(span.hi);
DiagnosticSpan {
file_name: start.file.name.clone(),
byte_start: span.lo.0,
byte_end: span.hi.0,
line_start: start.line,
line_end: end.line,
column_start: 0,
column_end: 0,
}
RenderSpan::FileLine(ref msp) => {
msp.spans.iter().map(|span| {
let start = je.cm.lookup_char_pos(span.lo);
let end = je.cm.lookup_char_pos(span.hi);
DiagnosticSpan {
file_name: start.file.name.clone(),
byte_start: span.lo.0,
byte_end: span.hi.0,
line_start: start.line,
line_end: end.line,
column_start: 0,
column_end: 0,
}
}).collect()
}
}
}

View file

@ -13,7 +13,7 @@ pub use errors::emitter::ColorConfig;
use self::Level::*;
use self::RenderSpan::*;
use codemap::{self, Span};
use codemap::{self, CodeMap, MultiSpan};
use diagnostics;
use errors::emitter::{Emitter, EmitterWriter};
@ -31,38 +31,115 @@ pub enum RenderSpan {
/// A FullSpan renders with both with an initial line for the
/// message, prefixed by file:linenum, followed by a summary of
/// the source code covered by the span.
FullSpan(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(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 the `String` is spliced
/// into the lines in place of the code covered by the span.
Suggestion(Span, String),
/// of hypothetical source code, where each `String` is spliced
/// into the lines in place of the code covered by each span.
Suggestion(CodeSuggestion),
/// A FileLine renders with just a line for the message prefixed
/// by file:linenum.
FileLine(Span),
FileLine(MultiSpan),
}
#[derive(Clone)]
pub struct CodeSuggestion {
msp: MultiSpan,
substitutes: Vec<String>,
}
impl RenderSpan {
fn span(&self) -> Span {
fn span(&self) -> &MultiSpan {
match *self {
FullSpan(s) |
Suggestion(s, _) |
EndSpan(s) |
FileLine(s) =>
s
FullSpan(ref msp) |
Suggestion(CodeSuggestion { ref msp, .. }) |
EndSpan(ref msp) |
FileLine(ref msp) =>
msp
}
}
}
impl CodeSuggestion {
/// Returns the assembled code suggestion.
pub fn splice_lines(&self, cm: &CodeMap) -> String {
use codemap::{CharPos, Loc, Pos};
fn push_trailing(buf: &mut String, line_opt: Option<&str>,
lo: &Loc, hi_opt: Option<&Loc>) {
let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi|hi.col.to_usize()));
if let Some(line) = line_opt {
if line.len() > lo {
buf.push_str(match hi_opt {
Some(hi) => &line[lo..hi],
None => &line[lo..],
});
}
if let None = hi_opt {
buf.push('\n');
}
}
}
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());
// To build up the result, we do this for each span:
// - push the line segment trailing the previous span
// (at the beginning a "phantom" span pointing at the start of the line)
// - push lines between the previous and current span (if any)
// - if the previous and current span are not on the same line
// push the line segment leading up to the current span
// - splice in the span substitution
//
// Finally push the trailing line segment of the last span
let fm = &lines.file;
let mut prev_hi = cm.lookup_char_pos(bounds.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()) {
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));
} else {
push_trailing(&mut buf, prev_line, &prev_hi, None);
// push lines between the previous and current span (if any)
for idx in prev_hi.line..(cur_lo.line - 1) {
if let Some(line) = fm.get_line(idx) {
buf.push_str(line);
buf.push('\n');
}
}
if let Some(cur_line) = fm.get_line(cur_lo.line - 1) {
buf.push_str(&cur_line[.. cur_lo.col.to_usize()]);
}
}
buf.push_str(substitute);
prev_hi = cm.lookup_char_pos(sp.hi);
prev_line = fm.get_line(prev_hi.line - 1);
}
push_trailing(&mut buf, prev_line, &prev_hi, None);
// remove trailing newline
buf.pop();
buf
}
}
/// Used as a return value to signify a fatal error occurred. (It is also
/// used as the argument to panic at the moment, but that will eventually
/// not be true.)
@ -106,7 +183,7 @@ pub struct DiagnosticBuilder<'a> {
level: Level,
message: String,
code: Option<String>,
span: Option<Span>,
span: Option<MultiSpan>,
children: Vec<SubDiagnostic>,
}
@ -114,7 +191,7 @@ pub struct DiagnosticBuilder<'a> {
struct SubDiagnostic {
level: Level,
message: String,
span: Option<Span>,
span: Option<MultiSpan>,
render_span: Option<RenderSpan>,
}
@ -150,81 +227,84 @@ impl<'a> DiagnosticBuilder<'a> {
self.level == Level::Fatal
}
pub fn note(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> {
pub fn note(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> {
self.sub(Level::Note, msg, None, None);
self
}
pub fn span_note(&mut self ,
sp: Span,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Note, msg, Some(sp), None);
pub fn span_note<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Note, msg, Some(sp.into()), None);
self
}
pub fn warn(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> {
pub fn warn(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> {
self.sub(Level::Warning, msg, None, None);
self
}
pub fn span_warn(&mut self,
sp: Span,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Warning, msg, Some(sp), None);
pub fn span_warn<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Warning, msg, Some(sp.into()), None);
self
}
pub fn help(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> {
pub fn help(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> {
self.sub(Level::Help, msg, None, None);
self
}
pub fn span_help(&mut self ,
sp: Span,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Help, msg, Some(sp), None);
pub fn span_help<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Help, msg, Some(sp.into()), None);
self
}
/// Prints out a message with a suggested edit of the code.
///
/// See `diagnostic::RenderSpan::Suggestion` for more information.
pub fn span_suggestion(&mut self ,
sp: Span,
msg: &str,
suggestion: String)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Help, msg, Some(sp), Some(Suggestion(sp, suggestion)));
pub fn span_suggestion<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str,
suggestion: String)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Help, msg, None, Some(Suggestion(CodeSuggestion {
msp: sp.into(),
substitutes: vec![suggestion],
})));
self
}
pub fn span_end_note(&mut self ,
sp: Span,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Note, msg, Some(sp), Some(EndSpan(sp)));
pub fn span_end_note<S: Into<MultiSpan>>(&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: Span,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Warning, msg, Some(sp), Some(FileLine(sp)));
pub fn fileline_warn<S: Into<MultiSpan>>(&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: Span,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Note, msg, Some(sp), Some(FileLine(sp)));
pub fn fileline_note<S: Into<MultiSpan>>(&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: Span,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Help, msg, Some(sp), Some(FileLine(sp)));
pub fn fileline_help<S: Into<MultiSpan>>(&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: Span) -> &mut Self {
self.span = Some(sp);
pub fn span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
self.span = Some(sp.into());
self
}
@ -237,7 +317,7 @@ impl<'a> DiagnosticBuilder<'a> {
/// struct_* methods on Handler.
fn new(emitter: &'a RefCell<Box<Emitter>>,
level: Level,
message: &str) -> DiagnosticBuilder<'a> {
message: &str) -> DiagnosticBuilder<'a> {
DiagnosticBuilder {
emitter: emitter,
level: level,
@ -253,7 +333,7 @@ impl<'a> DiagnosticBuilder<'a> {
fn sub(&mut self,
level: Level,
message: &str,
span: Option<Span>,
span: Option<MultiSpan>,
render_span: Option<RenderSpan>) {
let sub = SubDiagnostic {
level: level,
@ -290,7 +370,7 @@ pub struct Handler {
emit: RefCell<Box<Emitter>>,
pub can_emit_warnings: bool,
treat_err_as_bug: bool,
delayed_span_bug: RefCell<Option<(codemap::Span, String)>>,
delayed_span_bug: RefCell<Option<(MultiSpan, String)>>,
}
impl Handler {
@ -320,10 +400,10 @@ impl Handler {
DiagnosticBuilder::new(&self.emit, Level::Cancelled, "")
}
pub fn struct_span_warn<'a>(&'a self,
sp: Span,
msg: &str)
-> DiagnosticBuilder<'a> {
pub fn struct_span_warn<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str)
-> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
result.span(sp);
if !self.can_emit_warnings {
@ -331,11 +411,11 @@ impl Handler {
}
result
}
pub fn struct_span_warn_with_code<'a>(&'a self,
sp: Span,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a> {
pub fn struct_span_warn_with_code<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
result.span(sp);
result.code(code.to_owned());
@ -351,20 +431,20 @@ impl Handler {
}
result
}
pub fn struct_span_err<'a>(&'a self,
sp: Span,
msg: &str)
-> DiagnosticBuilder<'a> {
pub fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str)
-> DiagnosticBuilder<'a> {
self.bump_err_count();
let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg);
result.span(sp);
result
}
pub fn struct_span_err_with_code<'a>(&'a self,
sp: Span,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a> {
pub fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a> {
self.bump_err_count();
let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg);
result.span(sp);
@ -375,20 +455,20 @@ impl Handler {
self.bump_err_count();
DiagnosticBuilder::new(&self.emit, Level::Error, msg)
}
pub fn struct_span_fatal<'a>(&'a self,
sp: Span,
msg: &str)
-> DiagnosticBuilder<'a> {
pub fn struct_span_fatal<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str)
-> DiagnosticBuilder<'a> {
self.bump_err_count();
let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg);
result.span(sp);
result
}
pub fn struct_span_fatal_with_code<'a>(&'a self,
sp: Span,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a> {
pub fn struct_span_fatal_with_code<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a> {
self.bump_err_count();
let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg);
result.span(sp);
@ -408,58 +488,59 @@ impl Handler {
err.cancel();
}
pub fn span_fatal(&self, sp: Span, msg: &str) -> FatalError {
pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> FatalError {
if self.treat_err_as_bug {
self.span_bug(sp, msg);
}
self.emit(Some(sp), msg, Fatal);
self.emit(Some(&sp.into()), msg, Fatal);
self.bump_err_count();
return FatalError;
}
pub fn span_fatal_with_code(&self, sp: Span, msg: &str, code: &str) -> FatalError {
pub fn span_fatal_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str)
-> FatalError {
if self.treat_err_as_bug {
self.span_bug(sp, msg);
}
self.emit_with_code(Some(sp), msg, code, Fatal);
self.emit_with_code(Some(&sp.into()), msg, code, Fatal);
self.bump_err_count();
return FatalError;
}
pub fn span_err(&self, sp: Span, msg: &str) {
pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
if self.treat_err_as_bug {
self.span_bug(sp, msg);
}
self.emit(Some(sp), msg, Error);
self.emit(Some(&sp.into()), msg, Error);
self.bump_err_count();
}
pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) {
pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
if self.treat_err_as_bug {
self.span_bug(sp, msg);
}
self.emit_with_code(Some(sp), msg, code, Error);
self.emit_with_code(Some(&sp.into()), msg, code, Error);
self.bump_err_count();
}
pub fn span_warn(&self, sp: Span, msg: &str) {
self.emit(Some(sp), msg, Warning);
pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.emit(Some(&sp.into()), msg, Warning);
}
pub fn span_warn_with_code(&self, sp: Span, msg: &str, code: &str) {
self.emit_with_code(Some(sp), msg, code, Warning);
pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
self.emit_with_code(Some(&sp.into()), msg, code, Warning);
}
pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
self.emit(Some(sp), msg, Bug);
pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
self.emit(Some(&sp.into()), msg, Bug);
panic!(ExplicitBug);
}
pub fn delay_span_bug(&self, sp: Span, msg: &str) {
pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
let mut delayed = self.delayed_span_bug.borrow_mut();
*delayed = Some((sp, msg.to_string()));
*delayed = Some((sp.into(), msg.to_string()));
}
pub fn span_bug_no_panic(&self, sp: Span, msg: &str) {
self.emit(Some(sp), msg, Bug);
pub fn span_bug_no_panic<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.emit(Some(&sp.into()), msg, Bug);
self.bump_err_count();
}
pub fn span_note_without_error(&self, sp: Span, msg: &str) {
self.emit.borrow_mut().emit(Some(sp), msg, None, Note);
pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.emit.borrow_mut().emit(Some(&sp.into()), msg, None, Note);
}
pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! {
pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
self.span_bug(sp, &format!("unimplemented {}", msg));
}
pub fn fatal(&self, msg: &str) -> FatalError {
@ -502,15 +583,14 @@ impl Handler {
pub fn has_errors(&self) -> bool {
self.err_count.get() > 0
}
pub fn abort_if_errors(&self) {
let s;
match self.err_count.get() {
0 => {
let delayed_bug = self.delayed_span_bug.borrow();
match *delayed_bug {
Some((span, ref errmsg)) => {
self.span_bug(span, errmsg);
Some((ref span, ref errmsg)) => {
self.span_bug(span.clone(), errmsg);
},
_ => {}
}
@ -526,27 +606,24 @@ impl Handler {
panic!(self.fatal(&s));
}
pub fn emit(&self,
sp: Option<Span>,
msp: Option<&MultiSpan>,
msg: &str,
lvl: Level) {
if lvl == Warning && !self.can_emit_warnings { return }
self.emit.borrow_mut().emit(sp, msg, None, lvl);
self.emit.borrow_mut().emit(msp, msg, None, lvl);
}
pub fn emit_with_code(&self,
sp: Option<Span>,
msp: Option<&MultiSpan>,
msg: &str,
code: &str,
lvl: Level) {
if lvl == Warning && !self.can_emit_warnings { return }
self.emit.borrow_mut().emit(sp, msg, Some(code), lvl);
self.emit.borrow_mut().emit(msp, msg, Some(code), lvl);
}
pub fn custom_emit(&self, sp: RenderSpan, msg: &str, lvl: Level) {
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(sp, msg, lvl);
self.emit.borrow_mut().custom_emit(&rsp, msg, lvl);
}
}

View file

@ -4,5 +4,5 @@ all:
cp foo.rs $(TMPDIR)
cd $(TMPDIR)
-$(RUSTC) -Z unstable-options --error-format=json foo.rs 2>foo.log
grep -q '{"message":"unresolved name `y`","code":{"code":"E0425","explanation":"\\nAn unresolved name was used. Example of erroneous codes.*"},"level":"error","span":{"file_name":"foo.rs","byte_start":496,"byte_end":497,"line_start":12,"line_end":12,"column_start":18,"column_end":19},"children":\[\]}' foo.log
grep -q '{"message":".*","code":{"code":"E0277","explanation":"\\nYou tried.*"},"level":"error","span":{.*},"children":\[{"message":"the .*","code":null,"level":"help","span":{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":0,"column_end":0},"children":\[\]},{"message":" <u8 as core::ops::Add>","code":null,"level":"help",' foo.log
grep -q '{"message":"unresolved name `y`","code":{"code":"E0425","explanation":"\\nAn unresolved name was used. Example of erroneous codes.*"},"level":"error","spans":\[{"file_name":"foo.rs","byte_start":496,"byte_end":497,"line_start":12,"line_end":12,"column_start":18,"column_end":19}\],"children":\[\]}' foo.log
grep -q '{"message":".*","code":{"code":"E0277","explanation":"\\nYou tried.*"},"level":"error","spans":\[{.*}\],"children":\[{"message":"the .*","code":null,"level":"help","spans":\[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":0,"column_end":0}\],"children":\[\]},{"message":" <u8 as core::ops::Add>","code":null,"level":"help",' foo.log