@@ -14,12 +14,14 @@ use self::RootUnsafeContext::*;
1414
1515use ty:: { self , TyCtxt } ;
1616use lint;
17+ use lint:: builtin:: UNUSED_UNSAFE ;
1718
18- use syntax:: ast;
19- use syntax_pos:: Span ;
20- use hir:: { self , PatKind } ;
2119use hir:: def:: Def ;
2220use hir:: intravisit:: { self , FnKind , Visitor , NestedVisitorMap } ;
21+ use hir:: { self , PatKind } ;
22+ use syntax:: ast;
23+ use syntax_pos:: Span ;
24+ use util:: nodemap:: FxHashSet ;
2325
2426#[ derive( Copy , Clone ) ]
2527struct UnsafeContext {
@@ -47,6 +49,7 @@ struct EffectCheckVisitor<'a, 'tcx: 'a> {
4749
4850 /// Whether we're in an unsafe context.
4951 unsafe_context : UnsafeContext ,
52+ used_unsafe : FxHashSet < ast:: NodeId > ,
5053}
5154
5255impl < ' a , ' tcx > EffectCheckVisitor < ' a , ' tcx > {
@@ -73,7 +76,7 @@ impl<'a, 'tcx> EffectCheckVisitor<'a, 'tcx> {
7376 UnsafeBlock ( block_id) => {
7477 // OK, but record this.
7578 debug ! ( "effect: recording unsafe block as used: {}" , block_id) ;
76- self . tcx . used_unsafe . borrow_mut ( ) . insert ( block_id) ;
79+ self . used_unsafe . insert ( block_id) ;
7780 }
7881 UnsafeFn => { }
7982 }
@@ -159,7 +162,48 @@ impl<'a, 'tcx> Visitor<'tcx> for EffectCheckVisitor<'a, 'tcx> {
159162
160163 intravisit:: walk_block ( self , block) ;
161164
162- self . unsafe_context = old_unsafe_context
165+ self . unsafe_context = old_unsafe_context;
166+
167+ // Don't warn about generated blocks, that'll just pollute the output.
168+ match block. rules {
169+ hir:: UnsafeBlock ( hir:: UserProvided ) => { }
170+ _ => return ,
171+ }
172+ if self . used_unsafe . contains ( & block. id ) {
173+ return
174+ }
175+
176+ /// Return the NodeId for an enclosing scope that is also `unsafe`
177+ fn is_enclosed ( tcx : TyCtxt ,
178+ used_unsafe : & FxHashSet < ast:: NodeId > ,
179+ id : ast:: NodeId ) -> Option < ( String , ast:: NodeId ) > {
180+ let parent_id = tcx. hir . get_parent_node ( id) ;
181+ if parent_id != id {
182+ if used_unsafe. contains ( & parent_id) {
183+ Some ( ( "block" . to_string ( ) , parent_id) )
184+ } else if let Some ( hir:: map:: NodeItem ( & hir:: Item {
185+ node : hir:: ItemFn ( _, hir:: Unsafety :: Unsafe , _, _, _, _) ,
186+ ..
187+ } ) ) = tcx. hir . find ( parent_id) {
188+ Some ( ( "fn" . to_string ( ) , parent_id) )
189+ } else {
190+ is_enclosed ( tcx, used_unsafe, parent_id)
191+ }
192+ } else {
193+ None
194+ }
195+ }
196+
197+ let mut db = self . tcx . struct_span_lint_node ( UNUSED_UNSAFE ,
198+ block. id ,
199+ block. span ,
200+ "unnecessary `unsafe` block" ) ;
201+ db. span_label ( block. span , "unnecessary `unsafe` block" ) ;
202+ if let Some ( ( kind, id) ) = is_enclosed ( self . tcx , & self . used_unsafe , block. id ) {
203+ db. span_note ( self . tcx . hir . span ( id) ,
204+ & format ! ( "because it's nested under this `unsafe` {}" , kind) ) ;
205+ }
206+ db. emit ( ) ;
163207 }
164208
165209 fn visit_expr ( & mut self , expr : & ' tcx hir:: Expr ) {
@@ -265,6 +309,7 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
265309 tables : & ty:: TypeckTables :: empty ( None ) ,
266310 body_id : hir:: BodyId { node_id : ast:: CRATE_NODE_ID } ,
267311 unsafe_context : UnsafeContext :: new ( SafeContext ) ,
312+ used_unsafe : FxHashSet ( ) ,
268313 } ;
269314
270315 tcx. hir . krate ( ) . visit_all_item_likes ( & mut visitor. as_deep_visitor ( ) ) ;
0 commit comments