@@ -7,6 +7,7 @@ use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid
77use clippy_utils:: visitors:: find_all_ret_expressions;
88use clippy_utils:: { fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty} ;
99use rustc_errors:: Applicability ;
10+ use rustc_hir:: def:: { DefKind , Res } ;
1011use rustc_hir:: def_id:: DefId ;
1112use rustc_hir:: { BorrowKind , Expr , ExprKind , ItemKind , Node } ;
1213use rustc_hir_typeck:: { FnCtxt , Inherited } ;
@@ -37,6 +38,9 @@ pub fn check<'tcx>(
3738 if is_cloned_or_copied ( cx, method_name, method_def_id) {
3839 unnecessary_iter_cloned:: check ( cx, expr, method_name, receiver) ;
3940 } 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+ }
4044 // At this point, we know the call is of a `to_owned`-like function. The functions
4145 // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
4246 // 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(
233237 false
234238}
235239
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+
236292/// Checks whether `expr` is an argument in a function call and, if so, determines whether its call
237293/// of a `to_owned`-like function is unnecessary.
238294fn check_other_call_arg < ' tcx > (
0 commit comments