@@ -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}
@@ -74,7 +76,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
7476 TerminatorKind :: Resume |
7577 TerminatorKind :: Return |
7678 TerminatorKind :: Unreachable => {
77- // safe (at least as emitted during MIR construction)
79+ // safe (at least as emitted during MIR construction)
7880 }
7981
8082 TerminatorKind :: Call { ref func, .. } => {
@@ -116,12 +118,20 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
116118 rvalue : & Rvalue < ' tcx > ,
117119 location : Location )
118120 {
119- if let & Rvalue :: Aggregate (
120- box AggregateKind :: Closure ( def_id, _) ,
121- _
122- ) = rvalue {
123- let unsafety_violations = self . tcx . unsafety_violations ( def_id) ;
124- self . register_violations ( & unsafety_violations) ;
121+ if let & Rvalue :: Aggregate ( box ref aggregate, _) = rvalue {
122+ match aggregate {
123+ & AggregateKind :: Array ( ..) |
124+ & AggregateKind :: Tuple |
125+ & AggregateKind :: Adt ( ..) => { }
126+ & AggregateKind :: Closure ( def_id, _) |
127+ & AggregateKind :: Generator ( def_id, _, _) => {
128+ let UnsafetyCheckResult {
129+ violations, unsafe_blocks
130+ } = self . tcx . unsafety_check_result ( def_id) ;
131+ self . inherited_blocks . extend ( unsafe_blocks. iter ( ) . cloned ( ) ) ;
132+ self . register_violations ( & violations, & unsafe_blocks) ;
133+ }
134+ }
125135 }
126136 self . super_rvalue ( rvalue, location) ;
127137 }
@@ -188,7 +198,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
188198 source_info,
189199 description : "use of extern static" ,
190200 lint_node_id : Some ( lint_root)
191- } ] ) ;
201+ } ] , & [ ] ) ;
192202 }
193203 }
194204 }
@@ -221,41 +231,49 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
221231 let source_info = self . source_info ;
222232 self . register_violations ( & [ UnsafetyViolation {
223233 source_info, description, lint_node_id : None
224- } ] ) ;
234+ } ] , & [ ] ) ;
225235 }
226236
227- fn register_violations ( & mut self , violations : & [ UnsafetyViolation ] ) {
228- match self . visibility_scope_info [ self . source_info . scope ] . safety {
237+ fn register_violations ( & mut self ,
238+ violations : & [ UnsafetyViolation ] ,
239+ unsafe_blocks : & [ ( ast:: NodeId , bool ) ] ) {
240+ let within_unsafe = match self . visibility_scope_info [ self . source_info . scope ] . safety {
229241 Safety :: Safe => {
230242 for violation in violations {
231243 if !self . violations . contains ( violation) {
232244 self . violations . push ( violation. clone ( ) )
233245 }
234246 }
247+
248+ false
235249 }
236- Safety :: BuiltinUnsafe | Safety :: FnUnsafe => { }
250+ Safety :: BuiltinUnsafe | Safety :: FnUnsafe => true ,
237251 Safety :: ExplicitUnsafe ( node_id) => {
238252 if !violations. is_empty ( ) {
239253 self . used_unsafe . insert ( node_id) ;
240254 }
255+ true
241256 }
242- }
257+ } ;
258+ self . inherited_blocks . extend ( unsafe_blocks. iter ( ) . map ( |& ( node_id, is_used) | {
259+ ( node_id, is_used && !within_unsafe)
260+ } ) ) ;
243261 }
244262}
245263
246264pub ( crate ) fn provide ( providers : & mut Providers ) {
247265 * providers = Providers {
248- unsafety_violations ,
266+ unsafety_check_result ,
249267 ..* providers
250268 } ;
251269}
252270
253- struct UnusedUnsafeVisitor < ' a , ' tcx : ' a > {
254- tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
255- used_unsafe : FxHashSet < ast:: NodeId >
271+ struct UnusedUnsafeVisitor < ' a > {
272+ used_unsafe : & ' a FxHashSet < ast :: NodeId > ,
273+ unsafe_blocks : & ' a mut Vec < ( ast:: NodeId , bool ) > ,
256274}
257275
258- impl < ' a , ' tcx > hir:: intravisit:: Visitor < ' tcx > for UnusedUnsafeVisitor < ' a , ' tcx > {
276+ impl < ' a , ' tcx > hir:: intravisit:: Visitor < ' tcx > for UnusedUnsafeVisitor < ' a > {
259277 fn nested_visit_map < ' this > ( & ' this mut self ) ->
260278 hir:: intravisit:: NestedVisitorMap < ' this , ' tcx >
261279 {
@@ -266,50 +284,15 @@ impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a, 'tcx>
266284 hir:: intravisit:: walk_block ( self , block) ;
267285
268286 if let hir:: UnsafeBlock ( hir:: UserProvided ) = block. rules {
269- if !self . used_unsafe . contains ( & block. id ) {
270- self . report_unused_unsafe ( block) ;
271- }
272- }
273- }
274- }
275-
276- impl < ' a , ' tcx > UnusedUnsafeVisitor < ' a , ' tcx > {
277- /// Return the NodeId for an enclosing scope that is also `unsafe`
278- fn is_enclosed ( & self , id : ast:: NodeId ) -> Option < ( String , ast:: NodeId ) > {
279- let parent_id = self . tcx . hir . get_parent_node ( id) ;
280- if parent_id != id {
281- if self . used_unsafe . contains ( & parent_id) {
282- Some ( ( "block" . to_string ( ) , parent_id) )
283- } else if let Some ( hir:: map:: NodeItem ( & hir:: Item {
284- node : hir:: ItemFn ( _, hir:: Unsafety :: Unsafe , _, _, _, _) ,
285- ..
286- } ) ) = self . tcx . hir . find ( parent_id) {
287- Some ( ( "fn" . to_string ( ) , parent_id) )
288- } else {
289- self . is_enclosed ( parent_id)
290- }
291- } else {
292- None
293- }
294- }
295-
296- fn report_unused_unsafe ( & self , block : & ' tcx hir:: Block ) {
297- let mut db = self . tcx . struct_span_lint_node ( UNUSED_UNSAFE ,
298- block. id ,
299- block. span ,
300- "unnecessary `unsafe` block" ) ;
301- db. span_label ( block. span , "unnecessary `unsafe` block" ) ;
302- if let Some ( ( kind, id) ) = self . is_enclosed ( block. id ) {
303- db. span_note ( self . tcx . hir . span ( id) ,
304- & format ! ( "because it's nested under this `unsafe` {}" , kind) ) ;
287+ self . unsafe_blocks . push ( ( block. id , self . used_unsafe . contains ( & block. id ) ) ) ;
305288 }
306- db. emit ( ) ;
307289 }
308290}
309291
310292fn check_unused_unsafe < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
311293 def_id : DefId ,
312- used_unsafe : FxHashSet < ast:: NodeId > )
294+ used_unsafe : & FxHashSet < ast:: NodeId > ,
295+ unsafe_blocks : & ' a mut Vec < ( ast:: NodeId , bool ) > )
313296{
314297 let body_id =
315298 tcx. hir . as_local_node_id ( def_id) . and_then ( |node_id| {
@@ -327,25 +310,27 @@ fn check_unused_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
327310 debug ! ( "check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})" ,
328311 def_id, body, used_unsafe) ;
329312
330- hir:: intravisit:: Visitor :: visit_body (
331- & mut UnusedUnsafeVisitor { tcx, used_unsafe } ,
332- body) ;
313+ let mut visitor = UnusedUnsafeVisitor { used_unsafe, unsafe_blocks } ;
314+ hir:: intravisit:: Visitor :: visit_body ( & mut visitor, body) ;
333315}
334316
335- fn unsafety_violations < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > , def_id : DefId ) ->
336- Rc < [ UnsafetyViolation ] >
317+ fn unsafety_check_result < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > , def_id : DefId )
318+ -> UnsafetyCheckResult
337319{
338320 debug ! ( "unsafety_violations({:?})" , def_id) ;
339321
340322 // NB: this borrow is valid because all the consumers of
341- // `mir_const ` force this.
342- let mir = & tcx. mir_const ( def_id) . borrow ( ) ;
323+ // `mir_built ` force this.
324+ let mir = & tcx. mir_built ( def_id) . borrow ( ) ;
343325
344326 let visibility_scope_info = match mir. visibility_scope_info {
345327 ClearOnDecode :: Set ( ref data) => data,
346328 ClearOnDecode :: Clear => {
347329 debug ! ( "unsafety_violations: {:?} - remote, skipping" , def_id) ;
348- return Rc :: new ( [ ] )
330+ return UnsafetyCheckResult {
331+ violations : Rc :: new ( [ ] ) ,
332+ unsafe_blocks : Rc :: new ( [ ] )
333+ }
349334 }
350335 } ;
351336
@@ -354,8 +339,43 @@ fn unsafety_violations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) ->
354339 mir, visibility_scope_info, tcx, param_env) ;
355340 checker. visit_mir ( mir) ;
356341
357- check_unused_unsafe ( tcx, def_id, checker. used_unsafe ) ;
358- checker. violations . into ( )
342+ check_unused_unsafe ( tcx, def_id, & checker. used_unsafe , & mut checker. inherited_blocks ) ;
343+ UnsafetyCheckResult {
344+ violations : checker. violations . into ( ) ,
345+ unsafe_blocks : checker. inherited_blocks . into ( )
346+ }
347+ }
348+
349+ /// Return the NodeId for an enclosing scope that is also `unsafe`
350+ fn is_enclosed ( tcx : TyCtxt ,
351+ used_unsafe : & FxHashSet < ast:: NodeId > ,
352+ id : ast:: NodeId ) -> Option < ( String , ast:: NodeId ) > {
353+ let parent_id = tcx. hir . get_parent_node ( id) ;
354+ if parent_id != id {
355+ if used_unsafe. contains ( & parent_id) {
356+ Some ( ( "block" . to_string ( ) , parent_id) )
357+ } else if let Some ( hir:: map:: NodeItem ( & hir:: Item {
358+ node : hir:: ItemFn ( _, hir:: Unsafety :: Unsafe , _, _, _, _) ,
359+ ..
360+ } ) ) = tcx. hir . find ( parent_id) {
361+ Some ( ( "fn" . to_string ( ) , parent_id) )
362+ } else {
363+ is_enclosed ( tcx, used_unsafe, parent_id)
364+ }
365+ } else {
366+ None
367+ }
368+ }
369+
370+ fn report_unused_unsafe ( tcx : TyCtxt , used_unsafe : & FxHashSet < ast:: NodeId > , id : ast:: NodeId ) {
371+ let span = tcx. hir . span ( id) ;
372+ let mut db = tcx. struct_span_lint_node ( UNUSED_UNSAFE , id, span, "unnecessary `unsafe` block" ) ;
373+ db. span_label ( span, "unnecessary `unsafe` block" ) ;
374+ if let Some ( ( kind, id) ) = is_enclosed ( tcx, used_unsafe, id) {
375+ db. span_note ( tcx. hir . span ( id) ,
376+ & format ! ( "because it's nested under this `unsafe` {}" , kind) ) ;
377+ }
378+ db. emit ( ) ;
359379}
360380
361381pub fn check_unsafety < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > , def_id : DefId ) {
@@ -366,9 +386,14 @@ pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
366386 _ => { }
367387 } ;
368388
389+ let UnsafetyCheckResult {
390+ violations,
391+ unsafe_blocks
392+ } = tcx. unsafety_check_result ( def_id) ;
393+
369394 for & UnsafetyViolation {
370395 source_info, description, lint_node_id
371- } in & * tcx . unsafety_violations ( def_id ) {
396+ } in violations . iter ( ) {
372397 // Report an error.
373398 if let Some ( lint_node_id) = lint_node_id {
374399 tcx. lint_node ( SAFE_EXTERN_STATICS ,
@@ -384,4 +409,15 @@ pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
384409 . emit ( ) ;
385410 }
386411 }
412+
413+ let mut unsafe_blocks: Vec < _ > = unsafe_blocks. into_iter ( ) . collect ( ) ;
414+ unsafe_blocks. sort ( ) ;
415+ let used_unsafe: FxHashSet < _ > = unsafe_blocks. iter ( )
416+ . flat_map ( |& & ( id, used) | if used { Some ( id) } else { None } )
417+ . collect ( ) ;
418+ for & ( block_id, is_used) in unsafe_blocks {
419+ if !is_used {
420+ report_unused_unsafe ( tcx, & used_unsafe, block_id) ;
421+ }
422+ }
387423}
0 commit comments