about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2017-08-12 15:37:28 -0700
committerAlex Crichton <alex@alexcrichton.com>2017-08-24 15:18:06 -0700
commit8e95b3a939d73e1bc0bd65b136dda3e5b2a23425 (patch)
tree1e73744431d0bfce780c5d6edeede1557760511d
parent2c0558f635861533e2fcb4298ea93250cdfc2c58 (diff)
downloadrust-8e95b3a939d73e1bc0bd65b136dda3e5b2a23425.tar.gz
rust-8e95b3a939d73e1bc0bd65b136dda3e5b2a23425.zip
rustc: Capture diagnostics from all queries
This commit alters the `rustc::ty::maps` implementation to ensure that all
output diagnostics from the compiler are tracked for the duration of each query.
These are then intended to be replayed back the first time a cached value is
loaded, and otherwise the cache should operate the same as it does today.

Closes #42513
-rw-r--r--src/librustc/ty/maps.rs82
-rw-r--r--src/librustc_errors/diagnostic_builder.rs13
-rw-r--r--src/librustc_errors/lib.rs23
3 files changed, 90 insertions, 28 deletions
diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs
index 6e02e38aee1..f1c624a94e3 100644
--- a/src/librustc/ty/maps.rs
+++ b/src/librustc/ty/maps.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 use dep_graph::{DepConstructor, DepNode, DepNodeIndex};
+use errors::{Diagnostic, DiagnosticBuilder};
 use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
 use hir::def::Def;
 use hir;
@@ -32,7 +33,7 @@ use util::common::{profq_msg, ProfileQueriesMsg};
 
 use rustc_data_structures::indexed_vec::IndexVec;
 use rustc_data_structures::fx::FxHashMap;
-use std::cell::{RefCell, RefMut};
+use std::cell::{RefCell, RefMut, Cell};
 use std::fmt::Debug;
 use std::hash::Hash;
 use std::marker::PhantomData;
@@ -188,7 +189,18 @@ impl<'tcx> Value<'tcx> for ty::SymbolName {
 
 struct QueryMap<D: QueryDescription> {
     phantom: PhantomData<D>,
-    map: FxHashMap<D::Key, (D::Value, DepNodeIndex)>,
+    map: FxHashMap<D::Key, QueryValue<D::Value>>,
+}
+
+struct QueryValue<T> {
+    value: T,
+    index: DepNodeIndex,
+    diagnostics: Option<Box<QueryDiagnostics>>,
+}
+
+struct QueryDiagnostics {
+    diagnostics: Vec<Diagnostic>,
+    emitted_diagnostics: Cell<bool>,
 }
 
 impl<M: QueryDescription> QueryMap<M> {
@@ -618,10 +630,20 @@ macro_rules! define_maps {
                     )
                 );
 
-                if let Some(&(ref result, dep_node_index)) = tcx.maps.$name.borrow().map.get(&key) {
-                    tcx.dep_graph.read_index(dep_node_index);
+                if let Some(value) = tcx.maps.$name.borrow().map.get(&key) {
+                    if let Some(ref d) = value.diagnostics {
+                        if !d.emitted_diagnostics.get() {
+                            d.emitted_diagnostics.set(true);
+                            let handle = tcx.sess.diagnostic();
+                            for diagnostic in d.diagnostics.iter() {
+                                DiagnosticBuilder::new_diagnostic(handle, diagnostic.clone())
+                                    .emit();
+                            }
+                        }
+                    }
                     profq_msg!(tcx, ProfileQueriesMsg::CacheHit);
-                    return Ok(f(result));
+                    tcx.dep_graph.read_index(value.index);
+                    return Ok(f(&value.value));
                 }
                 // else, we are going to run the provider:
                 profq_msg!(tcx, ProfileQueriesMsg::ProviderBegin);
@@ -633,36 +655,52 @@ macro_rules! define_maps {
                     span = key.default_span(tcx)
                 }
 
-                let (result, dep_node_index) = tcx.cycle_check(span, Query::$name(key), || {
+                let res = tcx.cycle_check(span, Query::$name(key), || {
                     let dep_node = Self::to_dep_node(tcx, &key);
 
-                    if dep_node.kind.is_anon() {
-                        tcx.dep_graph.with_anon_task(dep_node.kind, || {
-                            let provider = tcx.maps.providers[key.map_crate()].$name;
-                            provider(tcx.global_tcx(), key)
-                        })
-                    } else {
-                        fn run_provider<'a, 'tcx, 'lcx>(tcx: TyCtxt<'a, 'tcx, 'lcx>,
-                                                        key: $K)
-                                                        -> $V {
-                            let provider = tcx.maps.providers[key.map_crate()].$name;
-                            provider(tcx.global_tcx(), key)
+                    tcx.sess.diagnostic().track_diagnostics(|| {
+                        if dep_node.kind.is_anon() {
+                            tcx.dep_graph.with_anon_task(dep_node.kind, || {
+                                let provider = tcx.maps.providers[key.map_crate()].$name;
+                                provider(tcx.global_tcx(), key)
+                            })
+                        } else {
+                            fn run_provider<'a, 'tcx, 'lcx>(tcx: TyCtxt<'a, 'tcx, 'lcx>,
+                                                            key: $K)
+                                                            -> $V {
+                                let provider = tcx.maps.providers[key.map_crate()].$name;
+                                provider(tcx.global_tcx(), key)
+                            }
+
+                            tcx.dep_graph.with_task(dep_node, tcx, key, run_provider)
                         }
-
-                        tcx.dep_graph.with_task(dep_node, tcx, key, run_provider)
-                    }
+                    })
                 })?;
                 profq_msg!(tcx, ProfileQueriesMsg::ProviderEnd);
+                let ((result, dep_node_index), diagnostics) = res;
 
                 tcx.dep_graph.read_index(dep_node_index);
 
+                let value = QueryValue {
+                    value: result,
+                    index: dep_node_index,
+                    diagnostics: if diagnostics.len() == 0 {
+                        None
+                    } else {
+                        Some(Box::new(QueryDiagnostics {
+                            diagnostics,
+                            emitted_diagnostics: Cell::new(true),
+                        }))
+                    },
+                };
+
                 Ok(f(&tcx.maps
                          .$name
                          .borrow_mut()
                          .map
                          .entry(key)
-                         .or_insert((result, dep_node_index))
-                         .0))
+                         .or_insert(value)
+                         .value))
             }
 
             pub fn try_get(tcx: TyCtxt<'a, $tcx, 'lcx>, span: Span, key: $K)
diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs
index 8d7ce4eb4f6..0a811989350 100644
--- a/src/librustc_errors/diagnostic_builder.rs
+++ b/src/librustc_errors/diagnostic_builder.rs
@@ -98,7 +98,7 @@ impl<'a> DiagnosticBuilder<'a> {
             }
         };
 
-        self.handler.emitter.borrow_mut().emit(&self);
+        self.handler.emit_db(&self);
         self.cancel();
 
         if is_error {
@@ -178,10 +178,13 @@ impl<'a> DiagnosticBuilder<'a> {
                          code: Option<String>,
                          message: &str)
                          -> DiagnosticBuilder<'a> {
-        DiagnosticBuilder {
-            handler,
-            diagnostic: Diagnostic::new_with_code(level, code, message)
-        }
+        let diagnostic = Diagnostic::new_with_code(level, code, message);
+        DiagnosticBuilder::new_diagnostic(handler, diagnostic)
+    }
+
+    /// Creates a new `DiagnosticBuilder` with an already constructed diagnostic.
+    pub fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> {
+        DiagnosticBuilder { handler, diagnostic }
     }
 }
 
diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs
index 12b5ccf4837..a51e6022350 100644
--- a/src/librustc_errors/lib.rs
+++ b/src/librustc_errors/lib.rs
@@ -35,8 +35,9 @@ use emitter::{Emitter, EmitterWriter};
 
 use std::borrow::Cow;
 use std::cell::{RefCell, Cell};
-use std::{error, fmt};
+use std::mem;
 use std::rc::Rc;
+use std::{error, fmt};
 
 mod diagnostic;
 mod diagnostic_builder;
@@ -275,6 +276,7 @@ pub struct Handler {
     treat_err_as_bug: bool,
     continue_after_error: Cell<bool>,
     delayed_span_bug: RefCell<Option<(MultiSpan, String)>>,
+    tracked_diagnostics: RefCell<Option<Vec<Diagnostic>>>,
 }
 
 impl Handler {
@@ -298,6 +300,7 @@ impl Handler {
             treat_err_as_bug,
             continue_after_error: Cell::new(true),
             delayed_span_bug: RefCell::new(None),
+            tracked_diagnostics: RefCell::new(None),
         }
     }
 
@@ -547,6 +550,24 @@ impl Handler {
             self.abort_if_errors();
         }
     }
+
+    pub fn track_diagnostics<F, R>(&self, f: F) -> (R, Vec<Diagnostic>)
+        where F: FnOnce() -> R
+    {
+        let prev = mem::replace(&mut *self.tracked_diagnostics.borrow_mut(),
+                                Some(Vec::new()));
+        let ret = f();
+        let diagnostics = mem::replace(&mut *self.tracked_diagnostics.borrow_mut(), prev)
+            .unwrap();
+        (ret, diagnostics)
+    }
+
+    fn emit_db(&self, db: &DiagnosticBuilder) {
+        if let Some(ref mut list) = *self.tracked_diagnostics.borrow_mut() {
+            list.push((**db).clone());
+        }
+        self.emitter.borrow_mut().emit(db);
+    }
 }