about summary refs log tree commit diff
path: root/src/librustc_errors
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-09-23 04:45:21 +0200
committerMazdak Farrokhzad <twingoow@gmail.com>2019-09-23 17:50:06 +0200
commitae8b3e8fc6e95a0f68fc49338f394d670e233883 (patch)
tree1bf3feee7671515ce6921ea2c1bf041eb3e96e93 /src/librustc_errors
parent66bf391c3aabfc77f5f7139fc9e6944f995d574e (diff)
downloadrust-ae8b3e8fc6e95a0f68fc49338f394d670e233883.tar.gz
rust-ae8b3e8fc6e95a0f68fc49338f394d670e233883.zip
Introduce a diagnostic stashing API.
Diffstat (limited to 'src/librustc_errors')
-rw-r--r--src/librustc_errors/diagnostic_builder.rs41
-rw-r--r--src/librustc_errors/lib.rs84
2 files changed, 100 insertions, 25 deletions
diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs
index e85388bfea2..cc60bf89c7e 100644
--- a/src/librustc_errors/diagnostic_builder.rs
+++ b/src/librustc_errors/diagnostic_builder.rs
@@ -1,10 +1,6 @@
-use crate::Diagnostic;
-use crate::DiagnosticId;
-use crate::DiagnosticStyledString;
-use crate::Applicability;
+use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString};
+use crate::{Applicability, Level, Handler, StashKey};
 
-use crate::Level;
-use crate::Handler;
 use std::fmt::{self, Debug};
 use std::ops::{Deref, DerefMut};
 use std::thread::panicking;
@@ -117,18 +113,30 @@ impl<'a> DiagnosticBuilder<'a> {
         }
     }
 
