14
14
//! of MIR building, and only after this pass we think of the program has having the
15
15
//! normal MIR semantics.
16
16
17
- use syntax_pos:: Span ;
18
- use syntax:: ast:: NodeId ;
19
17
use rustc:: ty:: { self , TyCtxt , RegionKind } ;
20
18
use rustc:: hir;
21
19
use rustc:: mir:: * ;
@@ -84,9 +82,11 @@ fn lval_context<'a, 'tcx, D>(
84
82
85
83
/// Check if this function contains an unsafe block or is an unsafe function.
86
84
fn fn_contains_unsafe < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > , src : MirSource ) -> bool {
87
- use rustc:: hir:: intravisit:: { self , Visitor } ;
85
+ use rustc:: hir:: intravisit:: { self , Visitor , FnKind } ;
86
+ use rustc:: hir:: map:: blocks:: FnLikeNode ;
88
87
use rustc:: hir:: map:: Node ;
89
88
89
+ /// Decide if this is an unsafe block
90
90
fn block_is_unsafe ( block : & hir:: Block ) -> bool {
91
91
use rustc:: hir:: BlockCheckMode :: * ;
92
92
@@ -98,77 +98,74 @@ fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) ->
98
98
}
99
99
}
100
100
101
- let fn_node_id = match src {
102
- MirSource :: Fn ( node_id) => node_id,
101
+ /// Decide if this FnLike is a closure
102
+ fn fn_is_closure < ' a > ( fn_like : FnLikeNode < ' a > ) -> bool {
103
+ match fn_like. kind ( ) {
104
+ FnKind :: Closure ( _) => true ,
105
+ FnKind :: Method ( ..) | FnKind :: ItemFn ( ..) => false ,
106
+ }
107
+ }
108
+
109
+ let fn_like = match src {
110
+ MirSource :: Fn ( node_id) => {
111
+ match FnLikeNode :: from_node ( tcx. hir . get ( node_id) ) {
112
+ Some ( fn_like) => fn_like,
113
+ None => return false , // e.g. struct ctor shims -- such auto-generated code cannot
114
+ // contain unsafe.
115
+ }
116
+ } ,
103
117
_ => return false , // only functions can have unsafe
104
118
} ;
105
119
106
- struct FindUnsafe < ' b , ' tcx > where ' tcx : ' b {
107
- map : & ' b hir:: map :: Map < ' tcx > ,
108
- found_unsafe : bool ,
120
+ // Test if the function is marked unsafe.
121
+ if fn_like . unsafety ( ) == hir:: Unsafety :: Unsafe {
122
+ return true ;
109
123
}
110
- let mut finder = FindUnsafe { map : & tcx. hir , found_unsafe : false } ;
111
- // Run the visitor on the NodeId we got. Seems like there is no uniform way to do that.
112
- match tcx. hir . find ( fn_node_id) {
113
- Some ( Node :: NodeItem ( item) ) => finder. visit_item ( item) ,
114
- Some ( Node :: NodeImplItem ( item) ) => finder. visit_impl_item ( item) ,
115
- Some ( Node :: NodeTraitItem ( item) ) => finder. visit_trait_item ( item) ,
116
- Some ( Node :: NodeExpr ( item) ) => {
117
- // This is a closure.
118
- // We also have to walk up the parents and check that there is no unsafe block
119
- // there.
120
- let mut cur = fn_node_id;
121
- loop {
122
- // Go further upwards.
123
- let parent = tcx. hir . get_parent_node ( cur) ;
124
- if cur == parent {
125
- break ;
124
+
125
+ // For closures, we need to walk up the parents and see if we are inside an unsafe fn or
126
+ // unsafe block.
127
+ if fn_is_closure ( fn_like) {
128
+ let mut cur = fn_like. id ( ) ;
129
+ loop {
130
+ // Go further upwards.
131
+ let parent = tcx. hir . get_parent_node ( cur) ;
132
+ if cur == parent {
133
+ bug ! ( "Closures muts be inside a non-closure fn_like" ) ;
134
+ }
135
+ cur = parent;
136
+ // Check if this is an unsafe block
137
+ match tcx. hir . find ( cur) {
138
+ Some ( Node :: NodeExpr ( & hir:: Expr { node : hir:: ExprBlock ( ref block) , ..} ) ) => {
139
+ if block_is_unsafe ( & * block) {
140
+ // Found an unsafe block, we can bail out here.
141
+ return true ;
142
+ }
126
143
}
127
- cur = parent;
128
- // Check if this is a a block
129
- match tcx. hir . find ( cur) {
130
- Some ( Node :: NodeExpr ( & hir:: Expr { node : hir:: ExprBlock ( ref block) , ..} ) ) => {
131
- if block_is_unsafe ( & * block) {
132
- // Found an unsafe block, we can bail out here.
133
- return true ;
134
- }
144
+ _ => { } ,
145
+ }
146
+ // Check if this is a non-closure fn_like, at which point we have to stop moving up
147
+ if let Some ( fn_like) = FnLikeNode :: from_node ( tcx. hir . get ( cur) ) {
148
+ if !fn_is_closure ( fn_like) {
149
+ if fn_like. unsafety ( ) == hir:: Unsafety :: Unsafe {
150
+ return true ;
135
151
}
136
- _ => { } ,
152
+ break ;
137
153
}
138
154
}
139
- // Finally, visit the closure itself.
140
- finder. visit_expr ( item) ;
141
155
}
142
- Some ( Node :: NodeStructCtor ( _) ) => {
143
- // Auto-generated tuple struct ctor. Cannot contain unsafe code.
144
- return false ;
145
- } ,
146
- Some ( _) | None =>
147
- bug ! ( "Expected function, method or closure, found {}" ,
148
- tcx. hir. node_to_string( fn_node_id) ) ,
149
- } ;
156
+ }
150
157
151
- impl < ' b , ' tcx > Visitor < ' tcx > for FindUnsafe < ' b , ' tcx > {
152
- fn nested_visit_map < ' this > ( & ' this mut self ) -> intravisit:: NestedVisitorMap < ' this , ' tcx > {
153
- intravisit:: NestedVisitorMap :: OnlyBodies ( self . map )
154
- }
158
+ // Visit the entire body of the function and check for unsafe blocks in there
159
+ struct FindUnsafe {
160
+ found_unsafe : bool ,
161
+ }
162
+ let mut finder = FindUnsafe { found_unsafe : false } ;
163
+ // Run the visitor on the NodeId we got. Seems like there is no uniform way to do that.
164
+ finder. visit_body ( tcx. hir . body ( fn_like. body ( ) ) ) ;
155
165
156
- fn visit_fn ( & mut self , fk : intravisit:: FnKind < ' tcx > , fd : & ' tcx hir:: FnDecl ,
157
- b : hir:: BodyId , s : Span , id : NodeId )
158
- {
159
- assert ! ( !self . found_unsafe, "We should never see a fn when we already saw unsafe" ) ;
160
- let is_unsafe = match fk {
161
- intravisit:: FnKind :: ItemFn ( _, _, unsafety, ..) => unsafety == hir:: Unsafety :: Unsafe ,
162
- intravisit:: FnKind :: Method ( _, sig, ..) => sig. unsafety == hir:: Unsafety :: Unsafe ,
163
- intravisit:: FnKind :: Closure ( _) => false ,
164
- } ;
165
- if is_unsafe {
166
- // This is unsafe, and we are done.
167
- self . found_unsafe = true ;
168
- } else {
169
- // Go on searching.
170
- intravisit:: walk_fn ( self , fk, fd, b, s, id)
171
- }
166
+ impl < ' tcx > Visitor < ' tcx > for FindUnsafe {
167
+ fn nested_visit_map < ' this > ( & ' this mut self ) -> intravisit:: NestedVisitorMap < ' this , ' tcx > {
168
+ intravisit:: NestedVisitorMap :: None
172
169
}
173
170
174
171
fn visit_block ( & mut self , b : & ' tcx hir:: Block ) {
0 commit comments