@@ -10,24 +10,26 @@ use hir_def::{
10
10
path:: Path ,
11
11
resolver:: { HasResolver , ResolveValueResult , Resolver , ValueNs } ,
12
12
type_ref:: Rawness ,
13
- AdtId , DefWithBodyId , FieldId , VariantId ,
13
+ AdtId , DefWithBodyId , FieldId , FunctionId , VariantId ,
14
14
} ;
15
+ use span:: Edition ;
15
16
16
17
use crate :: {
17
18
db:: HirDatabase , utils:: is_fn_unsafe_to_call, InferenceResult , Interner , TargetFeatures , TyExt ,
18
19
TyKind ,
19
20
} ;
20
21
21
- /// Returns `(unsafe_exprs, fn_is_unsafe)`.
22
- ///
23
- /// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`.
24
- pub fn missing_unsafe (
25
- db : & dyn HirDatabase ,
26
- def : DefWithBodyId ,
27
- ) -> ( Vec < ( ExprOrPatId , UnsafetyReason ) > , bool ) {
22
+ #[ derive( Debug , Default ) ]
23
+ pub struct MissingUnsafeResult {
24
+ pub unsafe_exprs : Vec < ( ExprOrPatId , UnsafetyReason ) > ,
25
+ /// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`.
26
+ pub fn_is_unsafe : bool ,
27
+ pub deprecated_safe_calls : Vec < ExprId > ,
28
+ }
29
+
30
+ pub fn missing_unsafe ( db : & dyn HirDatabase , def : DefWithBodyId ) -> MissingUnsafeResult {
28
31
let _p = tracing:: info_span!( "missing_unsafe" ) . entered ( ) ;
29
32
30
- let mut res = Vec :: new ( ) ;
31
33
let is_unsafe = match def {
32
34
DefWithBodyId :: FunctionId ( it) => db. function_data ( it) . is_unsafe ( ) ,
33
35
DefWithBodyId :: StaticId ( _)
@@ -37,11 +39,19 @@ pub fn missing_unsafe(
37
39
| DefWithBodyId :: FieldId ( _) => false ,
38
40
} ;
39
41
42
+ let mut res = MissingUnsafeResult { fn_is_unsafe : is_unsafe, ..MissingUnsafeResult :: default ( ) } ;
40
43
let body = db. body ( def) ;
41
44
let infer = db. infer ( def) ;
42
- let mut callback = |node, inside_unsafe_block, reason| {
43
- if inside_unsafe_block == InsideUnsafeBlock :: No {
44
- res. push ( ( node, reason) ) ;
45
+ let mut callback = |diag| match diag {
46
+ UnsafeDiagnostic :: UnsafeOperation { node, inside_unsafe_block, reason } => {
47
+ if inside_unsafe_block == InsideUnsafeBlock :: No {
48
+ res. unsafe_exprs . push ( ( node, reason) ) ;
49
+ }
50
+ }
51
+ UnsafeDiagnostic :: DeprecatedSafe2024 { node, inside_unsafe_block } => {
52
+ if inside_unsafe_block == InsideUnsafeBlock :: No {
53
+ res. deprecated_safe_calls . push ( node)
54
+ }
45
55
}
46
56
} ;
47
57
let mut visitor = UnsafeVisitor :: new ( db, & infer, & body, def, & mut callback) ;
@@ -56,7 +66,7 @@ pub fn missing_unsafe(
56
66
}
57
67
}
58
68
59
- ( res, is_unsafe )
69
+ res
60
70
}
61
71
62
72
#[ derive( Debug , Clone , Copy ) ]
@@ -75,15 +85,31 @@ pub enum InsideUnsafeBlock {
75
85
Yes ,
76
86
}
77
87
88
+ #[ derive( Debug ) ]
89
+ enum UnsafeDiagnostic {
90
+ UnsafeOperation {
91
+ node : ExprOrPatId ,
92
+ inside_unsafe_block : InsideUnsafeBlock ,
93
+ reason : UnsafetyReason ,
94
+ } ,
95
+ /// A lint.
96
+ DeprecatedSafe2024 { node : ExprId , inside_unsafe_block : InsideUnsafeBlock } ,
97
+ }
98
+
78
99
pub fn unsafe_expressions (
79
100
db : & dyn HirDatabase ,
80
101
infer : & InferenceResult ,
81
102
def : DefWithBodyId ,
82
103
body : & Body ,
83
104
current : ExprId ,
84
- unsafe_expr_cb : & mut dyn FnMut ( ExprOrPatId , InsideUnsafeBlock , UnsafetyReason ) ,
105
+ callback : & mut dyn FnMut ( InsideUnsafeBlock ) ,
85
106
) {
86
- let mut visitor = UnsafeVisitor :: new ( db, infer, body, def, unsafe_expr_cb) ;
107
+ let mut visitor_callback = |diag| {
108
+ if let UnsafeDiagnostic :: UnsafeOperation { inside_unsafe_block, .. } = diag {
109
+ callback ( inside_unsafe_block) ;
110
+ }
111
+ } ;
112
+ let mut visitor = UnsafeVisitor :: new ( db, infer, body, def, & mut visitor_callback) ;
87
113
_ = visitor. resolver . update_to_inner_scope ( db. upcast ( ) , def, current) ;
88
114
visitor. walk_expr ( current) ;
89
115
}
@@ -97,8 +123,10 @@ struct UnsafeVisitor<'a> {
97
123
inside_unsafe_block : InsideUnsafeBlock ,
98
124
inside_assignment : bool ,
99
125
inside_union_destructure : bool ,
100
- unsafe_expr_cb : & ' a mut dyn FnMut ( ExprOrPatId , InsideUnsafeBlock , UnsafetyReason ) ,
126
+ callback : & ' a mut dyn FnMut ( UnsafeDiagnostic ) ,
101
127
def_target_features : TargetFeatures ,
128
+ // FIXME: This needs to be the edition of the span of each call.
129
+ edition : Edition ,
102
130
}
103
131
104
132
impl < ' a > UnsafeVisitor < ' a > {
@@ -107,13 +135,14 @@ impl<'a> UnsafeVisitor<'a> {
107
135
infer : & ' a InferenceResult ,
108
136
body : & ' a Body ,
109
137
def : DefWithBodyId ,
110
- unsafe_expr_cb : & ' a mut dyn FnMut ( ExprOrPatId , InsideUnsafeBlock , UnsafetyReason ) ,
138
+ unsafe_expr_cb : & ' a mut dyn FnMut ( UnsafeDiagnostic ) ,
111
139
) -> Self {
112
140
let resolver = def. resolver ( db. upcast ( ) ) ;
113
141
let def_target_features = match def {
114
142
DefWithBodyId :: FunctionId ( func) => TargetFeatures :: from_attrs ( & db. attrs ( func. into ( ) ) ) ,
115
143
_ => TargetFeatures :: default ( ) ,
116
144
} ;
145
+ let edition = db. crate_graph ( ) [ resolver. module ( ) . krate ( ) ] . edition ;
117
146
Self {
118
147
db,
119
148
infer,
@@ -123,13 +152,34 @@ impl<'a> UnsafeVisitor<'a> {
123
152
inside_unsafe_block : InsideUnsafeBlock :: No ,
124
153
inside_assignment : false ,
125
154
inside_union_destructure : false ,
126
- unsafe_expr_cb,
155
+ callback : unsafe_expr_cb,
127
156
def_target_features,
157
+ edition,
128
158
}
129
159
}
130
160
131
- fn call_cb ( & mut self , node : ExprOrPatId , reason : UnsafetyReason ) {
132
- ( self . unsafe_expr_cb ) ( node, self . inside_unsafe_block , reason) ;
161
+ fn on_unsafe_op ( & mut self , node : ExprOrPatId , reason : UnsafetyReason ) {
162
+ ( self . callback ) ( UnsafeDiagnostic :: UnsafeOperation {
163
+ node,
164
+ inside_unsafe_block : self . inside_unsafe_block ,
165
+ reason,
166
+ } ) ;
167
+ }
168
+
169
+ fn check_call ( & mut self , node : ExprId , func : FunctionId ) {
170
+ let unsafety = is_fn_unsafe_to_call ( self . db , func, & self . def_target_features , self . edition ) ;
171
+ match unsafety {
172
+ crate :: utils:: Unsafety :: Safe => { }
173
+ crate :: utils:: Unsafety :: Unsafe => {
174
+ self . on_unsafe_op ( node. into ( ) , UnsafetyReason :: UnsafeFnCall )
175
+ }
176
+ crate :: utils:: Unsafety :: DeprecatedSafe2024 => {
177
+ ( self . callback ) ( UnsafeDiagnostic :: DeprecatedSafe2024 {
178
+ node,
179
+ inside_unsafe_block : self . inside_unsafe_block ,
180
+ } )
181
+ }
182
+ }
133
183
}
134
184
135
185
fn walk_pats_top ( & mut self , pats : impl Iterator < Item = PatId > , parent_expr : ExprId ) {
@@ -154,7 +204,9 @@ impl<'a> UnsafeVisitor<'a> {
154
204
| Pat :: Ref { .. }
155
205
| Pat :: Box { .. }
156
206
| Pat :: Expr ( ..)
157
- | Pat :: ConstBlock ( ..) => self . call_cb ( current. into ( ) , UnsafetyReason :: UnionField ) ,
207
+ | Pat :: ConstBlock ( ..) => {
208
+ self . on_unsafe_op ( current. into ( ) , UnsafetyReason :: UnionField )
209
+ }
158
210
// `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read.
159
211
Pat :: Missing | Pat :: Wild | Pat :: Or ( _) => { }
160
212
}
@@ -189,9 +241,7 @@ impl<'a> UnsafeVisitor<'a> {
189
241
match expr {
190
242
& Expr :: Call { callee, .. } => {
191
243
if let Some ( func) = self . infer [ callee] . as_fn_def ( self . db ) {
192
- if is_fn_unsafe_to_call ( self . db , func, & self . def_target_features ) {
193
- self . call_cb ( current. into ( ) , UnsafetyReason :: UnsafeFnCall ) ;
194
- }
244
+ self . check_call ( current, func) ;
195
245
}
196
246
}
197
247
Expr :: Path ( path) => {
@@ -217,18 +267,13 @@ impl<'a> UnsafeVisitor<'a> {
217
267
}
218
268
}
219
269
Expr :: MethodCall { .. } => {
220
- if self
221
- . infer
222
- . method_resolution ( current)
223
- . map ( |( func, _) | is_fn_unsafe_to_call ( self . db , func, & self . def_target_features ) )
224
- . unwrap_or ( false )
225
- {
226
- self . call_cb ( current. into ( ) , UnsafetyReason :: UnsafeFnCall ) ;
270
+ if let Some ( ( func, _) ) = self . infer . method_resolution ( current) {
271
+ self . check_call ( current, func) ;
227
272
}
228
273
}
229
274
Expr :: UnaryOp { expr, op : UnaryOp :: Deref } => {
230
275
if let TyKind :: Raw ( ..) = & self . infer [ * expr] . kind ( Interner ) {
231
- self . call_cb ( current. into ( ) , UnsafetyReason :: RawPtrDeref ) ;
276
+ self . on_unsafe_op ( current. into ( ) , UnsafetyReason :: RawPtrDeref ) ;
232
277
}
233
278
}
234
279
Expr :: Unsafe { .. } => {
@@ -243,7 +288,7 @@ impl<'a> UnsafeVisitor<'a> {
243
288
self . walk_pats_top ( std:: iter:: once ( target) , current) ;
244
289
self . inside_assignment = old_inside_assignment;
245
290
}
246
- Expr :: InlineAsm ( _) => self . call_cb ( current. into ( ) , UnsafetyReason :: InlineAsm ) ,
291
+ Expr :: InlineAsm ( _) => self . on_unsafe_op ( current. into ( ) , UnsafetyReason :: InlineAsm ) ,
247
292
// rustc allows union assignment to propagate through field accesses and casts.
248
293
Expr :: Cast { .. } => self . inside_assignment = inside_assignment,
249
294
Expr :: Field { .. } => {
@@ -252,7 +297,7 @@ impl<'a> UnsafeVisitor<'a> {
252
297
if let Some ( Either :: Left ( FieldId { parent : VariantId :: UnionId ( _) , .. } ) ) =
253
298
self . infer . field_resolution ( current)
254
299
{
255
- self . call_cb ( current. into ( ) , UnsafetyReason :: UnionField ) ;
300
+ self . on_unsafe_op ( current. into ( ) , UnsafetyReason :: UnionField ) ;
256
301
}
257
302
}
258
303
}
@@ -287,9 +332,9 @@ impl<'a> UnsafeVisitor<'a> {
287
332
if let Some ( ResolveValueResult :: ValueNs ( ValueNs :: StaticId ( id) , _) ) = value_or_partial {
288
333
let static_data = self . db . static_data ( id) ;
289
334
if static_data. mutable {
290
- self . call_cb ( node, UnsafetyReason :: MutableStatic ) ;
335
+ self . on_unsafe_op ( node, UnsafetyReason :: MutableStatic ) ;
291
336
} else if static_data. is_extern && !static_data. has_safe_kw {
292
- self . call_cb ( node, UnsafetyReason :: ExternStatic ) ;
337
+ self . on_unsafe_op ( node, UnsafetyReason :: ExternStatic ) ;
293
338
}
294
339
}
295
340
}
0 commit comments