]> git.lizzy.rs Git - rust.git/blob - src/librustc_incremental/persist/dirty_clean.rs
Unignore u128 test for stage 0,1
[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 and just after it has been saved.
13 //! For each node marked with `#[rustc_clean]` or `#[rustc_dirty]`,
14 //! we will check that a suitable node for that item either appears
15 //! or does not appear in the dep-graph, as appropriate:
16 //!
17 //! - `#[rustc_dirty(label="TypeckTables", cfg="rev2")]` if we are
18 //!   in `#[cfg(rev2)]`, then there MUST NOT be a node
19 //!   `DepNode::TypeckTables(X)` where `X` is the def-id of the
20 //!   current node.
21 //! - `#[rustc_clean(label="TypeckTables", 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 //! The `#[rustc_metadata_dirty]` and `#[rustc_metadata_clean]` attributes
28 //! can be used to check the incremental compilation hash (ICH) values of
29 //! metadata exported in rlibs.
30 //!
31 //! - If a node is marked with `#[rustc_metadata_clean(cfg="rev2")]` we
32 //!   check that the metadata hash for that node is the same for "rev2"
33 //!   it was for "rev1".
34 //! - If a node is marked with `#[rustc_metadata_dirty(cfg="rev2")]` we
35 //!   check that the metadata hash for that node is *different* for "rev2"
36 //!   than it was for "rev1".
37 //!
38 //! Note that the metadata-testing attributes must never specify the
39 //! first revision. This would lead to a crash since there is no
40 //! previous revision to compare things to.
41 //!
42
43 use super::directory::RetracedDefIdDirectory;
44 use super::load::DirtyNodes;
45 use rustc::dep_graph::{DepGraphQuery, DepNode};
46 use rustc::hir;
47 use rustc::hir::def_id::DefId;
48 use rustc::hir::itemlikevisit::ItemLikeVisitor;
49 use syntax::ast::{self, Attribute, NestedMetaItem};
50 use rustc_data_structures::fx::{FxHashSet, FxHashMap};
51 use syntax_pos::Span;
52 use rustc::ty::TyCtxt;
53 use ich::Fingerprint;
54
55 use {ATTR_DIRTY, ATTR_CLEAN, ATTR_DIRTY_METADATA, ATTR_CLEAN_METADATA};
56
57 const LABEL: &'static str = "label";
58 const CFG: &'static str = "cfg";
59
60 pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
61                                                dirty_inputs: &DirtyNodes,
62                                                retraced: &RetracedDefIdDirectory) {
63     // can't add `#[rustc_dirty]` etc without opting in to this feature
64     if !tcx.sess.features.borrow().rustc_attrs {
65         return;
66     }
67
68     let _ignore = tcx.dep_graph.in_ignore();
69     let dirty_inputs: FxHashSet<DepNode<DefId>> =
70         dirty_inputs.keys()
71                     .filter_map(|d| retraced.map(d))
72                     .collect();
73     let query = tcx.dep_graph.query();
74     debug!("query-nodes: {:?}", query.nodes());
75     let krate = tcx.hir.krate();
76     krate.visit_all_item_likes(&mut DirtyCleanVisitor {
77         tcx: tcx,
78         query: &query,
79         dirty_inputs: dirty_inputs,
80     });
81 }
82
83 pub struct DirtyCleanVisitor<'a, 'tcx:'a> {
84     tcx: TyCtxt<'a, 'tcx, 'tcx>,
85     query: &'a DepGraphQuery<DefId>,
86     dirty_inputs: FxHashSet<DepNode<DefId>>,
87 }
88
89 impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
90     fn dep_node(&self, attr: &Attribute, def_id: DefId) -> DepNode<DefId> {
91         for item in attr.meta_item_list().unwrap_or(&[]) {
92             if item.check_name(LABEL) {
93                 let value = expect_associated_value(self.tcx, item);
94                 match DepNode::from_label_string(&value.as_str(), def_id) {
95                     Ok(def_id) => return def_id,
96                     Err(()) => {
97                         self.tcx.sess.span_fatal(
98                             item.span,
99                             &format!("dep-node label `{}` not recognized", value));
100                     }
101                 }
102             }
103         }
104
105         self.tcx.sess.span_fatal(attr.span, "no `label` found");
106     }
107
108     fn dep_node_str(&self, dep_node: &DepNode<DefId>) -> DepNode<String> {
109         dep_node.map_def(|&def_id| Some(self.tcx.item_path_str(def_id))).unwrap()
110     }
111
112     fn assert_dirty(&self, item: &hir::Item, dep_node: DepNode<DefId>) {
113         debug!("assert_dirty({:?})", dep_node);
114
115         match dep_node {
116             DepNode::Krate |
117             DepNode::Hir(_) |
118             DepNode::HirBody(_) => {
119                 // HIR nodes are inputs, so if we are asserting that the HIR node is
120                 // dirty, we check the dirty input set.
121                 if !self.dirty_inputs.contains(&dep_node) {
122                     let dep_node_str = self.dep_node_str(&dep_node);
123                     self.tcx.sess.span_err(
124                         item.span,
125                         &format!("`{:?}` not found in dirty set, but should be dirty",
126                                  dep_node_str));
127                 }
128             }
129             _ => {
130                 // Other kinds of nodes would be targets, so check if
131                 // the dep-graph contains the node.
132                 if self.query.contains_node(&dep_node) {
133                     let dep_node_str = self.dep_node_str(&dep_node);
134                     self.tcx.sess.span_err(
135                         item.span,
136                         &format!("`{:?}` found in dep graph, but should be dirty", dep_node_str));
137                 }
138             }
139         }
140     }
141
142     fn assert_clean(&self, item: &hir::Item, dep_node: DepNode<DefId>) {
143         debug!("assert_clean({:?})", dep_node);
144
145         match dep_node {
146             DepNode::Krate |
147             DepNode::Hir(_) |
148             DepNode::HirBody(_) => {
149                 // For HIR nodes, check the inputs.
150                 if self.dirty_inputs.contains(&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 dirty-node set, but should be clean",
155                                  dep_node_str));
156                 }
157             }
158             _ => {
159                 // Otherwise, check if the dep-node exists.
160                 if !self.query.contains_node(&dep_node) {
161                     let dep_node_str = self.dep_node_str(&dep_node);
162                     self.tcx.sess.span_err(
163                         item.span,
164                         &format!("`{:?}` not found in dep graph, but should be clean",
165                                  dep_node_str));
166                 }
167             }
168         }
169     }
170 }
171
172 impl<'a, 'tcx> ItemLikeVisitor<'tcx> for DirtyCleanVisitor<'a, 'tcx> {
173     fn visit_item(&mut self, item: &'tcx hir::Item) {
174         let def_id = self.tcx.hir.local_def_id(item.id);
175         for attr in self.tcx.get_attrs(def_id).iter() {
176             if attr.check_name(ATTR_DIRTY) {
177                 if check_config(self.tcx, attr) {
178                     self.assert_dirty(item, self.dep_node(attr, def_id));
179                 }
180             } else if attr.check_name(ATTR_CLEAN) {
181                 if check_config(self.tcx, attr) {
182                     self.assert_clean(item, self.dep_node(attr, def_id));
183                 }
184             }
185         }
186     }
187
188     fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) {
189     }
190
191     fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) {
192     }
193 }
194
195 pub fn check_dirty_clean_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
196                                   prev_metadata_hashes: &FxHashMap<DefId, Fingerprint>,
197                                   current_metadata_hashes: &FxHashMap<DefId, Fingerprint>) {
198     if !tcx.sess.opts.debugging_opts.query_dep_graph {
199         return;
200     }
201
202     tcx.dep_graph.with_ignore(||{
203         let krate = tcx.hir.krate();
204         krate.visit_all_item_likes(&mut DirtyCleanMetadataVisitor {
205             tcx: tcx,
206             prev_metadata_hashes: prev_metadata_hashes,
207             current_metadata_hashes: current_metadata_hashes,
208         });
209     });
210 }
211
212 pub struct DirtyCleanMetadataVisitor<'a, 'tcx:'a, 'm> {
213     tcx: TyCtxt<'a, 'tcx, 'tcx>,
214     prev_metadata_hashes: &'m FxHashMap<DefId, Fingerprint>,
215     current_metadata_hashes: &'m FxHashMap<DefId, Fingerprint>,
216 }
217
218 impl<'a, 'tcx, 'm> ItemLikeVisitor<'tcx> for DirtyCleanMetadataVisitor<'a, 'tcx, 'm> {
219     fn visit_item(&mut self, item: &'tcx hir::Item) {
220         let def_id = self.tcx.hir.local_def_id(item.id);
221
222         for attr in self.tcx.get_attrs(def_id).iter() {
223             if attr.check_name(ATTR_DIRTY_METADATA) {
224                 if check_config(self.tcx, attr) {
225                     self.assert_state(false, def_id, item.span);
226                 }
227             } else if attr.check_name(ATTR_CLEAN_METADATA) {
228                 if check_config(self.tcx, attr) {
229                     self.assert_state(true, def_id, item.span);
230                 }
231             }
232         }
233     }
234
235     fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) {
236     }
237
238     fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) {
239     }
240 }
241
242 impl<'a, 'tcx, 'm> DirtyCleanMetadataVisitor<'a, 'tcx, 'm> {
243
244     fn assert_state(&self, should_be_clean: bool, def_id: DefId, span: Span) {
245         let item_path = self.tcx.item_path_str(def_id);
246         debug!("assert_state({})", item_path);
247
248         if let Some(&prev_hash) = self.prev_metadata_hashes.get(&def_id) {
249             let hashes_are_equal = prev_hash == self.current_metadata_hashes[&def_id];
250
251             if should_be_clean && !hashes_are_equal {
252                 self.tcx.sess.span_err(
253                         span,
254                         &format!("Metadata hash of `{}` is dirty, but should be clean",
255                                  item_path));
256             }
257
258             let should_be_dirty = !should_be_clean;
259             if should_be_dirty && hashes_are_equal {
260                 self.tcx.sess.span_err(
261                         span,
262                         &format!("Metadata hash of `{}` is clean, but should be dirty",
263                                  item_path));
264             }
265         } else {
266             self.tcx.sess.span_err(
267                         span,
268                         &format!("Could not find previous metadata hash of `{}`",
269                                  item_path));
270         }
271     }
272 }
273
274 /// Given a `#[rustc_dirty]` or `#[rustc_clean]` attribute, scan
275 /// for a `cfg="foo"` attribute and check whether we have a cfg
276 /// flag called `foo`.
277 fn check_config(tcx: TyCtxt, attr: &ast::Attribute) -> bool {
278     debug!("check_config(attr={:?})", attr);
279     let config = &tcx.sess.parse_sess.config;
280     debug!("check_config: config={:?}", config);
281     for item in attr.meta_item_list().unwrap_or(&[]) {
282         if item.check_name(CFG) {
283             let value = expect_associated_value(tcx, item);
284             debug!("check_config: searching for cfg {:?}", value);
285             return config.contains(&(value, None));
286         }
287     }
288
289     tcx.sess.span_fatal(
290         attr.span,
291         &format!("no cfg attribute"));
292 }
293
294 fn expect_associated_value(tcx: TyCtxt, item: &NestedMetaItem) -> ast::Name {
295     if let Some(value) = item.value_str() {
296         value
297     } else {
298         let msg = if let Some(name) = item.name() {
299             format!("associated value expected for `{}`", name)
300         } else {
301             "expected an associated value".to_string()
302         };
303
304         tcx.sess.span_fatal(item.span, &msg);
305     }
306 }