-    /// Buffers the diagnostic for later emission, unless handler
-    /// has disabled such buffering.
-    pub fn buffer(mut self, buffered_diagnostics: &mut Vec<Diagnostic>) {
+    /// Stashes diagnostic for possible later improvement in a different,
+    /// later stage of the compiler. The diagnostic can be accessed with
+    /// the provided `span` and `key` through `.steal_diagnostic` on `Handler`.
+    ///
+    /// As with `buffer`, this is unless the handler has disabled such buffering.
+    pub fn stash(self, span: Span, key: StashKey) {
+        if let Some((diag, handler)) = self.into_diagnostic() {
+            handler.stash_diagnostic(span, key, diag);
+        }
+    }
+
+    /// Converts the builder to a `Diagnostic` for later emission,
+    /// unless handler has disabled such buffering.
+    pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> {
         if self.0.handler.flags.dont_buffer_diagnostics ||
             self.0.handler.flags.treat_err_as_bug.is_some()
         {
             self.emit();
-            return;
+            return None;
         }
 
-        // We need to use `ptr::read` because `DiagnosticBuilder`
-        // implements `Drop`.
+        let handler = self.0.handler;
+
+        // We need to use `ptr::read` because `DiagnosticBuilder` implements `Drop`.
         let diagnostic;
         unsafe {
             diagnostic = std::ptr::read(&self.0.diagnostic);
@@ -137,7 +145,14 @@ impl<'a> DiagnosticBuilder<'a> {
         // Logging here is useful to help track down where in logs an error was
         // actually emitted.
         debug!("buffer: diagnostic={:?}", diagnostic);
-        buffered_diagnostics.push(diagnostic);
+
+        Some((diagnostic, handler))
+    }
+
+    /// Buffers the diagnostic for later emission,
+    /// unless handler has disabled such buffering.
+    pub fn buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>) {
+        buffered_diagnostics.extend(self.into_diagnostic().map(|(diag, _)| diag));
     }
 
     /// Convenience function for internal use, clients should use one of the
diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs
index 1fe5b71d7b1..40f63ae1eee 100644
--- a/src/librustc_errors/lib.rs
+++ b/src/librustc_errors/lib.rs
@@ -17,7 +17,7 @@ use emitter::{Emitter, EmitterWriter};
 use registry::Registry;
 
 use rustc_data_structures::sync::{self, Lrc, Lock};
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_data_structures::stable_hasher::StableHasher;
 
 use std::borrow::Cow;
@@ -326,6 +326,18 @@ struct HandlerInner {
     /// this handler. These hashes is used to avoid emitting the same error
     /// twice.
     emitted_diagnostics: FxHashSet<u128>,
+
+    /// Stashed diagnostics emitted in one stage of the compiler that may be
+    /// stolen by other stages (e.g. to improve them and add more information).
+    /// The stashed diagnostics count towards the total error count.
+    /// When `.abort_if_errors()` is called, these are also emitted.
+    stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>,
+}
+
+/// A key denoting where from a diagnostic was stashed.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+pub enum StashKey {
+    ItemNoType,
 }
 
 fn default_track_diagnostic(_: &Diagnostic) {}
@@ -354,7 +366,9 @@ pub struct HandlerFlags {
 
 impl Drop for HandlerInner {
     fn drop(&mut self) {
-        if self.err_count == 0 {
+        self.emit_stashed_diagnostics();
+
+        if !self.has_errors() {
             let bugs = std::mem::replace(&mut self.delayed_span_bugs, Vec::new());
             let has_bugs = !bugs.is_empty();
             for bug in bugs {
@@ -419,6 +433,7 @@ impl Handler {
                 taught_diagnostics: Default::default(),
                 emitted_diagnostic_codes: Default::default(),
                 emitted_diagnostics: Default::default(),
+                stashed_diagnostics: Default::default(),
             }),
         }
     }
@@ -445,6 +460,31 @@ impl Handler {
         inner.emitted_diagnostics = Default::default();
         inner.deduplicated_err_count = 0;
         inner.err_count = 0;
+        inner.stashed_diagnostics.clear();
+    }
+
+    /// Stash a given diagnostic with the given `Span` and `StashKey` as the key for later stealing.
+    /// If the diagnostic with this `(span, key)` already exists, this will result in an ICE.
+    pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: Diagnostic) {
+        if let Some(old) = self.inner.borrow_mut().stashed_diagnostics.insert((span, key), diag) {
+            // We are removing a previously stashed diagnostic which should not happen.
+            // Create a builder and drop it on the floor to get an ICE.
+            drop(DiagnosticBuilder::new_diagnostic(self, old));
+        }
+    }
+
+    /// Steal a previously stashed diagnostic with the given `Span` and `StashKey` as the key.
+    pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_>> {
+        self.inner
+            .borrow_mut()
+            .stashed_diagnostics
+            .remove(&(span, key))
+            .map(|diag| DiagnosticBuilder::new_diagnostic(self, diag))
+    }
+
+    /// Emit all stashed diagnostics.
+    pub fn emit_stashed_diagnostics(&self) {
+        self.inner.borrow_mut().emit_stashed_diagnostics();
     }
 
     pub fn struct_dummy(&self) -> DiagnosticBuilder<'_> {
@@ -617,11 +657,11 @@ impl Handler {
     }
 
     pub fn err_count(&self) -> usize {
-        self.inner.borrow().err_count
+        self.inner.borrow().err_count()
     }
 
     pub fn has_errors(&self) -> bool {
-        self.err_count() > 0
+        self.inner.borrow().has_errors()
     }
 
     pub fn print_error_count(&self, registry: &Registry) {
@@ -629,11 +669,11 @@ impl Handler {
     }
 
     pub fn abort_if_errors(&self) {
-        self.inner.borrow().abort_if_errors()
+        self.inner.borrow_mut().abort_if_errors()
     }
 
     pub fn abort_if_errors_and_should_abort(&self) {
-        self.inner.borrow().abort_if_errors_and_should_abort()
+        self.inner.borrow_mut().abort_if_errors_and_should_abort()
     }
 
     pub fn must_teach(&self, code: &DiagnosticId) -> bool {
@@ -671,6 +711,12 @@ impl HandlerInner {
         self.emitter.emit_diagnostic(&db);
     }
 
+    /// Emit all stashed diagnostics.
+    fn emit_stashed_diagnostics(&mut self) {
+        let diags = self.stashed_diagnostics.drain(..).map(|x| x.1).collect::<Vec<_>>();
+        diags.iter().for_each(|diag| self.emit_diagnostic(diag));
+    }
+
     fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) {
         if diagnostic.cancelled() {
             return;
@@ -713,10 +759,12 @@ impl HandlerInner {
     }
 
     fn treat_err_as_bug(&self) -> bool {
-        self.flags.treat_err_as_bug.map(|c| self.err_count >= c).unwrap_or(false)
+        self.flags.treat_err_as_bug.map(|c| self.err_count() >= c).unwrap_or(false)
     }
 
     fn print_error_count(&mut self, registry: &Registry) {
+        self.emit_stashed_diagnostics();
+
         let s = match self.deduplicated_err_count {
             0 => return,
             1 => "aborting due to previous error".to_string(),
@@ -760,14 +808,26 @@ impl HandlerInner {
         }
     }
 
-    fn abort_if_errors_and_should_abort(&self) {
-        if self.err_count > 0 && !self.continue_after_error {
+    fn err_count(&self) -> usize {
+        self.err_count + self.stashed_diagnostics.len()
+    }
+
+    fn has_errors(&self) -> bool {
+        self.err_count() > 0
+    }
+
+    fn abort_if_errors_and_should_abort(&mut self) {
+        self.emit_stashed_diagnostics();
+
+        if self.has_errors() && !self.continue_after_error {
             FatalError.raise();
         }
     }
 
-    fn abort_if_errors(&self) {
-        if self.err_count > 0 {
+    fn abort_if_errors(&mut self) {
+        self.emit_stashed_diagnostics();
+
+        if self.has_errors() {
             FatalError.raise();
         }
     }
@@ -826,7 +886,7 @@ impl HandlerInner {
 
     fn panic_if_treat_err_as_bug(&self) {
         if self.treat_err_as_bug() {
-            let s = match (self.err_count, self.flags.treat_err_as_bug.unwrap_or(0)) {
+            let s = match (self.err_count(), self.flags.treat_err_as_bug.unwrap_or(0)) {
                 (0, _) => return,
                 (1, 1) => "aborting due to `-Z treat-err-as-bug=1`".to_string(),
                 (1, _) => return,