@@ -46,6 +46,7 @@ use rustc::dep_graph::{DepGraphQuery, DepNode};
4646use rustc:: hir;
4747use rustc:: hir:: def_id:: DefId ;
4848use rustc:: hir:: itemlikevisit:: ItemLikeVisitor ;
49+ use rustc:: hir:: intravisit;
4950use syntax:: ast:: { self , Attribute , NestedMetaItem } ;
5051use rustc_data_structures:: fx:: { FxHashSet , FxHashMap } ;
5152use 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
8398pub 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
89105impl < ' 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
212250pub 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
218257impl < ' 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