1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Debugging code to test the state of the dependency graph just
12 //! after it is loaded from disk. For each node marked with
13 //! `#[rustc_clean]` or `#[rustc_dirty]`, we will check that a
14 //! suitable node for that item either appears or does not appear in
15 //! the dep-graph, as appropriate:
17 //! - `#[rustc_dirty(label="TypeckItemBody", cfg="rev2")]` if we are
18 //! in `#[cfg(rev2)]`, then there MUST NOT be a node
19 //! `DepNode::TypeckItemBody(X)` where `X` is the def-id of the
21 //! - `#[rustc_clean(label="TypeckItemBody", cfg="rev2")]` same as above,
22 //! except that the node MUST exist.
24 //! Errors are reported if we are in the suitable configuration but
25 //! the required condition is not met.
27 use super::directory::RetracedDefIdDirectory;
28 use super::load::DirtyNodes;
29 use rustc::dep_graph::{DepGraphQuery, DepNode};
31 use rustc::hir::def_id::DefId;
32 use rustc::hir::intravisit::Visitor;
33 use rustc_data_structures::fnv::FnvHashSet;
34 use syntax::ast::{self, Attribute, MetaItem};
35 use syntax::attr::AttrMetaMethods;
36 use syntax::parse::token::InternedString;
37 use rustc::ty::TyCtxt;
39 const DIRTY: &'static str = "rustc_dirty";
40 const CLEAN: &'static str = "rustc_clean";
41 const LABEL: &'static str = "label";
42 const CFG: &'static str = "cfg";
44 pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
45 dirty_inputs: &DirtyNodes,
46 retraced: &RetracedDefIdDirectory) {
47 // can't add `#[rustc_dirty]` etc without opting in to this feature
48 if !tcx.sess.features.borrow().rustc_attrs {
52 let _ignore = tcx.dep_graph.in_ignore();
53 let dirty_inputs: FnvHashSet<DepNode<DefId>> =
55 .filter_map(|d| retraced.map(d))
57 let query = tcx.dep_graph.query();
58 debug!("query-nodes: {:?}", query.nodes());
59 let krate = tcx.map.krate();
60 krate.visit_all_items(&mut DirtyCleanVisitor {
63 dirty_inputs: dirty_inputs,
67 pub struct DirtyCleanVisitor<'a, 'tcx:'a> {
68 tcx: TyCtxt<'a, 'tcx, 'tcx>,
69 query: &'a DepGraphQuery<DefId>,
70 dirty_inputs: FnvHashSet<DepNode<DefId>>,
73 impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
74 fn expect_associated_value(&self, item: &MetaItem) -> InternedString {
75 if let Some(value) = item.value_str() {
78 self.tcx.sess.span_fatal(
80 &format!("associated value expected for `{}`", item.name()));
84 /// Given a `#[rustc_dirty]` or `#[rustc_clean]` attribute, scan
85 /// for a `cfg="foo"` attribute and check whether we have a cfg
86 /// flag called `foo`.
87 fn check_config(&self, attr: &ast::Attribute) -> bool {
88 debug!("check_config(attr={:?})", attr);
89 let config = &self.tcx.map.krate().config;
90 debug!("check_config: config={:?}", config);
91 for item in attr.meta_item_list().unwrap_or(&[]) {
92 if item.check_name(CFG) {
93 let value = self.expect_associated_value(item);
94 debug!("check_config: searching for cfg {:?}", value);
95 for cfg in &config[..] {
96 if cfg.check_name(&value[..]) {
97 debug!("check_config: matched {:?}", cfg);
105 self.tcx.sess.span_fatal(
107 &format!("no cfg attribute"));
110 fn dep_node(&self, attr: &Attribute, def_id: DefId) -> DepNode<DefId> {
111 for item in attr.meta_item_list().unwrap_or(&[]) {
112 if item.check_name(LABEL) {
113 let value = self.expect_associated_value(item);
114 match DepNode::from_label_string(&value[..], def_id) {
115 Ok(def_id) => return def_id,
117 self.tcx.sess.span_fatal(
119 &format!("dep-node label `{}` not recognized", value));
125 self.tcx.sess.span_fatal(attr.span, "no `label` found");
128 fn dep_node_str(&self, dep_node: &DepNode<DefId>) -> DepNode<String> {
129 dep_node.map_def(|&def_id| Some(self.tcx.item_path_str(def_id))).unwrap()
132 fn assert_dirty(&self, item: &hir::Item, dep_node: DepNode<DefId>) {
133 debug!("assert_dirty({:?})", dep_node);
137 // HIR nodes are inputs, so if we are asserting that the HIR node is
138 // dirty, we check the dirty input set.
139 if !self.dirty_inputs.contains(&dep_node) {
140 let dep_node_str = self.dep_node_str(&dep_node);
141 self.tcx.sess.span_err(
143 &format!("`{:?}` not found in dirty set, but should be dirty",
148 // Other kinds of nodes would be targets, so check if
149 // the dep-graph contains the node.
150 if self.query.contains_node(&dep_node) {
151 let dep_node_str = self.dep_node_str(&dep_node);
152 self.tcx.sess.span_err(
154 &format!("`{:?}` found in dep graph, but should be dirty", dep_node_str));
160 fn assert_clean(&self, item: &hir::Item, dep_node: DepNode<DefId>) {
161 debug!("assert_clean({:?})", dep_node);
165 // For HIR nodes, check the inputs.
166 if self.dirty_inputs.contains(&dep_node) {
167 let dep_node_str = self.dep_node_str(&dep_node);
168 self.tcx.sess.span_err(
170 &format!("`{:?}` found in dirty-node set, but should be clean",
175 // Otherwise, check if the dep-node exists.
176 if !self.query.contains_node(&dep_node) {
177 let dep_node_str = self.dep_node_str(&dep_node);
178 self.tcx.sess.span_err(
180 &format!("`{:?}` not found in dep graph, but should be clean",
188 impl<'a, 'tcx> Visitor<'tcx> for DirtyCleanVisitor<'a, 'tcx> {
189 fn visit_item(&mut self, item: &'tcx hir::Item) {
190 let def_id = self.tcx.map.local_def_id(item.id);
191 for attr in self.tcx.get_attrs(def_id).iter() {
192 if attr.check_name(DIRTY) {
193 if self.check_config(attr) {
194 self.assert_dirty(item, self.dep_node(attr, def_id));
196 } else if attr.check_name(CLEAN) {
197 if self.check_config(attr) {
198 self.assert_clean(item, self.dep_node(attr, def_id));