make it possible to test if HIR is dirty

This requires passing in the dirty-node set explicitly since HIR nodes
wind up added to the graph either way.
This commit is contained in:
Niko Matsakis 2016-07-27 14:36:21 -04:00
parent e1d2bc2916
commit 775bd93d72
3 changed files with 68 additions and 16 deletions

View File

@ -150,6 +150,7 @@ impl<D: Clone + Debug> DepNode<D> {
check! { check! {
CollectItem, CollectItem,
BorrowCheck, BorrowCheck,
Hir,
TransCrateItem, TransCrateItem,
TypeckItemType, TypeckItemType,
TypeckItemBody, TypeckItemBody,

View File

@ -24,10 +24,13 @@
//! Errors are reported if we are in the suitable configuration but //! Errors are reported if we are in the suitable configuration but
//! the required condition is not met. //! the required condition is not met.
use super::directory::RetracedDefIdDirectory;
use super::load::DirtyNodes;
use rustc::dep_graph::{DepGraphQuery, DepNode}; use rustc::dep_graph::{DepGraphQuery, DepNode};
use rustc::hir; use rustc::hir;
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::hir::intravisit::Visitor; use rustc::hir::intravisit::Visitor;
use rustc_data_structures::fnv::FnvHashSet;
use syntax::ast::{self, Attribute, MetaItem}; use syntax::ast::{self, Attribute, MetaItem};
use syntax::attr::AttrMetaMethods; use syntax::attr::AttrMetaMethods;
use syntax::parse::token::InternedString; use syntax::parse::token::InternedString;
@ -38,19 +41,33 @@ const CLEAN: &'static str = "rustc_clean";
const LABEL: &'static str = "label"; const LABEL: &'static str = "label";
const CFG: &'static str = "cfg"; const CFG: &'static str = "cfg";
pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
dirty_inputs: &DirtyNodes,
retraced: &RetracedDefIdDirectory) {
// can't add `#[rustc_dirty]` etc without opting in to this feature
if !tcx.sess.features.borrow().rustc_attrs {
return;
}
let _ignore = tcx.dep_graph.in_ignore(); let _ignore = tcx.dep_graph.in_ignore();
let dirty_inputs: FnvHashSet<DepNode<DefId>> =
dirty_inputs.iter()
.filter_map(|d| retraced.map(d))
.collect();
let query = tcx.dep_graph.query(); let query = tcx.dep_graph.query();
debug!("query-nodes: {:?}", query.nodes());
let krate = tcx.map.krate(); let krate = tcx.map.krate();
krate.visit_all_items(&mut DirtyCleanVisitor { krate.visit_all_items(&mut DirtyCleanVisitor {
tcx: tcx, tcx: tcx,
query: &query, query: &query,
dirty_inputs: dirty_inputs,
}); });
} }
pub struct DirtyCleanVisitor<'a, 'tcx:'a> { pub struct DirtyCleanVisitor<'a, 'tcx:'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>,
query: &'a DepGraphQuery<DefId>, query: &'a DepGraphQuery<DefId>,
dirty_inputs: FnvHashSet<DepNode<DefId>>,
} }
impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> { impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
@ -81,10 +98,13 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
return true; return true;
} }
} }
return false;
} }
} }
debug!("check_config: no match found");
return false; self.tcx.sess.span_fatal(
attr.span,
&format!("no cfg attribute"));
} }
fn dep_node(&self, attr: &Attribute, def_id: DefId) -> DepNode<DefId> { fn dep_node(&self, attr: &Attribute, def_id: DefId) -> DepNode<DefId> {
@ -105,29 +125,59 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
self.tcx.sess.span_fatal(attr.span, "no `label` found"); self.tcx.sess.span_fatal(attr.span, "no `label` found");
} }
fn dep_node_str(&self, dep_node: DepNode<DefId>) -> DepNode<String> { fn dep_node_str(&self, dep_node: &DepNode<DefId>) -> DepNode<String> {
dep_node.map_def(|&def_id| Some(self.tcx.item_path_str(def_id))).unwrap() dep_node.map_def(|&def_id| Some(self.tcx.item_path_str(def_id))).unwrap()
} }
fn assert_dirty(&self, item: &hir::Item, dep_node: DepNode<DefId>) { fn assert_dirty(&self, item: &hir::Item, dep_node: DepNode<DefId>) {
debug!("assert_dirty({:?})", dep_node); debug!("assert_dirty({:?})", dep_node);
if self.query.contains_node(&dep_node) { match dep_node {
let dep_node_str = self.dep_node_str(dep_node); DepNode::Hir(_) => {
self.tcx.sess.span_err( // HIR nodes are inputs, so if we are asserting that the HIR node is
item.span, // dirty, we check the dirty input set.
&format!("`{:?}` found in dep graph, but should be dirty", dep_node_str)); if !self.dirty_inputs.contains(&dep_node) {
let dep_node_str = self.dep_node_str(&dep_node);
self.tcx.sess.span_err(
item.span,
&format!("`{:?}` not found in dirty set, but should be dirty", dep_node_str));
}
}
_ => {
// Other kinds of nodes would be targets, so check if
// the dep-graph contains the node.
if self.query.contains_node(&dep_node) {
let dep_node_str = self.dep_node_str(&dep_node);
self.tcx.sess.span_err(
item.span,
&format!("`{:?}` found in dep graph, but should be dirty", dep_node_str));
}
}
} }
} }
fn assert_clean(&self, item: &hir::Item, dep_node: DepNode<DefId>) { fn assert_clean(&self, item: &hir::Item, dep_node: DepNode<DefId>) {
debug!("assert_clean({:?})", dep_node); debug!("assert_clean({:?})", dep_node);
if !self.query.contains_node(&dep_node) { match dep_node {
let dep_node_str = self.dep_node_str(dep_node); DepNode::Hir(_) => {
self.tcx.sess.span_err( // For HIR nodes, check the inputs.
item.span, if self.dirty_inputs.contains(&dep_node) {
&format!("`{:?}` not found in dep graph, but should be clean", dep_node_str)); let dep_node_str = self.dep_node_str(&dep_node);
self.tcx.sess.span_err(
item.span,
&format!("`{:?}` found in dirty-node set, but should be clean", dep_node_str));
}
}
_ => {
// Otherwise, check if the dep-node exists.
if !self.query.contains_node(&dep_node) {
let dep_node_str = self.dep_node_str(&dep_node);
self.tcx.sess.span_err(
item.span,
&format!("`{:?}` not found in dep graph, but should be clean", dep_node_str));
}
}
} }
} }
} }

