]> git.lizzy.rs Git - rust.git/blob - src/librustc_incremental/persist/dirty_clean.rs
Changed issue number to 36105
[rust.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