|
| 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 rustc::dep_graph::{DepGraphQuery, DepNode}; |
| 28 | +use rustc::middle::def_id::DefId; |
| 29 | +use rustc_front::hir; |
| 30 | +use rustc_front::intravisit::Visitor; |
| 31 | +use syntax::ast::{self, Attribute, MetaItem}; |
| 32 | +use syntax::attr::AttrMetaMethods; |
| 33 | +use syntax::parse::token::InternedString; |
| 34 | +use rustc::ty; |
| 35 | + |
| 36 | +const DIRTY: &'static str = "rustc_dirty"; |
| 37 | +const CLEAN: &'static str = "rustc_clean"; |
| 38 | +const LABEL: &'static str = "label"; |
| 39 | +const CFG: &'static str = "cfg"; |
| 40 | + |
| 41 | +pub fn check_dirty_clean_annotations(tcx: &ty::TyCtxt) { |
| 42 | + let _ignore = tcx.dep_graph.in_ignore(); |
| 43 | + let query = tcx.dep_graph.query(); |
| 44 | + let krate = tcx.map.krate(); |
| 45 | + krate.visit_all_items(&mut DirtyCleanVisitor { |
| 46 | + tcx: tcx, |
| 47 | + query: &query, |
| 48 | + }); |
| 49 | +} |
| 50 | + |
| 51 | +pub struct DirtyCleanVisitor<'a, 'tcx:'a> { |
| 52 | + tcx: &'a ty::TyCtxt<'tcx>, |
| 53 | + query: &'a DepGraphQuery<DefId>, |
| 54 | +} |
| 55 | + |
| 56 | +impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> { |
| 57 | + fn expect_associated_value(&self, item: &MetaItem) -> InternedString { |
| 58 | + if let Some(value) = item.value_str() { |
| 59 | + value |
| 60 | + } else { |
| 61 | + self.tcx.sess.span_fatal( |
| 62 | + item.span, |
| 63 | + &format!("associated value expected for `{}`", item.name())); |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + /// Given a `#[rustc_dirty]` or `#[rustc_clean]` attribute, scan |
| 68 | + /// for a `cfg="foo"` attribute and check whether we have a cfg |
| 69 | + /// flag called `foo`. |
| 70 | + fn check_config(&self, attr: &ast::Attribute) -> bool { |
| 71 | + debug!("check_config(attr={:?})", attr); |
| 72 | + let config = &self.tcx.map.krate().config; |
| 73 | + debug!("check_config: config={:?}", config); |
| 74 | + for item in attr.meta_item_list().unwrap_or(&[]) { |
| 75 | + if item.check_name(CFG) { |
| 76 | + let value = self.expect_associated_value(item); |
| 77 | + debug!("check_config: searching for cfg {:?}", value); |
| 78 | + for cfg in &config[..] { |
| 79 | + if cfg.check_name(&value[..]) { |
| 80 | + debug!("check_config: matched {:?}", cfg); |
| 81 | + return true; |
| 82 | + } |
| 83 | + } |
| 84 | + } |
| 85 | + } |
| 86 | + debug!("check_config: no match found"); |
| 87 | + return false; |
| 88 | + } |
| 89 | + |
| 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 = self.expect_associated_value(item); |
| 94 | + match DepNode::from_label_string(&value[..], 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 | + if self.query.contains_node(&dep_node) { |
| 116 | + let dep_node_str = self.dep_node_str(dep_node); |
| 117 | + self.tcx.sess.span_err( |
| 118 | + item.span, |
| 119 | + &format!("`{:?}` found in dep graph, but should be dirty", dep_node_str)); |
| 120 | + } |
| 121 | + } |
| 122 | + |
| 123 | + fn assert_clean(&self, item: &hir::Item, dep_node: DepNode<DefId>) { |
| 124 | + debug!("assert_clean({:?})", dep_node); |
| 125 | + |
| 126 | + if !self.query.contains_node(&dep_node) { |
| 127 | + let dep_node_str = self.dep_node_str(dep_node); |
| 128 | + self.tcx.sess.span_err( |
| 129 | + item.span, |
| 130 | + &format!("`{:?}` not found in dep graph, but should be clean", dep_node_str)); |
| 131 | + } |
| 132 | + } |
| 133 | +} |
| 134 | + |
| 135 | +impl<'a, 'tcx> Visitor<'tcx> for DirtyCleanVisitor<'a, 'tcx> { |
| 136 | + fn visit_item(&mut self, item: &'tcx hir::Item) { |
| 137 | + let def_id = self.tcx.map.local_def_id(item.id); |
| 138 | + for attr in self.tcx.get_attrs(def_id).iter() { |
| 139 | + if attr.check_name(DIRTY) { |
| 140 | + if self.check_config(attr) { |
| 141 | + self.assert_dirty(item, self.dep_node(attr, def_id)); |
| 142 | + } |
| 143 | + } else if attr.check_name(CLEAN) { |
| 144 | + if self.check_config(attr) { |
| 145 | + self.assert_clean(item, self.dep_node(attr, def_id)); |
| 146 | + } |
| 147 | + } |
| 148 | + } |
| 149 | + } |
| 150 | +} |
| 151 | + |
0 commit comments