@@ -7,6 +7,7 @@ use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid
7
7
use clippy_utils:: visitors:: find_all_ret_expressions;
8
8
use clippy_utils:: { fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty} ;
9
9
use rustc_errors:: Applicability ;
10
+ use rustc_hir:: def:: { DefKind , Res } ;
10
11
use rustc_hir:: def_id:: DefId ;
11
12
use rustc_hir:: { BorrowKind , Expr , ExprKind , ItemKind , Node } ;
12
13
use rustc_hir_typeck:: { FnCtxt , Inherited } ;
@@ -37,6 +38,9 @@ pub fn check<'tcx>(
37
38
if is_cloned_or_copied ( cx, method_name, method_def_id) {
38
39
unnecessary_iter_cloned:: check ( cx, expr, method_name, receiver) ;
39
40
} else if is_to_owned_like ( cx, expr, method_name, method_def_id) {
41
+ if check_split_call_arg ( cx, expr, method_name, receiver) {
42
+ return ;
43
+ }
40
44
// At this point, we know the call is of a `to_owned`-like function. The functions
41
45
// `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
42
46
// based on its context, that is, whether it is a referent in an `AddrOf` expression, an
@@ -233,6 +237,58 @@ fn check_into_iter_call_arg(
233
237
false
234
238
}
235
239
240
+ /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
241
+ /// call of a `to_owned`-like function is unnecessary.
242
+ fn check_split_call_arg ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , method_name : Symbol , receiver : & Expr < ' _ > ) -> bool {
243
+ if let Some ( parent) = get_parent_expr ( cx, expr)
244
+ && let Some ( ( fn_name, argument_expr) ) = get_fn_name_and_arg ( cx, parent)
245
+ && fn_name. as_str ( ) == "split"
246
+ && let Some ( receiver_snippet) = snippet_opt ( cx, receiver. span )
247
+ && let Some ( arg_snippet) = snippet_opt ( cx, argument_expr. span )
248
+ {
249
+ // The next suggestion may be incorrect because the removal of the `to_owned`-like
250
+ // function could cause the iterator to hold a reference to a resource that is used
251
+ // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
252
+ span_lint_and_sugg (
253
+ cx,
254
+ UNNECESSARY_TO_OWNED ,
255
+ parent. span ,
256
+ & format ! ( "unnecessary use of `{method_name}`" ) ,
257
+ "use" ,
258
+ format ! ( "{receiver_snippet}.split({arg_snippet})" ) ,
259
+ Applicability :: MaybeIncorrect ,
260
+ ) ;
261
+ return true ;
262
+ }
263
+ false
264
+ }
265
+
266
+ fn get_fn_name_and_arg < ' tcx > ( cx : & LateContext < ' tcx > , expr : & Expr < ' tcx > ) -> Option < ( Symbol , Expr < ' tcx > ) > {
267
+ match & expr. kind {
268
+ ExprKind :: MethodCall ( path, _, [ arg_expr] , ..) => Some ( ( path. ident . name , * arg_expr) ) ,
269
+ ExprKind :: Call (
270
+ Expr {
271
+ kind : ExprKind :: Path ( qpath) ,
272
+ hir_id : path_hir_id,
273
+ ..
274
+ } ,
275
+ [ arg_expr] ,
276
+ ) => {
277
+ // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or
278
+ // deref to fn pointers, dyn Fn, impl Fn - #8850
279
+ if let Res :: Def ( DefKind :: Fn | DefKind :: Ctor ( ..) | DefKind :: AssocFn , def_id) =
280
+ cx. typeck_results ( ) . qpath_res ( qpath, * path_hir_id)
281
+ && let Some ( fn_name) = cx. tcx . opt_item_name ( def_id)
282
+ {
283
+ Some ( ( fn_name, * arg_expr) )
284
+ } else {
285
+ None
286
+ }
287
+ } ,
288
+ _ => None ,
289
+ }
290
+ }
291
+
236
292
/// Checks whether `expr` is an argument in a function call and, if so, determines whether its call
237
293
/// of a `to_owned`-like function is unnecessary.
238
294
fn check_other_call_arg < ' tcx > (
0 commit comments