diff options
Diffstat (limited to 'src/librustc_errors/lib.rs')
| -rw-r--r-- | src/librustc_errors/lib.rs | 273 |
1 files changed, 148 insertions, 125 deletions
diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index d9b0f4ac8a6..605cfc5ed12 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -18,10 +18,12 @@ #![feature(range_contains)] #![cfg_attr(unix, feature(libc))] #![feature(conservative_impl_trait)] +#![feature(i128_type)] extern crate term; #[cfg(unix)] extern crate libc; +extern crate rustc_data_structures; extern crate serialize as rustc_serialize; extern crate syntax_pos; @@ -31,6 +33,9 @@ use self::Level::*; use emitter::{Emitter, EmitterWriter}; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::stable_hasher::StableHasher; + use std::borrow::Cow; use std::cell::{RefCell, Cell}; use std::mem; @@ -47,21 +52,7 @@ mod lock; use syntax_pos::{BytePos, Loc, FileLinesResult, FileMap, FileName, MultiSpan, Span, NO_EXPANSION}; -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] -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(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 - /// into the lines in place of the code covered by each span. - Suggestion(CodeSuggestion), -} - -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct CodeSuggestion { /// Each substitute can have multiple variants due to multiple /// applicable suggestions @@ -71,26 +62,34 @@ pub struct CodeSuggestion { /// /// ``` /// vec![ - /// (0..3, vec!["a", "x"]), - /// (4..7, vec!["b", "y"]), + /// Substitution { parts: vec![(0..3, "a"), (4..7, "b")] }, + /// Substitution { parts: vec![(0..3, "x"), (4..7, "y")] }, /// ] /// ``` /// /// or by replacing the entire span: /// /// ``` - /// vec![(0..7, vec!["a.b", "x.y"])] + /// vec![ + /// Substitution { parts: vec![(0..7, "a.b")] }, + /// Substitution { parts: vec![(0..7, "x.y")] }, + /// ] /// ``` - pub substitution_parts: Vec<Substitution>, + pub substitutions: Vec<Substitution>, pub msg: String, pub show_code_when_inline: bool, } -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] /// See the docs on `CodeSuggestion::substitutions` pub struct Substitution { + pub parts: Vec<SubstitutionPart>, +} + +#[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] +pub struct SubstitutionPart { pub span: Span, - pub substitutions: Vec<String>, + pub snippet: String, } pub trait CodeMapper { @@ -104,18 +103,8 @@ pub trait CodeMapper { } impl CodeSuggestion { - /// Returns the number of substitutions - fn substitutions(&self) -> usize { - self.substitution_parts[0].substitutions.len() - } - - /// Returns the number of substitutions - fn substitution_spans<'a>(&'a self) -> impl Iterator<Item = Span> + 'a { - self.substitution_parts.iter().map(|sub| sub.span) - } - - /// Returns the assembled code suggestions and wether they should be shown with an underline. - pub fn splice_lines(&self, cm: &CodeMapper) -> Vec<(String, bool)> { + /// Returns the assembled code suggestions and whether they should be shown with an underline. + pub fn splice_lines(&self, cm: &CodeMapper) -> Vec<(String, Vec<SubstitutionPart>)> { use syntax_pos::{CharPos, Loc, Pos}; fn push_trailing(buf: &mut String, @@ -137,60 +126,42 @@ impl CodeSuggestion { } } - if self.substitution_parts.is_empty() { - return vec![(String::new(), false)]; - } - - let mut primary_spans: Vec<_> = self.substitution_parts - .iter() - .map(|sub| (sub.span, &sub.substitutions)) - .collect(); - - // Assumption: all spans are in the same file, and all spans - // are disjoint. Sort in ascending order. - primary_spans.sort_by_key(|sp| sp.0.lo()); - - // Find the bounding span. - let lo = primary_spans.iter().map(|sp| sp.0.lo()).min().unwrap(); - let hi = primary_spans.iter().map(|sp| sp.0.hi()).min().unwrap(); - let bounding_span = Span::new(lo, hi, 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 - // (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(bounding_span.lo()); - prev_hi.col = CharPos::from_usize(0); - - let mut prev_line = fm.get_line(lines.lines[0].line_index); - let mut bufs = vec![(String::new(), false); self.substitutions()]; - - for (sp, substitutes) in primary_spans { - let cur_lo = cm.lookup_char_pos(sp.lo()); - for (&mut (ref mut buf, ref mut underline), substitute) in bufs.iter_mut() - .zip(substitutes) { + assert!(!self.substitutions.is_empty()); + + self.substitutions.iter().cloned().map(|mut substitution| { + // Assumption: all spans are in the same file, and all spans + // are disjoint. Sort in ascending order. + substitution.parts.sort_by_key(|part| part.span.lo()); + + // Find the bounding span. + let lo = substitution.parts.iter().map(|part| part.span.lo()).min().unwrap(); + let hi = substitution.parts.iter().map(|part| part.span.hi()).min().unwrap(); + let bounding_span = Span::new(lo, hi, 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 + // (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(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 part in &substitution.parts { + let cur_lo = cm.lookup_char_pos(part.span.lo()); if prev_hi.line == cur_lo.line { - push_trailing(buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo)); - - // Only show an underline in the suggestions if the suggestion is not the - // entirety of the code being shown and the displayed code is not multiline. - if prev_line.as_ref().unwrap().trim().len() > 0 - && !substitute.ends_with('\n') - && substitute.lines().count() == 1 - { - *underline = true; - } + push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo)); } else { - *underline = false; - push_trailing(buf, prev_line.as_ref(), &prev_hi, None); + push_trailing(&mut buf, prev_line.as_ref(), &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) { @@ -202,22 +173,20 @@ impl CodeSuggestion { buf.push_str(&cur_line[..cur_lo.col.to_usize()]); } } - buf.push_str(substitute); + buf.push_str(&part.snippet); + prev_hi = cm.lookup_char_pos(part.span.hi()); + prev_line = fm.get_line(prev_hi.line - 1); } - prev_hi = cm.lookup_char_pos(sp.hi()); - prev_line = fm.get_line(prev_hi.line - 1); - } - for &mut (ref mut buf, _) in &mut bufs { // if the replacement already ends with a newline, don't print the next line if !buf.ends_with('\n') { - push_trailing(buf, prev_line.as_ref(), &prev_hi, None); + push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None); } // remove trailing newlines while buf.ends_with('\n') { buf.pop(); } - } - bufs + (buf, substitution.parts) + }).collect() } } @@ -257,20 +226,32 @@ impl error::Error for ExplicitBug { } } -pub use diagnostic::{Diagnostic, SubDiagnostic, DiagnosticStyledString}; +pub use diagnostic::{Diagnostic, SubDiagnostic, DiagnosticStyledString, DiagnosticId}; pub use diagnostic_builder::DiagnosticBuilder; /// A handler deals with errors; certain errors /// (fatal, bug, unimpl) may cause immediate exit, /// others log errors for later reporting. pub struct Handler { + pub flags: HandlerFlags, + err_count: Cell<usize>, emitter: RefCell<Box<Emitter>>, - pub can_emit_warnings: bool, - treat_err_as_bug: bool, continue_after_error: Cell<bool>, delayed_span_bug: RefCell<Option<Diagnostic>>, tracked_diagnostics: RefCell<Option<Vec<Diagnostic>>>, + + // This set contains a hash of every diagnostic that has been emitted by + // this handler. These hashes is used to avoid emitting the same error + // twice. + emitted_diagnostics: RefCell<FxHashSet<u128>>, +} + +#[derive(Default)] +pub struct HandlerFlags { + pub can_emit_warnings: bool, + pub treat_err_as_bug: bool, + pub external_macro_backtrace: bool, } impl Handler { @@ -279,22 +260,46 @@ impl Handler { treat_err_as_bug: bool, cm: Option<Rc<CodeMapper>>) -> Handler { - let emitter = Box::new(EmitterWriter::stderr(color_config, cm)); - Handler::with_emitter(can_emit_warnings, treat_err_as_bug, emitter) + Handler::with_tty_emitter_and_flags( + color_config, + cm, + HandlerFlags { + can_emit_warnings, + treat_err_as_bug, + .. Default::default() + }) + } + + pub fn with_tty_emitter_and_flags(color_config: ColorConfig, + cm: Option<Rc<CodeMapper>>, + flags: HandlerFlags) + -> Handler { + let emitter = Box::new(EmitterWriter::stderr(color_config, cm, false)); + Handler::with_emitter_and_flags(emitter, flags) } pub fn with_emitter(can_emit_warnings: bool, treat_err_as_bug: bool, e: Box<Emitter>) -> Handler { + Handler::with_emitter_and_flags( + e, + HandlerFlags { + can_emit_warnings, + treat_err_as_bug, + .. Default::default() + }) + } + + pub fn with_emitter_and_flags(e: Box<Emitter>, flags: HandlerFlags) -> Handler { Handler { + flags, err_count: Cell::new(0), emitter: RefCell::new(e), - can_emit_warnings, - treat_err_as_bug, continue_after_error: Cell::new(true), delayed_span_bug: RefCell::new(None), tracked_diagnostics: RefCell::new(None), + emitted_diagnostics: RefCell::new(FxHashSet()), } } @@ -318,7 +323,7 @@ impl Handler { -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); result.set_span(sp); - if !self.can_emit_warnings { + if !self.flags.can_emit_warnings { result.cancel(); } result @@ -326,19 +331,19 @@ impl Handler { pub fn struct_span_warn_with_code<'a, S: Into<MultiSpan>>(&'a self, sp: S, msg: &str, - code: &str) + code: DiagnosticId) -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); result.set_span(sp); - result.code(code.to_owned()); - if !self.can_emit_warnings { + result.code(code); + if !self.flags.can_emit_warnings { result.cancel(); } result } pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); - if !self.can_emit_warnings { + if !self.flags.can_emit_warnings { result.cancel(); } result @@ -354,20 +359,24 @@ impl Handler { pub fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self, sp: S, msg: &str, - code: &str) + code: DiagnosticId) -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(self, Level::Error, msg); result.set_span(sp); - result.code(code.to_owned()); + result.code(code); result } // FIXME: This method should be removed (every error should have an associated error code). pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { DiagnosticBuilder::new(self, Level::Error, msg) } - pub fn struct_err_with_code<'a>(&'a self, msg: &str, code: &str) -> DiagnosticBuilder<'a> { + pub fn struct_err_with_code<'a>( + &'a self, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(self, Level::Error, msg); - result.code(code.to_owned()); + result.code(code); result } pub fn struct_span_fatal<'a, S: Into<MultiSpan>>(&'a self, @@ -381,11 +390,11 @@ impl Handler { pub fn struct_span_fatal_with_code<'a, S: Into<MultiSpan>>(&'a self, sp: S, msg: &str, - code: &str) + code: DiagnosticId) -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg); result.set_span(sp); - result.code(code.to_owned()); + result.code(code); result } pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { @@ -397,7 +406,7 @@ impl Handler { } fn panic_if_treat_err_as_bug(&self) { - if self.treat_err_as_bug { + if self.flags.treat_err_as_bug { panic!("encountered error with `-Z treat_err_as_bug"); } } @@ -409,7 +418,7 @@ impl Handler { pub fn span_fatal_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, - code: &str) + code: DiagnosticId) -> FatalError { self.emit_with_code(&sp.into(), msg, code, Fatal); FatalError @@ -425,13 +434,13 @@ impl Handler { result.set_span(sp); result } - pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) { + pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) { self.emit_with_code(&sp.into(), msg, code, Error); } pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { self.emit(&sp.into(), msg, Warning); } - pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) { + pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) { self.emit_with_code(&sp.into(), msg, code, Warning); } pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! { @@ -439,7 +448,7 @@ impl Handler { panic!(ExplicitBug); } pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { - if self.treat_err_as_bug { + if self.flags.treat_err_as_bug { self.span_bug(sp, msg); } let mut diagnostic = Diagnostic::new(Level::Bug, msg); @@ -464,7 +473,7 @@ impl Handler { self.span_bug(sp, &format!("unimplemented {}", msg)); } pub fn fatal(&self, msg: &str) -> FatalError { - if self.treat_err_as_bug { + if self.flags.treat_err_as_bug { self.bug(msg); } let mut db = DiagnosticBuilder::new(self, Fatal, msg); @@ -472,7 +481,7 @@ impl Handler { FatalError } pub fn err(&self, msg: &str) { - if self.treat_err_as_bug { + if self.flags.treat_err_as_bug { self.bug(msg); } let mut db = DiagnosticBuilder::new(self, Error, msg); @@ -525,7 +534,7 @@ impl Handler { panic!(self.fatal(&s)); } pub fn emit(&self, msp: &MultiSpan, msg: &str, lvl: Level) { - if lvl == Warning && !self.can_emit_warnings { + if lvl == Warning && !self.flags.can_emit_warnings { return; } let mut db = DiagnosticBuilder::new(self, lvl, msg); @@ -535,11 +544,11 @@ impl Handler { self.abort_if_errors(); } } - pub fn emit_with_code(&self, msp: &MultiSpan, msg: &str, code: &str, lvl: Level) { - if lvl == Warning && !self.can_emit_warnings { + pub fn emit_with_code(&self, msp: &MultiSpan, msg: &str, code: DiagnosticId, lvl: Level) { + if lvl == Warning && !self.flags.can_emit_warnings { return; } - let mut db = DiagnosticBuilder::new_with_code(self, lvl, Some(code.to_owned()), msg); + let mut db = DiagnosticBuilder::new_with_code(self, lvl, Some(code), msg); db.set_span(msp.clone()); db.emit(); if !self.continue_after_error.get() { @@ -559,15 +568,29 @@ impl Handler { } fn emit_db(&self, db: &DiagnosticBuilder) { + let diagnostic = &**db; + if let Some(ref mut list) = *self.tracked_diagnostics.borrow_mut() { - list.push((**db).clone()); + list.push(diagnostic.clone()); + } + + let diagnostic_hash = { + use std::hash::Hash; + let mut hasher = StableHasher::new(); + diagnostic.hash(&mut hasher); + hasher.finish() + }; + + // Only emit the diagnostic if we haven't already emitted an equivalent + // one: + if self.emitted_diagnostics.borrow_mut().insert(diagnostic_hash) { + self.emitter.borrow_mut().emit(db); } - self.emitter.borrow_mut().emit(db); } } -#[derive(Copy, PartialEq, Clone, Debug, RustcEncodable, RustcDecodable)] +#[derive(Copy, PartialEq, Clone, Hash, Debug, RustcEncodable, RustcDecodable)] pub enum Level { Bug, Fatal, |
