@@ -118,7 +118,7 @@ use rustc_middle::ty::fast_reject::SimplifiedType;
118
118
use rustc_middle:: ty:: layout:: IntegerExt ;
119
119
use rustc_middle:: ty:: {
120
120
self as rustc_ty, Binder , BorrowKind , ClosureKind , EarlyBinder , FloatTy , GenericArgKind , GenericArgsRef , IntTy , Ty ,
121
- TyCtxt , TypeVisitableExt , UintTy , UpvarCapture ,
121
+ TyCtxt , TypeFlags , TypeVisitableExt , UintTy , UpvarCapture ,
122
122
} ;
123
123
use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
124
124
use rustc_span:: source_map:: SourceMap ;
@@ -3508,3 +3508,90 @@ pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'t
3508
3508
} )
3509
3509
. is_break ( )
3510
3510
}
3511
+
3512
+ /// Returns true if the specified `expr` requires coercion,
3513
+ /// meaning that it either has a coercion or propagates a coercion from one of its sub expressions.
3514
+ ///
3515
+ /// Similar to [`is_adjusted`], this not only checks if an expression's type was adjusted,
3516
+ /// but also going through extra steps to see if it fits the description of [coercion sites].
3517
+ ///
3518
+ /// You should used this when you want to avoid suggesting replacing an expression that is currently
3519
+ /// a coercion site or coercion propagating expression with one that is not.
3520
+ ///
3521
+ /// [coercion sites]: https://doc.rust-lang.org/stable/reference/type-coercions.html#coercion-sites
3522
+ pub fn expr_requires_coercion < ' tcx > ( cx : & LateContext < ' tcx > , expr : & Expr < ' tcx > ) -> bool {
3523
+ let expr_ty_is_adjusted = cx
3524
+ . typeck_results ( )
3525
+ . expr_adjustments ( expr)
3526
+ . iter ( )
3527
+ // ignore `NeverToAny` adjustments, such as `panic!` call.
3528
+ . any ( |adj| !matches ! ( adj. kind, Adjust :: NeverToAny ) ) ;
3529
+ if expr_ty_is_adjusted {
3530
+ return true ;
3531
+ }
3532
+
3533
+ // Identify coercion sites and recursively check if those sites
3534
+ // actually have type adjustments.
3535
+ match expr. kind {
3536
+ ExprKind :: Call ( _, args) | ExprKind :: MethodCall ( _, _, args, _) if let Some ( def_id) = fn_def_id ( cx, expr) => {
3537
+ let fn_sig = cx. tcx . fn_sig ( def_id) . instantiate_identity ( ) ;
3538
+
3539
+ if !fn_sig. output ( ) . skip_binder ( ) . has_type_flags ( TypeFlags :: HAS_TY_PARAM ) {
3540
+ return false ;
3541
+ }
3542
+
3543
+ let self_arg_count = usize:: from ( matches ! ( expr. kind, ExprKind :: MethodCall ( ..) ) ) ;
3544
+ let mut args_with_ty_param = {
3545
+ fn_sig
3546
+ . inputs ( )
3547
+ . skip_binder ( )
3548
+ . iter ( )
3549
+ . skip ( self_arg_count)
3550
+ . zip ( args)
3551
+ . filter_map ( |( arg_ty, arg) | {
3552
+ if arg_ty. has_type_flags ( TypeFlags :: HAS_TY_PARAM ) {
3553
+ Some ( arg)
3554
+ } else {
3555
+ None
3556
+ }
3557
+ } )
3558
+ } ;
3559
+ args_with_ty_param. any ( |arg| expr_requires_coercion ( cx, arg) )
3560
+ } ,
3561
+ // Struct/union initialization.
3562
+ ExprKind :: Struct ( qpath, _, _) => {
3563
+ let res = cx. typeck_results ( ) . qpath_res ( qpath, expr. hir_id ) ;
3564
+ if let Some ( ( _, v_def) ) = adt_and_variant_of_res ( cx, res) {
3565
+ let generic_args = cx. typeck_results ( ) . node_args ( expr. hir_id ) ;
3566
+ v_def
3567
+ . fields
3568
+ . iter ( )
3569
+ . any ( |field| field. ty ( cx. tcx , generic_args) . has_type_flags ( TypeFlags :: HAS_TY_PARAM ) )
3570
+ } else {
3571
+ false
3572
+ }
3573
+ } ,
3574
+ // Function results, including the final line of a block or a `return` expression.
3575
+ ExprKind :: Block (
3576
+ & Block {
3577
+ expr : Some ( ret_expr) , ..
3578
+ } ,
3579
+ _,
3580
+ )
3581
+ | ExprKind :: Ret ( Some ( ret_expr) ) => expr_requires_coercion ( cx, ret_expr) ,
3582
+
3583
+ // ===== Coercion-propagation expressions =====
3584
+ ExprKind :: Array ( elems) | ExprKind :: Tup ( elems) => elems. iter ( ) . any ( |elem| expr_requires_coercion ( cx, elem) ) ,
3585
+ // Array but with repeating syntax.
3586
+ ExprKind :: Repeat ( rep_elem, _) => expr_requires_coercion ( cx, rep_elem) ,
3587
+ // Others that may contain coercion sites.
3588
+ ExprKind :: If ( _, then, maybe_else) => {
3589
+ expr_requires_coercion ( cx, then) || maybe_else. is_some_and ( |e| expr_requires_coercion ( cx, e) )
3590
+ } ,
3591
+ ExprKind :: Match ( _, arms, _) => arms
3592
+ . iter ( )
3593
+ . map ( |arm| arm. body )
3594
+ . any ( |body| expr_requires_coercion ( cx, body) ) ,
3595
+ _ => false ,
3596
+ }
3597
+ }
0 commit comments