@@ -1377,8 +1377,8 @@ pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
1377
1377
}
1378
1378
}
1379
1379
1380
- /// Check if `ty` is an `Iterator` and has side effects when iterated over. Currently, this only
1381
- /// checks if the `ty` contains mutable captures, and thus may be imcomplete .
1380
+ /// Check if a Ty<'_> of `Iterator` has side effects when iterated over by checking if it
1381
+ /// captures any mutable references or equivalents .
1382
1382
pub fn is_iter_with_side_effects < ' tcx > ( cx : & LateContext < ' tcx > , iter_ty : Ty < ' tcx > ) -> bool {
1383
1383
let Some ( iter_trait) = cx. tcx . lang_items ( ) . iterator_trait ( ) else {
1384
1384
return false ;
@@ -1388,22 +1388,50 @@ pub fn is_iter_with_side_effects<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>
1388
1388
}
1389
1389
1390
1390
fn is_iter_with_side_effects_impl < ' tcx > ( cx : & LateContext < ' tcx > , iter_ty : Ty < ' tcx > , iter_trait : DefId ) -> bool {
1391
- if implements_trait ( cx, iter_ty, iter_trait, & [ ] )
1392
- && let ty:: Adt ( _, args) = iter_ty. kind ( )
1393
- {
1394
- return args. types ( ) . any ( |arg_ty| {
1395
- if let ty:: Closure ( _, closure_args) = arg_ty. kind ( )
1396
- && let Some ( captures) = closure_args. types ( ) . next_back ( )
1397
- {
1398
- captures
1399
- . tuple_fields ( )
1400
- . iter ( )
1401
- . any ( |capture_ty| matches ! ( capture_ty. ref_mutability( ) , Some ( Mutability :: Mut ) ) )
1402
- } else {
1403
- is_iter_with_side_effects_impl ( cx, arg_ty, iter_trait)
1404
- }
1405
- } ) ;
1391
+ if let ty:: Adt ( adt_def, args) = iter_ty. kind ( ) {
1392
+ return adt_def
1393
+ . all_fields ( )
1394
+ . any ( |field| is_ty_with_side_effects ( cx, field. ty ( cx. tcx , args) , iter_trait) ) ;
1406
1395
}
1407
1396
1408
1397
false
1409
1398
}
1399
+
1400
+ fn is_ty_with_side_effects < ' tcx > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > , iter_trait : DefId ) -> bool {
1401
+ match ty. kind ( ) {
1402
+ ty:: RawPtr ( ..) | ty:: Ref ( ..) | ty:: Adt ( ..) => is_mutable_reference_or_equivalent_and ( cx, ty, |inner_ty| {
1403
+ !implements_trait ( cx, inner_ty, iter_trait, & [ ] ) || is_iter_with_side_effects_impl ( cx, inner_ty, iter_trait)
1404
+ } ) ,
1405
+ ty:: Closure ( _, closure_args) => {
1406
+ matches ! ( closure_args. types( ) . next_back( ) , Some ( captures) if captures. tuple_fields( ) . iter( ) . any( |capture_ty| is_ty_with_side_effects( cx, capture_ty, iter_trait) ) )
1407
+ } ,
1408
+ ty:: Array ( elem_ty, _) | ty:: Slice ( elem_ty) => is_ty_with_side_effects ( cx, * elem_ty, iter_trait) ,
1409
+ ty:: Tuple ( field_tys) => field_tys
1410
+ . iter ( )
1411
+ . any ( |field_ty| is_ty_with_side_effects ( cx, field_ty, iter_trait) ) ,
1412
+ _ => false ,
1413
+ }
1414
+ }
1415
+
1416
+ /// Check if `ty` is a mutable reference or equivalent. This includes:
1417
+ /// - A mutable reference/pointer.
1418
+ /// - A reference/pointer to a non-`Freeze` type.
1419
+ /// - A `PhantomData` type containing any of the previous.
1420
+ pub fn is_mutable_reference_or_equivalent < ' tcx > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> bool {
1421
+ is_mutable_reference_or_equivalent_and ( cx, ty, |_| true )
1422
+ }
1423
+
1424
+ fn is_mutable_reference_or_equivalent_and < ' tcx , F > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > , pred : F ) -> bool
1425
+ where
1426
+ F : Fn ( Ty < ' tcx > ) -> bool ,
1427
+ {
1428
+ match ty. kind ( ) {
1429
+ ty:: RawPtr ( ty, mutability) | ty:: Ref ( _, ty, mutability) => {
1430
+ ( mutability. is_mut ( ) || !ty. is_freeze ( cx. tcx , cx. typing_env ( ) ) ) && pred ( * ty)
1431
+ } ,
1432
+ ty:: Adt ( adt_def, args) => adt_def. all_fields ( ) . any ( |field| {
1433
+ matches ! ( field. ty( cx. tcx, args) . kind( ) , ty:: Adt ( adt_def, args) if adt_def. is_phantom_data( ) && args. types( ) . any( |arg_ty| is_mutable_reference_or_equivalent( cx, arg_ty) ) )
1434
+ } ) ,
1435
+ _ => false ,
1436
+ }
1437
+ }
0 commit comments