@@ -4,15 +4,17 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
44use clippy_utils:: sugg:: Sugg ;
55use clippy_utils:: ty:: { is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function} ;
66use clippy_utils:: {
7- can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id,
7+ can_move_expr_to_closure, fn_def_id , is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id,
88 peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind ,
99} ;
1010use rustc_ast:: util:: parser:: PREC_UNAMBIGUOUS ;
1111use rustc_errors:: Applicability ;
1212use rustc_hir:: def:: Res ;
1313use rustc_hir:: LangItem :: { OptionNone , OptionSome } ;
14- use rustc_hir:: { BindingMode , Expr , ExprKind , HirId , Mutability , Pat , PatKind , Path , QPath } ;
14+ use rustc_hir:: { self as hir , BindingMode , Expr , ExprKind , HirId , Mutability , Pat , PatKind , Path , QPath } ;
1515use rustc_lint:: LateContext ;
16+ use rustc_middle:: ty:: adjustment:: Adjust ;
17+ use rustc_middle:: ty:: { TypeFlags , TypeVisitableExt } ;
1618use rustc_span:: { sym, SyntaxContext } ;
1719
1820#[ expect( clippy:: too_many_arguments) ]
7375 }
7476
7577 // `map` won't perform any adjustments.
76- if !cx . typeck_results ( ) . expr_adjustments ( some_expr . expr ) . is_empty ( ) {
78+ if expr_has_type_coercion ( cx , expr) {
7779 return None ;
7880 }
7981
@@ -124,6 +126,12 @@ where
124126 } ;
125127
126128 let closure_expr_snip = some_expr. to_snippet_with_context ( cx, expr_ctxt, & mut app) ;
129+ let closure_body = if some_expr. needs_unsafe_block {
130+ format ! ( "unsafe {}" , closure_expr_snip. blockify( ) )
131+ } else {
132+ closure_expr_snip. to_string ( )
133+ } ;
134+
127135 let body_str = if let PatKind :: Binding ( annotation, id, some_binding, None ) = some_pat. kind {
128136 if !some_expr. needs_unsafe_block
129137 && let Some ( func) = can_pass_as_func ( cx, id, some_expr. expr )
@@ -145,20 +153,12 @@ where
145153 ""
146154 } ;
147155
148- if some_expr. needs_unsafe_block {
149- format ! ( "|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}" )
150- } else {
151- format ! ( "|{annotation}{some_binding}| {closure_expr_snip}" )
152- }
156+ format ! ( "|{annotation}{some_binding}| {closure_body}" )
153157 }
154158 } else if !is_wild_none && explicit_ref. is_none ( ) {
155159 // TODO: handle explicit reference annotations.
156160 let pat_snip = snippet_with_context ( cx, some_pat. span , expr_ctxt, ".." , & mut app) . 0 ;
157- if some_expr. needs_unsafe_block {
158- format ! ( "|{pat_snip}| unsafe {{ {closure_expr_snip} }}" )
159- } else {
160- format ! ( "|{pat_snip}| {closure_expr_snip}" )
161- }
161+ format ! ( "|{pat_snip}| {closure_body}" )
162162 } else {
163163 // Refutable bindings and mixed reference annotations can't be handled by `map`.
164164 return None ;
@@ -274,3 +274,71 @@ pub(super) fn try_parse_pattern<'tcx>(
274274fn is_none_expr ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
275275 is_res_lang_ctor ( cx, path_res ( cx, peel_blocks ( expr) ) , OptionNone )
276276}
277+
278+ fn expr_ty_adjusted ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
279+ cx. typeck_results ( )
280+ . expr_adjustments ( expr)
281+ . iter ( )
282+ // We do not care about exprs with `NeverToAny` adjustments, such as `panic!` call.
283+ . any ( |adj| !matches ! ( adj. kind, Adjust :: NeverToAny ) )
284+ }
285+
286+ fn expr_has_type_coercion < ' tcx > ( cx : & LateContext < ' tcx > , expr : & Expr < ' tcx > ) -> bool {
287+ if expr. span . from_expansion ( ) {
288+ return false ;
289+ }
290+ if expr_ty_adjusted ( cx, expr) {
291+ return true ;
292+ }
293+
294+ // Identify coercion sites and recursively check it those sites
295+ // actually has type adjustments.
296+ match expr. kind {
297+ // Function/method calls, including enum initialization.
298+ ExprKind :: Call ( _, args) | ExprKind :: MethodCall ( _, _, args, _) if let Some ( def_id) = fn_def_id ( cx, expr) => {
299+ let fn_sig = cx. tcx . fn_sig ( def_id) . instantiate_identity ( ) ;
300+ if !fn_sig. output ( ) . skip_binder ( ) . has_type_flags ( TypeFlags :: HAS_TY_PARAM ) {
301+ return false ;
302+ }
303+ let mut args_with_ty_param = fn_sig
304+ . inputs ( )
305+ . skip_binder ( )
306+ . iter ( )
307+ . zip ( args)
308+ . filter_map ( |( arg_ty, arg) | if arg_ty. has_type_flags ( TypeFlags :: HAS_TY_PARAM ) {
309+ Some ( arg)
310+ } else {
311+ None
312+ } ) ;
313+ args_with_ty_param. any ( |arg| expr_has_type_coercion ( cx, arg) )
314+ } ,
315+ // Struct/union initialization.
316+ ExprKind :: Struct ( _, fields, _) => {
317+ fields. iter ( ) . map ( |expr_field| expr_field. expr ) . any ( |ex| expr_has_type_coercion ( cx, ex) )
318+ } ,
319+ // those two `ref` keywords cannot be removed
320+ #[ allow( clippy:: needless_borrow) ]
321+ // Function results, including the final line of a block or a `return` expression.
322+ ExprKind :: Block ( hir:: Block { expr : Some ( ref ret_expr) , .. } , _) |
323+ ExprKind :: Ret ( Some ( ref ret_expr) ) => expr_has_type_coercion ( cx, ret_expr) ,
324+
325+ // ===== Coercion-propagation expressions =====
326+
327+ // Array, where the type is `[U; n]`.
328+ ExprKind :: Array ( elems) |
329+ // Tuple, `(U_0, U_1, ..., U_n)`.
330+ ExprKind :: Tup ( elems) => {
331+ elems. iter ( ) . any ( |elem| expr_has_type_coercion ( cx, elem) )
332+ } ,
333+ // Array but with repeating syntax.
334+ ExprKind :: Repeat ( rep_elem, _) => expr_has_type_coercion ( cx, rep_elem) ,
335+ // Others that may contain coercion sites.
336+ ExprKind :: If ( _, then, maybe_else) => {
337+ expr_has_type_coercion ( cx, then) || maybe_else. is_some_and ( |e| expr_has_type_coercion ( cx, e) )
338+ }
339+ ExprKind :: Match ( _, arms, _) => {
340+ arms. iter ( ) . map ( |arm| arm. body ) . any ( |body| expr_has_type_coercion ( cx, body) )
341+ }
342+ _ => false
343+ }
344+ }
0 commit comments