about summary refs log tree commit diff
path: root/src/librustc_errors/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustc_errors/lib.rs')
-rw-r--r--src/librustc_errors/lib.rs273
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,