@@ -46,6 +46,7 @@ use rustc::dep_graph::{DepGraphQuery, DepNode};
46
46
use rustc:: hir;
47
47
use rustc:: hir:: def_id:: DefId ;
48
48
use rustc:: hir:: itemlikevisit:: ItemLikeVisitor ;
49
+ use rustc:: hir:: intravisit;
49
50
use syntax:: ast:: { self , Attribute , NestedMetaItem } ;
50
51
use rustc_data_structures:: fx:: { FxHashSet , FxHashMap } ;
51
52
use syntax_pos:: Span ;
@@ -73,17 +74,32 @@ pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
73
74
let query = tcx. dep_graph . query ( ) ;
74
75
debug ! ( "query-nodes: {:?}" , query. nodes( ) ) ;
75
76
let krate = tcx. hir . krate ( ) ;
76
- krate . visit_all_item_likes ( & mut DirtyCleanVisitor {
77
+ let mut dirty_clean_visitor = DirtyCleanVisitor {
77
78
tcx : tcx,
78
79
query : & query,
79
80
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 ) ;
81
96
}
82
97
83
98
pub struct DirtyCleanVisitor < ' a , ' tcx : ' a > {
84
99
tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
85
100
query : & ' a DepGraphQuery < DefId > ,
86
101
dirty_inputs : FxHashSet < DepNode < DefId > > ,
102
+ checked_attrs : FxHashSet < ast:: AttrId > ,
87
103
}
88
104
89
105
impl < ' a , ' tcx > DirtyCleanVisitor < ' a , ' tcx > {
@@ -109,7 +125,7 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
109
125
dep_node. map_def ( |& def_id| Some ( self . tcx . item_path_str ( def_id) ) ) . unwrap ( )
110
126
}
111
127
112
- fn assert_dirty ( & self , item : & hir :: Item , dep_node : DepNode < DefId > ) {
128
+ fn assert_dirty ( & self , item_span : Span , dep_node : DepNode < DefId > ) {
113
129
debug ! ( "assert_dirty({:?})" , dep_node) ;
114
130
115
131
match dep_node {
@@ -121,7 +137,7 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
121
137
if !self . dirty_inputs . contains ( & dep_node) {
122
138
let dep_node_str = self . dep_node_str ( & dep_node) ;
123
139
self . tcx . sess . span_err (
124
- item . span ,
140
+ item_span ,
125
141
& format ! ( "`{:?}` not found in dirty set, but should be dirty" ,
126
142
dep_node_str) ) ;
127
143
}
@@ -132,14 +148,14 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
132
148
if self . query . contains_node ( & dep_node) {
133
149
let dep_node_str = self . dep_node_str ( & dep_node) ;
134
150
self . tcx . sess . span_err (
135
- item . span ,
151
+ item_span ,
136
152
& format ! ( "`{:?}` found in dep graph, but should be dirty" , dep_node_str) ) ;
137
153
}
138
154
}
139
155
}
140
156
}
141
157
142
- fn assert_clean ( & self , item : & hir :: Item , dep_node : DepNode < DefId > ) {
158
+ fn assert_clean ( & self , item_span : Span , dep_node : DepNode < DefId > ) {
143
159
debug ! ( "assert_clean({:?})" , dep_node) ;
144
160
145
161
match dep_node {
@@ -150,7 +166,7 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
150
166
if self . dirty_inputs . contains ( & dep_node) {
151
167
let dep_node_str = self . dep_node_str ( & dep_node) ;
152
168
self . tcx . sess . span_err (
153
- item . span ,
169
+ item_span ,
154
170
& format ! ( "`{:?}` found in dirty-node set, but should be clean" ,
155
171
dep_node_str) ) ;
156
172
}
@@ -160,35 +176,43 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
160
176
if !self . query . contains_node ( & dep_node) {
161
177
let dep_node_str = self . dep_node_str ( & dep_node) ;
162
178
self . tcx . sess . span_err (
163
- item . span ,
179
+ item_span ,
164
180
& format ! ( "`{:?}` not found in dep graph, but should be clean" ,
165
181
dep_node_str) ) ;
166
182
}
167
183
}
168
184
}
169
185
}
170
- }
171
186
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) ;
175
189
for attr in self . tcx . get_attrs ( def_id) . iter ( ) {
176
190
if attr. check_name ( ATTR_DIRTY ) {
177
191
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) ) ;
179
194
}
180
195
} else if attr. check_name ( ATTR_CLEAN ) {
181
196
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) ) ;
183
199
}
184
200
}
185
201
}
186
202
}
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
+ }
187
209
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 ) ;
189
212
}
190
213
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 ) ;
192
216
}
193
217
}
194
218
@@ -201,46 +225,69 @@ pub fn check_dirty_clean_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
201
225
202
226
tcx. dep_graph . with_ignore ( ||{
203
227
let krate = tcx. hir . krate ( ) ;
204
- krate . visit_all_item_likes ( & mut DirtyCleanMetadataVisitor {
228
+ let mut dirty_clean_visitor = DirtyCleanMetadataVisitor {
205
229
tcx : tcx,
206
230
prev_metadata_hashes : prev_metadata_hashes,
207
231
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 ) ;
209
247
} ) ;
210
248
}
211
249
212
250
pub struct DirtyCleanMetadataVisitor < ' a , ' tcx : ' a , ' m > {
213
251
tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
214
252
prev_metadata_hashes : & ' m FxHashMap < DefId , Fingerprint > ,
215
253
current_metadata_hashes : & ' m FxHashMap < DefId , Fingerprint > ,
254
+ checked_attrs : FxHashSet < ast:: AttrId > ,
216
255
}
217
256
218
257
impl < ' a , ' tcx , ' m > ItemLikeVisitor < ' tcx > for DirtyCleanMetadataVisitor < ' a , ' tcx , ' m > {
219
258
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) ;
221
275
222
276
for attr in self . tcx . get_attrs ( def_id) . iter ( ) {
223
277
if attr. check_name ( ATTR_DIRTY_METADATA ) {
224
278
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) ;
226
281
}
227
282
} else if attr. check_name ( ATTR_CLEAN_METADATA ) {
228
283
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) ;
230
286
}
231
287
}
232
288
}
233
289
}
234
290
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
291
fn assert_state ( & self , should_be_clean : bool , def_id : DefId , span : Span ) {
245
292
let item_path = self . tcx . item_path_str ( def_id) ;
246
293
debug ! ( "assert_state({})" , item_path) ;
@@ -274,7 +321,7 @@ impl<'a, 'tcx, 'm> DirtyCleanMetadataVisitor<'a, 'tcx, 'm> {
274
321
/// Given a `#[rustc_dirty]` or `#[rustc_clean]` attribute, scan
275
322
/// for a `cfg="foo"` attribute and check whether we have a cfg
276
323
/// flag called `foo`.
277
- fn check_config ( tcx : TyCtxt , attr : & ast :: Attribute ) -> bool {
324
+ fn check_config ( tcx : TyCtxt , attr : & Attribute ) -> bool {
278
325
debug ! ( "check_config(attr={:?})" , attr) ;
279
326
let config = & tcx. sess . parse_sess . config ;
280
327
debug ! ( "check_config: config={:?}" , config) ;
@@ -304,3 +351,47 @@ fn expect_associated_value(tcx: TyCtxt, item: &NestedMetaItem) -> ast::Name {
304
351
tcx. sess . span_fatal ( item. span , & msg) ;
305
352
}
306
353
}
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