Skip to content

Commit 324b175

Browse files
committed
Auto merge of #39500 - michaelwoerister:fix-ich-testing, r=nikomatsakis
Let the dep-tracking test framework check that all #[rustc_dirty] attrs have been actually checked r? @nikomatsakis
2 parents 4711ac3 + ab3c826 commit 324b175

File tree

7 files changed

+306
-58
lines changed

7 files changed

+306
-58
lines changed

src/librustc/hir/intravisit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,7 @@ pub fn walk_decl<'v, V: Visitor<'v>>(visitor: &mut V, declaration: &'v Decl) {
914914

915915
pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
916916
visitor.visit_id(expression.id);
917+
walk_list!(visitor, visit_attribute, expression.attrs.iter());
917918
match expression.node {
918919
ExprBox(ref subexpression) => {
919920
visitor.visit_expr(subexpression)

src/librustc_incremental/persist/dirty_clean.rs

+122-31
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use rustc::dep_graph::{DepGraphQuery, DepNode};
4646
use rustc::hir;
4747
use rustc::hir::def_id::DefId;
4848
use rustc::hir::itemlikevisit::ItemLikeVisitor;
49+
use rustc::hir::intravisit;
4950
use syntax::ast::{self, Attribute, NestedMetaItem};
5051
use rustc_data_structures::fx::{FxHashSet, FxHashMap};
5152
use syntax_pos::Span;
@@ -73,17 +74,32 @@ pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
7374
let query = tcx.dep_graph.query();
7475
debug!("query-nodes: {:?}", query.nodes());
7576
let krate = tcx.hir.krate();
76-
krate.visit_all_item_likes(&mut DirtyCleanVisitor {
77+
let mut dirty_clean_visitor = DirtyCleanVisitor {
7778
tcx: tcx,
7879
query: &query,
7980
dirty_inputs: dirty_inputs,
80-
});
81+
checked_attrs: FxHashSet(),
82+
};
83+
krate.visit_all_item_likes(&mut dirty_clean_visitor);
84+
85+
let mut all_attrs = FindAllAttrs {
86+
tcx: tcx,
87+
attr_names: vec![ATTR_DIRTY, ATTR_CLEAN],
88+
found_attrs: vec![],
89+
};
90+
intravisit::walk_crate(&mut all_attrs, krate);
91+
92+
// Note that we cannot use the existing "unused attribute"-infrastructure
93+
// here, since that is running before trans. This is also the reason why
94+
// all trans-specific attributes are `Whitelisted` in syntax::feature_gate.
95+
all_attrs.report_unchecked_attrs(&dirty_clean_visitor.checked_attrs);
8196
}
8297

8398
pub struct DirtyCleanVisitor<'a, 'tcx:'a> {
8499
tcx: TyCtxt<'a, 'tcx, 'tcx>,
85100
query: &'a DepGraphQuery<DefId>,
86101
dirty_inputs: FxHashSet<DepNode<DefId>>,
102+
checked_attrs: FxHashSet<ast::AttrId>,
87103
}
88104

89105
impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
@@ -109,7 +125,7 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
109125
dep_node.map_def(|&def_id| Some(self.tcx.item_path_str(def_id))).unwrap()
110126
}
111127

112-
fn assert_dirty(&self, item: &hir::Item, dep_node: DepNode<DefId>) {
128+
fn assert_dirty(&self, item_span: Span, dep_node: DepNode<DefId>) {
113129
debug!("assert_dirty({:?})", dep_node);
114130

115131
match dep_node {
@@ -121,7 +137,7 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
121137
if !self.dirty_inputs.contains(&dep_node) {
122138
let dep_node_str = self.dep_node_str(&dep_node);
123139
self.tcx.sess.span_err(
124-
item.span,
140+
item_span,
125141
&format!("`{:?}` not found in dirty set, but should be dirty",
126142
dep_node_str));
127143
}
@@ -132,14 +148,14 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
132148
if self.query.contains_node(&dep_node) {
133149
let dep_node_str = self.dep_node_str(&dep_node);
134150
self.tcx.sess.span_err(
135-
item.span,
151+
item_span,
136152
&format!("`{:?}` found in dep graph, but should be dirty", dep_node_str));
137153
}
138154
}
139155
}
140156
}
141157

