@@ -87,6 +87,17 @@ fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) ->
87
87
use rustc:: hir:: intravisit:: { self , Visitor } ;
88
88
use rustc:: hir:: map:: Node ;
89
89
90
+ fn block_is_unsafe ( block : & hir:: Block ) -> bool {
91
+ use rustc:: hir:: BlockCheckMode :: * ;
92
+
93
+ match block. rules {
94
+ UnsafeBlock ( _) | PushUnsafeBlock ( _) => true ,
95
+ // For PopUnsafeBlock, we don't actually know -- but we will always also check all
96
+ // parent blocks, so we can safely declare the PopUnsafeBlock to not be unsafe.
97
+ DefaultBlock | PopUnsafeBlock ( _) => false ,
98
+ }
99
+ }
100
+
90
101
let fn_node_id = match src {
91
102
MirSource :: Fn ( node_id) => node_id,
92
103
_ => return false , // only functions can have unsafe
@@ -101,8 +112,35 @@ fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) ->
101
112
match tcx. hir . find ( fn_node_id) {
102
113
Some ( Node :: NodeItem ( item) ) => finder. visit_item ( item) ,
103
114
Some ( Node :: NodeImplItem ( item) ) => finder. visit_impl_item ( item) ,
115
+ Some ( Node :: NodeExpr ( item) ) => {
116
+ // This is a closure.
117
+ // We also have to walk up the parents and check that there is no unsafe block
118
+ // there.
119
+ let mut cur = fn_node_id;
120
+ loop {
121
+ // Go further upwards.
122
+ let parent = tcx. hir . get_parent_node ( cur) ;
123
+ if cur == parent {
124
+ break ;
125
+ }
126
+ cur = parent;
127
+ // Check if this is a a block
128
+ match tcx. hir . find ( cur) {
129
+ Some ( Node :: NodeExpr ( & hir:: Expr { node : hir:: ExprBlock ( ref block) , ..} ) ) => {
130
+ if block_is_unsafe ( & * block) {
131
+ // We can bail out here.
132
+ return true ;
133
+ }
134
+ }
135
+ _ => { } ,
136
+ }
137
+ }
138
+ // Finally, visit the closure itself.
139
+ finder. visit_expr ( item) ;
140
+ }
104
141
Some ( _) | None =>
105
- bug ! ( "Expected method or function, found {}" , tcx. hir. node_to_string( fn_node_id) ) ,
142
+ bug ! ( "Expected function, method or closure, found {}" ,
143
+ tcx. hir. node_to_string( fn_node_id) ) ,
106
144
} ;
107
145
108
146
impl < ' b , ' tcx > Visitor < ' tcx > for FindUnsafe < ' b , ' tcx > {
@@ -113,7 +151,7 @@ fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) ->
113
151
fn visit_fn ( & mut self , fk : intravisit:: FnKind < ' tcx > , fd : & ' tcx hir:: FnDecl ,
114
152
b : hir:: BodyId , s : Span , id : NodeId )
115
153
{
116
- assert ! ( !self . found_unsafe, "We should never see more than one fn " ) ;
154
+ assert ! ( !self . found_unsafe, "We should never see a fn when we already saw unsafe " ) ;
117
155
let is_unsafe = match fk {
118
156
intravisit:: FnKind :: ItemFn ( _, _, unsafety, ..) => unsafety == hir:: Unsafety :: Unsafe ,
119
157
intravisit:: FnKind :: Method ( _, sig, ..) => sig. unsafety == hir:: Unsafety :: Unsafe ,
@@ -129,20 +167,15 @@ fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) ->
129
167
}
130
168
131
169
fn visit_block ( & mut self , b : & ' tcx hir:: Block ) {
132
- use rustc:: hir:: BlockCheckMode :: * ;
133
-
134
170
if self . found_unsafe { return ; } // short-circuit
135
171
136
- match b. rules {
137
- UnsafeBlock ( _) | PushUnsafeBlock ( _) => {
138
- // We found an unsafe block.
139
- self . found_unsafe = true ;
140
- }
141
- DefaultBlock | PopUnsafeBlock ( _) => {
142
- // No unsafe block here, go on searching.
143
- intravisit:: walk_block ( self , b) ;
144
- }
145
- } ;
172
+ if block_is_unsafe ( b) {
173
+ // We found an unsafe block. We can stop searching.
174
+ self . found_unsafe = true ;
175
+ } else {
176
+ // No unsafe block here, go on searching.
177
+ intravisit:: walk_block ( self , b) ;
178
+ }
146
179
}
147
180
}
148
181
0 commit comments