]> git.proxmox.com Git - rustc.git/blob - src/librustc_incremental/persist/dirty_clean.rs
New upstream version 1.12.0+dfsg1
[rustc.git] / src / librustc_incremental / persist / dirty_clean.rs
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.
4 //
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.
10
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:
16 //!
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
20 //! current node.
21 //! - `#[rustc_clean(label="TypeckItemBody", cfg="rev2")]` same as above,
22 //! except that the node MUST exist.
23 //!
24 //! Errors are reported if we are in the suitable configuration but
25 //! the required condition is not met.
26
27 use super::directory::RetracedDefIdDirectory;
28 use super::load::DirtyNodes;
29 use rustc::dep_graph::{DepGraphQuery, DepNode};
30 use rustc::hir;
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;
38
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";
43
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 {
49 return;
50 }
51
52 let _ignore = tcx.dep_graph.in_ignore();
53 let dirty_inputs: FnvHashSet<DepNode<DefId>> =
54 dirty_inputs.iter()
55 .filter_map(|d| retraced.map(d))
56 .collect();
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 {
61 tcx: tcx,
62 query: &query,
63 dirty_inputs: dirty_inputs,
64 });
65 }
66
67 pub struct DirtyCleanVisitor<'a, 'tcx:'a> {
68 tcx: TyCtxt<'a, 'tcx, 'tcx>,
69 query: &'a DepGraphQuery<DefId>,
70 dirty_inputs: FnvHashSet<DepNode<DefId>>,
71 }
72
73 impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
74 fn expect_associated_value(&self, item: &MetaItem) -> InternedString {
75 if let Some(value) = item.value_str() {
76 value
77 } else {
78 self.tcx.sess.span_fatal(
79 item.span,
80 &format!("associated value expected for `{}`", item.name()));
81 }
82 }
83
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);
98 return true;
99 }
100 }
101 return false;
102 }
103 }
104
105 self.tcx.sess.span_fatal(
106 attr.span,
107 &format!("no cfg attribute"));
108 }
109
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,
116 Err(()) => {
117 self.tcx.sess.span_fatal(
118 item.span,
119 &format!("dep-node label `{}` not recognized", value));
120 }
121 }
122 }
123 }
124
125 self.tcx.sess.span_fatal(attr.span, "no `label` found");
126 }
127
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()
130 }
131
132 fn assert_dirty(&self, item: &hir::Item, dep_node: DepNode<DefId>) {
133 debug!("assert_dirty({:?})", dep_node);
134
135 match dep_node {
136 DepNode::Hir(_) => {
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(
142 item.span,
143 &format!("`{:?}` not found in dirty set, but should be dirty",
144 dep_node_str));
145 }
146 }
147 _ => {
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(
153 item.span,
154 &format!("`{:?}` found in dep graph, but should be dirty", dep_node_str));
155 }
156 }
157 }
158 }
159
160 fn assert_clean(&self, item: &hir::Item, dep_node: DepNode<DefId>) {
161 debug!("assert_clean({:?})", dep_node);
162
163 match dep_node {
164 DepNode::Hir(_) => {
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(
169 item.span,
170 &format!("`{:?}` found in dirty-node set, but should be clean",
171 dep_node_str));
172 }
173 }
174 _ => {
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(
179 item.span,
180 &format!("`{:?}` not found in dep graph, but should be clean",
181 dep_node_str));
182 }
183 }
184 }
185 }
186 }
187
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));
195 }
196 } else if attr.check_name(CLEAN) {
197 if self.check_config(attr) {
198 self.assert_clean(item, self.dep_node(attr, def_id));
199 }
200 }
201 }
202 }
203 }
204