Skip to content

Commit 71ea36b

Browse files
Extend UNNECESSARY_TO_OWNED to handle split
1 parent 7e650b7 commit 71ea36b

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

clippy_lints/src/methods/unnecessary_to_owned.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid
77
use clippy_utils::visitors::find_all_ret_expressions;
88
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
99
use rustc_errors::Applicability;
10+
use rustc_hir::def::{DefKind, Res};
1011
use rustc_hir::def_id::DefId;
1112
use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, Node};
1213
use 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.
238294
fn check_other_call_arg<'tcx>(

0 commit comments

Comments
 (0)