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
));