@@ -4,15 +4,17 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
4
4
use clippy_utils:: sugg:: Sugg ;
5
5
use clippy_utils:: ty:: { is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function} ;
6
6
use 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,
8
8
peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind ,
9
9
} ;
10
10
use rustc_ast:: util:: parser:: PREC_UNAMBIGUOUS ;
11
11
use rustc_errors:: Applicability ;
12
12
use rustc_hir:: def:: Res ;
13
13
use 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 } ;
15
15
use rustc_lint:: LateContext ;
16
+ use rustc_middle:: ty:: adjustment:: Adjust ;
17
+ use rustc_middle:: ty:: { TypeFlags , TypeVisitableExt } ;
16
18
use rustc_span:: { sym, SyntaxContext } ;
17
19
18
20
#[ expect( clippy:: too_many_arguments) ]
73
75
}
74
76
75
77
// `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) {
77
79
return None ;
78
80
}
79
81
@@ -124,6 +126,12 @@ where
124
126
} ;
125
127
126
128
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
+
127
135
let body_str = if let PatKind :: Binding ( annotation, id, some_binding, None ) = some_pat. kind {
128
136
if !some_expr. needs_unsafe_block
129
137
&& let Some ( func) = can_pass_as_func ( cx, id, some_expr. expr )
@@ -145,20 +153,12 @@ where
145
153
""
146
154
} ;
147
155
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}" )
153
157
}
154
158
} else if !is_wild_none && explicit_ref. is_none ( ) {
155
159
// TODO: handle explicit reference annotations.
156
160
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}" )
162
162
} else {
163
163
// Refutable bindings and mixed reference annotations can't be handled by `map`.
164
164
return None ;
@@ -274,3 +274,71 @@ pub(super) fn try_parse_pattern<'tcx>(
274
274
fn is_none_expr ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
275
275
is_res_lang_ctor ( cx, path_res ( cx, peel_blocks ( expr) ) , OptionNone )
276
276
}
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