142-
fn assert_clean(&self, item: &hir::Item, dep_node: DepNode<DefId>) {
158+
fn assert_clean(&self, item_span: Span, dep_node: DepNode<DefId>) {
143159
debug!("assert_clean({:?})", dep_node);
144160

145161
match dep_node {
@@ -150,7 +166,7 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
150166
if self.dirty_inputs.contains(&dep_node) {
151167
let dep_node_str = self.dep_node_str(&dep_node);
152168
self.tcx.sess.span_err(
153-
item.span,
169+
item_span,
154170
&format!("`{:?}` found in dirty-node set, but should be clean",
155171
dep_node_str));
156172
}
@@ -160,35 +176,43 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
160176
if !self.query.contains_node(&dep_node) {
161177
let dep_node_str = self.dep_node_str(&dep_node);
162178
self.tcx.sess.span_err(
163-
item.span,
179+
item_span,
164180
&format!("`{:?}` not found in dep graph, but should be clean",
165181
dep_node_str));
166182
}
167183
}
168184
}
169185
}
170-
}
171186

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);
187+
fn check_item(&mut self, item_id: ast::NodeId, item_span: Span) {
188+
let def_id = self.tcx.hir.local_def_id(item_id);
175189
for attr in self.tcx.get_attrs(def_id).iter() {
176190
if attr.check_name(ATTR_DIRTY) {
177191
if check_config(self.tcx, attr) {
178-
self.assert_dirty(item, self.dep_node(attr, def_id));
192+
self.checked_attrs.insert(attr.id);
193+
self.assert_dirty(item_span, self.dep_node(attr, def_id));
179194
}
180195
} else if attr.check_name(ATTR_CLEAN) {
181196
if check_config(self.tcx, attr) {
182-
self.assert_clean(item, self.dep_node(attr, def_id));
197+
self.checked_attrs.insert(attr.id);
198+
self.assert_clean(item_span, self.dep_node(attr, def_id));
183199
}
184200
}
185201
}
186202
}
203+
}
204+
205+
impl<'a, 'tcx> ItemLikeVisitor<'tcx> for DirtyCleanVisitor<'a, 'tcx> {
206+
fn visit_item(&mut self, item: &'tcx hir::Item) {
207+
self.check_item(item.id, item.span);
208+
}
187209

188-
fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) {
210+
fn visit_trait_item(&mut self, item: &hir::TraitItem) {
211+
self.check_item(item.id, item.span);
189212
}
190213

191-
fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) {
214+
fn visit_impl_item(&mut self, item: &hir::ImplItem) {
215+
self.check_item(item.id, item.span);
192216
}
193217
}
194218

@@ -201,46 +225,69 @@ pub fn check_dirty_clean_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
201225

202226
tcx.dep_graph.with_ignore(||{
203227
let krate = tcx.hir.krate();
204-
krate.visit_all_item_likes(&mut DirtyCleanMetadataVisitor {
228+
let mut dirty_clean_visitor = DirtyCleanMetadataVisitor {
205229
tcx: tcx,
206230
prev_metadata_hashes: prev_metadata_hashes,
207231
current_metadata_hashes: current_metadata_hashes,
208-
});
232+
checked_attrs: FxHashSet(),
233+
};
234+
krate.visit_all_item_likes(&mut dirty_clean_visitor);
235+
236+
let mut all_attrs = FindAllAttrs {
237+
tcx: tcx,
238+
attr_names: vec![ATTR_DIRTY_METADATA, ATTR_CLEAN_METADATA],
239+
found_attrs: vec![],
240+
};
241+
intravisit::walk_crate(&mut all_attrs, krate);
242+
243+
// Note that we cannot use the existing "unused attribute"-infrastructure
244+
// here, since that is running before trans. This is also the reason why
245+
// all trans-specific attributes are `Whitelisted` in syntax::feature_gate.
246+
all_attrs.report_unchecked_attrs(&dirty_clean_visitor.checked_attrs);
209247
});
210248
}
211249

212250
pub struct DirtyCleanMetadataVisitor<'a, 'tcx:'a, 'm> {
213251
tcx: TyCtxt<'a, 'tcx, 'tcx>,
214252
prev_metadata_hashes: &'m FxHashMap<DefId, Fingerprint>,
215253
current_metadata_hashes: &'m FxHashMap<DefId, Fingerprint>,
254+
checked_attrs: FxHashSet<ast::AttrId>,
216255
}
217256