View File

@ -28,7 +28,7 @@ use super::dirty_clean;
use super::hash::*; use super::hash::*;
use super::util::*; use super::util::*;
type DirtyNodes = FnvHashSet<DepNode<DefPathIndex>>; pub type DirtyNodes = FnvHashSet<DepNode<DefPathIndex>>;
type CleanEdges = Vec<(DepNode<DefId>, DepNode<DefId>)>; type CleanEdges = Vec<(DepNode<DefId>, DepNode<DefId>)>;
@ -45,7 +45,6 @@ pub fn load_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
let _ignore = tcx.dep_graph.in_ignore(); let _ignore = tcx.dep_graph.in_ignore();
load_dep_graph_if_exists(tcx); load_dep_graph_if_exists(tcx);
dirty_clean::check_dirty_clean_annotations(tcx);
} }
fn load_dep_graph_if_exists<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { fn load_dep_graph_if_exists<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
@ -184,6 +183,8 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let work_products = try!(<Vec<SerializedWorkProduct>>::decode(&mut work_product_decoder)); let work_products = try!(<Vec<SerializedWorkProduct>>::decode(&mut work_product_decoder));
reconcile_work_products(tcx, work_products, &dirty_target_nodes); reconcile_work_products(tcx, work_products, &dirty_target_nodes);
dirty_clean::check_dirty_clean_annotations(tcx, &dirty_raw_source_nodes, &retraced);
Ok(()) Ok(())
} }