@@ -34,6 +34,7 @@ pub struct UnsafetyChecker<'a, 'tcx: 'a> {
3434 tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
3535 param_env : ty:: ParamEnv < ' tcx > ,
3636 used_unsafe : FxHashSet < ast:: NodeId > ,
37+ inherited_blocks : Vec < ( ast:: NodeId , bool ) > ,
3738}
3839
3940impl < ' a , ' gcx , ' tcx > UnsafetyChecker < ' a , ' tcx > {
@@ -52,6 +53,7 @@ impl<'a, 'gcx, 'tcx> UnsafetyChecker<'a, 'tcx> {
5253 tcx,
5354 param_env,
5455 used_unsafe : FxHashSet ( ) ,
56+ inherited_blocks : vec ! [ ] ,
5557 }
5658 }
5759}
@@ -75,7 +77,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
7577 TerminatorKind :: Return |
7678 TerminatorKind :: Unreachable |
7779 TerminatorKind :: FalseEdges { .. } => {
78- // safe (at least as emitted during MIR construction)
80+ // safe (at least as emitted during MIR construction)
7981 }
8082
8183 TerminatorKind :: Call { ref func, .. } => {
@@ -117,12 +119,20 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
117119 rvalue : & Rvalue < ' tcx > ,
118120 location : Location )
119121 {
120- if let & Rvalue :: Aggregate (
121- box AggregateKind :: Closure ( def_id, _) ,
122- _
123- ) = rvalue {
124- let unsafety_violations = self . tcx . unsafety_violations ( def_id) ;
125- self . register_violations ( & unsafety_violations) ;
122+ if let & Rvalue :: Aggregate ( box ref aggregate, _) = rvalue {
123+ match aggregate {
124+ & AggregateKind :: Array ( ..) |
125+ & AggregateKind :: Tuple |
126+ & AggregateKind :: Adt ( ..) => { }
127+ & AggregateKind :: Closure ( def_id, _) |
128+ & AggregateKind :: Generator ( def_id, _, _) => {
129+ let UnsafetyCheckResult {
130+ violations, unsafe_blocks
131+ } = self . tcx . unsafety_check_result ( def_id) ;
132+ self . inherited_blocks . extend ( unsafe_blocks. iter ( ) . cloned ( ) ) ;
133+ self . register_violations ( & violations, & unsafe_blocks) ;
134+ }
135+ }
126136 }
127137 self . super_rvalue ( rvalue, location) ;
128138 }
@@ -189,7 +199,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
189199 source_info,
190200 description : "use of extern static" ,
191201 lint_node_id : Some ( lint_root)
192- } ] ) ;
202+ } ] , & [ ] ) ;
193203 }
194204 }
195205 }
@@ -222,41 +232,49 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
222232 let source_info = self . source_info ;
223233 self . register_violations ( & [ UnsafetyViolation {
224234 source_info, description, lint_node_id : None
225- } ] ) ;
235+ } ] , & [ ] ) ;
226236 }
227237
228- fn register_violations ( & mut self , violations : & [ UnsafetyViolation ] ) {
229- match self . visibility_scope_info [ self . source_info . scope ] . safety {
238+ fn register_violations ( & mut self ,
239+ violations : & [ UnsafetyViolation ] ,
240+ unsafe_blocks : & [ ( ast:: NodeId , bool ) ] ) {
241+ let within_unsafe = match self . visibility_scope_info [ self . source_info . scope ] . safety {
230242 Safety :: Safe => {
231243 for violation in violations {
232244 if !self . violations . contains ( violation) {
233245 self . violations . push ( violation. clone ( ) )
234246 }
235247 }
248+
249+ false
236250 }
237- Safety :: BuiltinUnsafe | Safety :: FnUnsafe => { }
251+ Safety :: BuiltinUnsafe | Safety :: FnUnsafe => true ,
238252 Safety :: ExplicitUnsafe ( node_id) => {
239253 if !violations. is_empty ( ) {
240254 self . used_unsafe . insert ( node_id) ;
241255 }
256+ true
242257 }
243- }
258+ } ;
259+ self . inherited_blocks . extend ( unsafe_blocks. iter ( ) . map ( |& ( node_id, is_used) | {
260+ ( node_id, is_used && !within_unsafe)
261+ } ) ) ;
244262 }
245263}
246264
247265pub ( crate ) fn provide ( providers : & mut Providers ) {
248266 * providers = Providers {
249- unsafety_violations ,
267+ unsafety_check_result ,
250268 ..* providers
251269 } ;
252270}
253271
254- struct UnusedUnsafeVisitor < ' a , ' tcx : ' a > {
255- tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
256- used_unsafe : FxHashSet < ast:: NodeId >
272+ struct UnusedUnsafeVisitor < ' a > {
273+ used_unsafe : & ' a FxHashSet < ast :: NodeId > ,
274+ unsafe_blocks : & ' a mut Vec < ( ast:: NodeId , bool ) > ,
257275}
258276
259- impl < ' a , ' tcx > hir:: intravisit:: Visitor < ' tcx > for UnusedUnsafeVisitor < ' a , ' tcx > {
277+ impl < ' a , ' tcx > hir:: intravisit:: Visitor < ' tcx > for UnusedUnsafeVisitor < ' a > {
260278 fn nested_visit_map < ' this > ( & ' this mut self ) ->
261279 hir:: intravisit:: NestedVisitorMap < ' this , ' tcx >
262280 {
@@ -267,50 +285,15 @@ impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a, 'tcx>
267285 hir:: intravisit:: walk_block ( self , block) ;
268286
269287 if let hir:: UnsafeBlock ( hir:: UserProvided ) = block. rules {
270- if !self . used_unsafe . contains ( & block. id ) {
271- self . report_unused_unsafe ( block) ;
272- }
273- }
274- }
275- }
276-
277- impl < ' a , ' tcx > UnusedUnsafeVisitor < ' a , ' tcx > {
278- /// Return the NodeId for an enclosing scope that is also `unsafe`
279- fn is_enclosed ( & self , id : ast:: NodeId ) -> Option < ( String , ast:: NodeId ) > {
280- let parent_id = self . tcx . hir . get_parent_node ( id) ;
281- if parent_id != id {
282- if self . used_unsafe . contains ( & parent_id) {
283- Some ( ( "block" . to_string ( ) , parent_id) )
284- } else if let Some ( hir:: map:: NodeItem ( & hir:: Item {
285- node : hir:: ItemFn ( _, hir:: Unsafety :: Unsafe , _, _, _, _) ,
286- ..
287- } ) ) = self . tcx . hir . find ( parent_id) {
288- Some ( ( "fn" . to_string ( ) , parent_id) )
289- } else {
290- self . is_enclosed ( parent_id)
291- }
292- } else {
293- None
294- }
295- }
296-
297- fn report_unused_unsafe ( & self , block : & ' tcx hir:: Block ) {
298- let mut db = self . tcx . struct_span_lint_node ( UNUSED_UNSAFE ,
299- block. id ,
300- block. span ,
301- "unnecessary `unsafe` block" ) ;
302- db. span_label ( block. span , "unnecessary `unsafe` block" ) ;
303- if let Some ( ( kind, id) ) = self . is_enclosed ( block. id ) {
304- db. span_note ( self . tcx . hir . span ( id) ,
305- & format ! ( "because it's nested under this `unsafe` {}" , kind) ) ;
288+ self . unsafe_blocks . push ( ( block. id , self . used_unsafe . contains ( & block. id ) ) ) ;
306289 }
307- db. emit ( ) ;
308290 }
309291}
310292
311293fn check_unused_unsafe < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
312294 def_id : DefId ,
313- used_unsafe : FxHashSet < ast:: NodeId > )
295+ used_unsafe : & FxHashSet < ast:: NodeId > ,
296+ unsafe_blocks : & ' a mut Vec < ( ast:: NodeId , bool ) > )
314297{
315298 let body_id =
316299 tcx. hir . as_local_node_id ( def_id) . and_then ( |node_id| {
@@ -328,25 +311,27 @@ fn check_unused_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
328311 debug ! ( "check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})" ,
329312 def_id, body, used_unsafe) ;
330313
331- hir:: intravisit:: Visitor :: visit_body (
332- & mut UnusedUnsafeVisitor { tcx, used_unsafe } ,
333- body) ;
314+ let mut visitor = UnusedUnsafeVisitor { used_unsafe, unsafe_blocks } ;
315+ hir:: intravisit:: Visitor :: visit_body ( & mut visitor, body) ;
334316}
335317
336- fn unsafety_violations < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > , def_id : DefId ) ->
337- Rc < [ UnsafetyViolation ] >
318+ fn unsafety_check_result < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > , def_id : DefId )
319+ -> UnsafetyCheckResult
338320{
339321 debug ! ( "unsafety_violations({:?})" , def_id) ;
340322
341323 // NB: this borrow is valid because all the consumers of
342- // `mir_const ` force this.
343- let mir = & tcx. mir_const ( def_id) . borrow ( ) ;
324+ // `mir_built ` force this.
325+ let mir = & tcx. mir_built ( def_id) . borrow ( ) ;
344326
345327 let visibility_scope_info = match mir. visibility_scope_info {
346328 ClearOnDecode :: Set ( ref data) => data,
347329 ClearOnDecode :: Clear => {
348330 debug ! ( "unsafety_violations: {:?} - remote, skipping" , def_id) ;
349- return Rc :: new ( [ ] )
331+ return UnsafetyCheckResult {
332+ violations : Rc :: new ( [ ] ) ,
333+ unsafe_blocks : Rc :: new ( [ ] )
334+ }
350335 }
351336 } ;
352337
@@ -355,8 +340,43 @@ fn unsafety_violations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) ->
355340 mir, visibility_scope_info, tcx, param_env) ;
356341 checker. visit_mir ( mir) ;
357342
358- check_unused_unsafe ( tcx, def_id, checker. used_unsafe ) ;
359- checker. violations . into ( )
343+ check_unused_unsafe ( tcx, def_id, & checker. used_unsafe , & mut checker. inherited_blocks ) ;
344+ UnsafetyCheckResult {
345+ violations : checker. violations . into ( ) ,
346+ unsafe_blocks : checker. inherited_blocks . into ( )
347+ }
348+ }
349+
350+ /// Return the NodeId for an enclosing scope that is also `unsafe`
351+ fn is_enclosed ( tcx : TyCtxt ,
352+ used_unsafe : & FxHashSet < ast:: NodeId > ,
353+ id : ast:: NodeId ) -> Option < ( String , ast:: NodeId ) > {
354+ let parent_id = tcx. hir . get_parent_node ( id) ;
355+ if parent_id != id {
356+ if used_unsafe. contains ( & parent_id) {
357+ Some ( ( "block" . to_string ( ) , parent_id) )
358+ } else if let Some ( hir:: map:: NodeItem ( & hir:: Item {
359+ node : hir:: ItemFn ( _, hir:: Unsafety :: Unsafe , _, _, _, _) ,
360+ ..
361+ } ) ) = tcx. hir . find ( parent_id) {
362+ Some ( ( "fn" . to_string ( ) , parent_id) )
363+ } else {
364+ is_enclosed ( tcx, used_unsafe, parent_id)
365+ }
366+ } else {
367+ None
368+ }
369+ }
370+
371+ fn report_unused_unsafe ( tcx : TyCtxt , used_unsafe : & FxHashSet < ast:: NodeId > , id : ast:: NodeId ) {
372+ let span = tcx. hir . span ( id) ;
373+ let mut db = tcx. struct_span_lint_node ( UNUSED_UNSAFE , id, span, "unnecessary `unsafe` block" ) ;
374+ db. span_label ( span, "unnecessary `unsafe` block" ) ;
375+ if let Some ( ( kind, id) ) = is_enclosed ( tcx, used_unsafe, id) {
376+ db. span_note ( tcx. hir . span ( id) ,
377+ & format ! ( "because it's nested under this `unsafe` {}" , kind) ) ;
378+ }
379+ db. emit ( ) ;
360380}
361381
362382pub fn check_unsafety < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > , def_id : DefId ) {
@@ -367,9 +387,14 @@ pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
367387 _ => { }
368388 } ;
369389
390+ let UnsafetyCheckResult {
391+ violations,
392+ unsafe_blocks
393+ } = tcx. unsafety_check_result ( def_id) ;
394+
370395 for & UnsafetyViolation {
371396 source_info, description, lint_node_id
372- } in & * tcx . unsafety_violations ( def_id ) {
397+ } in violations . iter ( ) {
373398 // Report an error.
374399 if let Some ( lint_node_id) = lint_node_id {
375400 tcx. lint_node ( SAFE_EXTERN_STATICS ,
@@ -385,4 +410,15 @@ pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
385410 . emit ( ) ;
386411 }
387412 }
413+
414+ let mut unsafe_blocks: Vec < _ > = unsafe_blocks. into_iter ( ) . collect ( ) ;
415+ unsafe_blocks. sort ( ) ;
416+ let used_unsafe: FxHashSet < _ > = unsafe_blocks. iter ( )
417+ . flat_map ( |& & ( id, used) | if used { Some ( id) } else { None } )
418+ . collect ( ) ;
419+ for & ( block_id, is_used) in unsafe_blocks {
420+ if !is_used {
421+ report_unused_unsafe ( tcx, & used_unsafe, block_id) ;
422+ }
423+ }
388424}
0 commit comments