@@ -118,7 +118,7 @@ use rustc_middle::ty::fast_reject::SimplifiedType;
118118use rustc_middle:: ty:: layout:: IntegerExt ;
119119use rustc_middle:: ty:: {
120120 self as rustc_ty, Binder , BorrowKind , ClosureKind , EarlyBinder , FloatTy , GenericArgKind , GenericArgsRef , IntTy , Ty ,
121- TyCtxt , TypeVisitableExt , UintTy , UpvarCapture ,
121+ TyCtxt , TypeFlags , TypeVisitableExt , UintTy , UpvarCapture ,
122122} ;
123123use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
124124use rustc_span:: source_map:: SourceMap ;
@@ -3508,3 +3508,90 @@ pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'t
35083508 } )
35093509 . is_break ( )
35103510}
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