218257
impl<'a, 'tcx, 'm> ItemLikeVisitor<'tcx> for DirtyCleanMetadataVisitor<'a, 'tcx, 'm> {
219258
fn visit_item(&mut self, item: &'tcx hir::Item) {
220-
let def_id = self.tcx.hir.local_def_id(item.id);
259+
self.check_item(item.id, item.span);
260+
}
261+
262+
fn visit_trait_item(&mut self, item: &hir::TraitItem) {
263+
self.check_item(item.id, item.span);
264+
}
265+
266+
fn visit_impl_item(&mut self, item: &hir::ImplItem) {
267+
self.check_item(item.id, item.span);
268+
}
269+
}
270+
271+
impl<'a, 'tcx, 'm> DirtyCleanMetadataVisitor<'a, 'tcx, 'm> {
272+
273+
fn check_item(&mut self, item_id: ast::NodeId, item_span: Span) {
274+
let def_id = self.tcx.hir.local_def_id(item_id);
221275

222276
for attr in self.tcx.get_attrs(def_id).iter() {
223277
if attr.check_name(ATTR_DIRTY_METADATA) {
224278
if check_config(self.tcx, attr) {
225-
self.assert_state(false, def_id, item.span);
279+
self.checked_attrs.insert(attr.id);
280+
self.assert_state(false, def_id, item_span);
226281
}
227282
} else if attr.check_name(ATTR_CLEAN_METADATA) {
228283
if check_config(self.tcx, attr) {
229-
self.assert_state(true, def_id, item.span);
284+
self.checked_attrs.insert(attr.id);
285+
self.assert_state(true, def_id, item_span);
230286
}
231287
}
232288
}
233289
}
234290

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-
244291
fn assert_state(&self, should_be_clean: bool, def_id: DefId, span: Span) {
245292
let item_path = self.tcx.item_path_str(def_id);
246293
debug!("assert_state({})", item_path);
@@ -274,7 +321,7 @@ impl<'a, 'tcx, 'm> DirtyCleanMetadataVisitor<'a, 'tcx, 'm> {
274321
/// Given a `#[rustc_dirty]` or `#[rustc_clean]` attribute, scan
275322
/// for a `cfg="foo"` attribute and check whether we have a cfg
276323
/// flag called `foo`.
277-
fn check_config(tcx: TyCtxt, attr: &ast::Attribute) -> bool {
324+
fn check_config(tcx: TyCtxt, attr: &Attribute) -> bool {
278325
debug!("check_config(attr={:?})", attr);
279326
let config = &tcx.sess.parse_sess.config;
280327
debug!("check_config: config={:?}", config);
@@ -304,3 +351,47 @@ fn expect_associated_value(tcx: TyCtxt, item: &NestedMetaItem) -> ast::Name {
304351
tcx.sess.span_fatal(item.span, &msg);
305352
}
306353
}
354+
355+
356+
// A visitor that collects all #[rustc_dirty]/#[rustc_clean] attributes from
357+
// the HIR. It is used to verfiy that we really ran checks for all annotated
358+
// nodes.
359+
pub struct FindAllAttrs<'a, 'tcx:'a> {
360+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
361+
attr_names: Vec<&'static str>,
362+
found_attrs: Vec<&'tcx Attribute>,
363+
}
364+
365+
impl<'a, 'tcx> FindAllAttrs<'a, 'tcx> {
366+
367+
fn is_active_attr(&mut self, attr: &Attribute) -> bool {
368+
for attr_name in &self.attr_names {
369+
if attr.check_name(attr_name) && check_config(self.tcx, attr) {
370+
return true;
371+
}
372+
}
373+
374+
false
375+
}
376+
377+
fn report_unchecked_attrs(&self, checked_attrs: &FxHashSet<ast::AttrId>) {
378+
for attr in &self.found_attrs {
379+
if !checked_attrs.contains(&attr.id) {
380+
self.tcx.sess.span_err(attr.span, &format!("found unchecked \
381+
#[rustc_dirty]/#[rustc_clean] attribute"));
382+
}
383+
}
384+
}
385+
}
386+
387+
impl<'a, 'tcx> intravisit::Visitor<'tcx> for FindAllAttrs<'a, 'tcx> {
388+
fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
389+
intravisit::NestedVisitorMap::All(&self.tcx.hir)
390+
}
391+
392+
fn visit_attribute(&mut self, attr: &'tcx Attribute) {
393+
if self.is_active_attr(attr) {
394+
self.found_attrs.push(attr);
395+
}
396+
}
397+
}

0 commit comments

Comments
 (0)