@@ -10,24 +10,26 @@ use hir_def::{
1010 path:: Path ,
1111 resolver:: { HasResolver , ResolveValueResult , Resolver , ValueNs } ,
1212 type_ref:: Rawness ,
13- AdtId , DefWithBodyId , FieldId , VariantId ,
13+ AdtId , DefWithBodyId , FieldId , FunctionId , VariantId ,
1414} ;
15+ use span:: Edition ;
1516
1617use crate :: {
1718 db:: HirDatabase , utils:: is_fn_unsafe_to_call, InferenceResult , Interner , TargetFeatures , TyExt ,
1819 TyKind ,
1920} ;
2021
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 {
2831 let _p = tracing:: info_span!( "missing_unsafe" ) . entered ( ) ;
2932
30- let mut res = Vec :: new ( ) ;
3133 let is_unsafe = match def {
3234 DefWithBodyId :: FunctionId ( it) => db. function_data ( it) . is_unsafe ( ) ,
3335 DefWithBodyId :: StaticId ( _)
@@ -37,11 +39,19 @@ pub fn missing_unsafe(
3739 | DefWithBodyId :: FieldId ( _) => false ,
3840 } ;
3941
42+ let mut res = MissingUnsafeResult { fn_is_unsafe : is_unsafe, ..MissingUnsafeResult :: default ( ) } ;
4043 let body = db. body ( def) ;
4144 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+ }
4555 }
4656 } ;
4757 let mut visitor = UnsafeVisitor :: new ( db, & infer, & body, def, & mut callback) ;
@@ -56,7 +66,7 @@ pub fn missing_unsafe(
5666 }
5767 }
5868
59- ( res, is_unsafe )
69+ res
6070}
6171
6272#[ derive( Debug , Clone , Copy ) ]
@@ -75,15 +85,31 @@ pub enum InsideUnsafeBlock {
7585 Yes ,
7686}
7787
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+
7899pub fn unsafe_expressions (
79100 db : & dyn HirDatabase ,
80101 infer : & InferenceResult ,
81102 def : DefWithBodyId ,
82103 body : & Body ,
83104 current : ExprId ,
84- unsafe_expr_cb : & mut dyn FnMut ( ExprOrPatId , InsideUnsafeBlock , UnsafetyReason ) ,
105+ callback : & mut dyn FnMut ( InsideUnsafeBlock ) ,
85106) {
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) ;
87113 _ = visitor. resolver . update_to_inner_scope ( db. upcast ( ) , def, current) ;
88114 visitor. walk_expr ( current) ;
89115}
@@ -97,8 +123,10 @@ struct UnsafeVisitor<'a> {
97123 inside_unsafe_block : InsideUnsafeBlock ,
98124 inside_assignment : bool ,
99125 inside_union_destructure : bool ,
100- unsafe_expr_cb : & ' a mut dyn FnMut ( ExprOrPatId , InsideUnsafeBlock , UnsafetyReason ) ,
126+ callback : & ' a mut dyn FnMut ( UnsafeDiagnostic ) ,
101127 def_target_features : TargetFeatures ,
128+ // FIXME: This needs to be the edition of the span of each call.
129+ edition : Edition ,
102130}
103131
104132impl < ' a > UnsafeVisitor < ' a > {
@@ -107,13 +135,14 @@ impl<'a> UnsafeVisitor<'a> {
107135 infer : & ' a InferenceResult ,
108136 body : & ' a Body ,
109137 def : DefWithBodyId ,
110- unsafe_expr_cb : & ' a mut dyn FnMut ( ExprOrPatId , InsideUnsafeBlock , UnsafetyReason ) ,
138+ unsafe_expr_cb : & ' a mut dyn FnMut ( UnsafeDiagnostic ) ,
111139 ) -> Self {
112140 let resolver = def. resolver ( db. upcast ( ) ) ;
113141 let def_target_features = match def {
114142 DefWithBodyId :: FunctionId ( func) => TargetFeatures :: from_attrs ( & db. attrs ( func. into ( ) ) ) ,
115143 _ => TargetFeatures :: default ( ) ,
116144 } ;
145+ let edition = db. crate_graph ( ) [ resolver. module ( ) . krate ( ) ] . edition ;
117146 Self {
118147 db,
119148 infer,
@@ -123,13 +152,34 @@ impl<'a> UnsafeVisitor<'a> {
123152 inside_unsafe_block : InsideUnsafeBlock :: No ,
124153 inside_assignment : false ,
125154 inside_union_destructure : false ,
126- unsafe_expr_cb,
155+ callback : unsafe_expr_cb,
127156 def_target_features,
157+ edition,
128158 }
129159 }
130160
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+ }
133183 }
134184
135185 fn walk_pats_top ( & mut self , pats : impl Iterator < Item = PatId > , parent_expr : ExprId ) {
@@ -154,7 +204,9 @@ impl<'a> UnsafeVisitor<'a> {
154204 | Pat :: Ref { .. }
155205 | Pat :: Box { .. }
156206 | 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+ }
158210 // `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read.
159211 Pat :: Missing | Pat :: Wild | Pat :: Or ( _) => { }
160212 }
@@ -189,9 +241,7 @@ impl<'a> UnsafeVisitor<'a> {
189241 match expr {
190242 & Expr :: Call { callee, .. } => {
191243 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) ;
195245 }
196246 }
197247 Expr :: Path ( path) => {
@@ -217,18 +267,13 @@ impl<'a> UnsafeVisitor<'a> {
217267 }
218268 }
219269 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) ;
227272 }
228273 }
229274 Expr :: UnaryOp { expr, op : UnaryOp :: Deref } => {
230275 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 ) ;
232277 }
233278 }
234279 Expr :: Unsafe { .. } => {
@@ -243,7 +288,7 @@ impl<'a> UnsafeVisitor<'a> {
243288 self . walk_pats_top ( std:: iter:: once ( target) , current) ;
244289 self . inside_assignment = old_inside_assignment;
245290 }
246- Expr :: InlineAsm ( _) => self . call_cb ( current. into ( ) , UnsafetyReason :: InlineAsm ) ,
291+ Expr :: InlineAsm ( _) => self . on_unsafe_op ( current. into ( ) , UnsafetyReason :: InlineAsm ) ,
247292 // rustc allows union assignment to propagate through field accesses and casts.
248293 Expr :: Cast { .. } => self . inside_assignment = inside_assignment,
249294 Expr :: Field { .. } => {
@@ -252,7 +297,7 @@ impl<'a> UnsafeVisitor<'a> {
252297 if let Some ( Either :: Left ( FieldId { parent : VariantId :: UnionId ( _) , .. } ) ) =
253298 self . infer . field_resolution ( current)
254299 {
255- self . call_cb ( current. into ( ) , UnsafetyReason :: UnionField ) ;
300+ self . on_unsafe_op ( current. into ( ) , UnsafetyReason :: UnionField ) ;
256301 }
257302 }
258303 }
@@ -287,9 +332,9 @@ impl<'a> UnsafeVisitor<'a> {
287332 if let Some ( ResolveValueResult :: ValueNs ( ValueNs :: StaticId ( id) , _) ) = value_or_partial {
288333 let static_data = self . db . static_data ( id) ;
289334 if static_data. mutable {
290- self . call_cb ( node, UnsafetyReason :: MutableStatic ) ;
335+ self . on_unsafe_op ( node, UnsafetyReason :: MutableStatic ) ;
291336 } 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 ) ;
293338 }
294339 }
295340 }
0 commit comments