Auto merge of #44046 - alexcrichton:capture-diagnostics, r=michaelwoerister
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
This commit is contained in:
commit
2aeb5930f3
@ -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)
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user