1 use super::implicit_clone::is_clone_like;
2 use super::unnecessary_iter_cloned::{self, is_into_iter};
3 use clippy_utils::diagnostics::span_lint_and_sugg;
4 use clippy_utils::source::snippet_opt;
5 use clippy_utils::ty::{
6 get_associated_type, get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, peel_mid_ty_refs,
8 use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
9 use clippy_utils::{meets_msrv, msrvs};
10 use rustc_errors::Applicability;
11 use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
12 use rustc_lint::LateContext;
13 use rustc_middle::mir::Mutability;
14 use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
15 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
16 use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
17 use rustc_semver::RustcVersion;
18 use rustc_span::{sym, Symbol};
21 use super::UNNECESSARY_TO_OWNED;
24 cx: &LateContext<'tcx>,
25 expr: &'tcx Expr<'tcx>,
27 receiver: &'tcx Expr<'_>,
28 args: &'tcx [Expr<'_>],
29 msrv: Option<RustcVersion>,
32 if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
35 if is_cloned_or_copied(cx, method_name, method_def_id) {
36 unnecessary_iter_cloned::check(cx, expr, method_name, receiver);
37 } else if is_to_owned_like(cx, method_name, method_def_id) {
38 // At this point, we know the call is of a `to_owned`-like function. The functions
39 // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
40 // based on its context, that is, whether it is a referent in an `AddrOf` expression, an
41 // argument in a `into_iter` call, or an argument in the call of some other function.
42 if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) {
45 if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) {
48 check_other_call_arg(cx, expr, method_name, receiver);
54 /// Checks whether `expr` is a referent in an `AddrOf` expression and, if so, determines whether its
55 /// call of a `to_owned`-like function is unnecessary.
56 #[allow(clippy::too_many_lines)]
57 fn check_addr_of_expr(
65 if let Some(parent) = get_parent_expr(cx, expr);
66 if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind;
67 let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::<Vec<_>>();
69 // For matching uses of `Cow::from`
72 kind: Adjust::Deref(None),
76 kind: Adjust::Borrow(_),
80 // For matching uses of arrays
83 kind: Adjust::Deref(None),
87 kind: Adjust::Borrow(_),
91 kind: Adjust::Pointer(_),
95 // For matching everything else
98 kind: Adjust::Deref(None),
102 kind: Adjust::Deref(Some(OverloadedDeref { .. })),
106 kind: Adjust::Borrow(_),
110 let receiver_ty = cx.typeck_results().expr_ty(receiver);
111 let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty);
112 let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty);
113 // Only flag cases satisfying at least one of the following three conditions:
114 // * the referent and receiver types are distinct
115 // * the referent/receiver type is a copyable array
116 // * the method is `Cow::into_owned`
117 // This restriction is to ensure there is no overlap between `redundant_clone` and this
118 // lint. It also avoids the following false positive:
119 // https://github.com/rust-lang/rust-clippy/issues/8759
120 // Arrays are a bit of a corner case. Non-copyable arrays are handled by
121 // `redundant_clone`, but copyable arrays are not.
122 if *referent_ty != receiver_ty
123 || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty))
124 || is_cow_into_owned(cx, method_name, method_def_id);
125 if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
127 if receiver_ty == target_ty && n_target_refs >= n_receiver_refs {
130 UNNECESSARY_TO_OWNED,
132 &format!("unnecessary use of `{}`", method_name),
138 width = n_target_refs - n_receiver_refs
140 Applicability::MachineApplicable,
145 if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
146 if implements_trait(cx, receiver_ty, deref_trait_id, &[]);
147 if get_associated_type(cx, receiver_ty, deref_trait_id, "Target") == Some(target_ty);
149 if n_receiver_refs > 0 {
152 UNNECESSARY_TO_OWNED,
154 &format!("unnecessary use of `{}`", method_name),
157 Applicability::MachineApplicable,
162 UNNECESSARY_TO_OWNED,
163 expr.span.with_lo(receiver.span.hi()),
164 &format!("unnecessary use of `{}`", method_name),
167 Applicability::MachineApplicable,
174 if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
175 if implements_trait(cx, receiver_ty, as_ref_trait_id, &[GenericArg::from(target_ty)]);
179 UNNECESSARY_TO_OWNED,
181 &format!("unnecessary use of `{}`", method_name),
183 format!("{}.as_ref()", receiver_snippet),
184 Applicability::MachineApplicable,
194 /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
195 /// call of a `to_owned`-like function is unnecessary.
196 fn check_into_iter_call_arg(
197 cx: &LateContext<'_>,
201 msrv: Option<RustcVersion>,
204 if let Some(parent) = get_parent_expr(cx, expr);
205 if let Some(callee_def_id) = fn_def_id(cx, parent);
206 if is_into_iter(cx, callee_def_id);
207 if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
208 let parent_ty = cx.typeck_results().expr_ty(parent);
209 if implements_trait(cx, parent_ty, iterator_trait_id, &[]);
210 if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty);
211 if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
213 if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
216 let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
221 // The next suggestion may be incorrect because the removal of the `to_owned`-like
222 // function could cause the iterator to hold a reference to a resource that is used
223 // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
226 UNNECESSARY_TO_OWNED,
228 &format!("unnecessary use of `{}`", method_name),
230 format!("{}.iter().{}()", receiver_snippet, cloned_or_copied),
231 Applicability::MaybeIncorrect,
239 /// Checks whether `expr` is an argument in a function call and, if so, determines whether its call
240 /// of a `to_owned`-like function is unnecessary.
241 fn check_other_call_arg<'tcx>(
242 cx: &LateContext<'tcx>,
243 expr: &'tcx Expr<'tcx>,
245 receiver: &'tcx Expr<'tcx>,
248 if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr);
249 if let Some((callee_def_id, call_substs, call_receiver, call_args)) = get_callee_substs_and_args(cx, maybe_call);
250 let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
251 let index = if let Some(call_receiver) = call_receiver {
252 std::iter::once(call_receiver).chain(call_args.iter()).position(|arg| arg.hir_id == maybe_arg.hir_id)
254 call_args.iter().position(|arg| arg.hir_id == maybe_arg.hir_id)
256 if let Some(i) = index;
257 if let Some(input) = fn_sig.inputs().get(i);
258 let (input, n_refs) = peel_mid_ty_refs(*input);
259 if let (trait_predicates, projection_predicates) = get_input_traits_and_projections(cx, callee_def_id, input);
260 if let Some(sized_def_id) = cx.tcx.lang_items().sized_trait();
261 if let [trait_predicate] = trait_predicates
263 .filter(|trait_predicate| trait_predicate.def_id() != sized_def_id)
264 .collect::<Vec<_>>()[..];
265 if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
266 if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
267 let receiver_ty = cx.typeck_results().expr_ty(receiver);
268 // If the callee has type parameters, they could appear in `projection_predicate.ty` or the
269 // types of `trait_predicate.trait_ref.substs`.
270 if if trait_predicate.def_id() == deref_trait_id {
271 if let [projection_predicate] = projection_predicates[..] {
274 .subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.term);
275 implements_trait(cx, receiver_ty, deref_trait_id, &[])
276 && get_associated_type(cx, receiver_ty, deref_trait_id, "Target")
277 .map_or(false, |ty| ty::Term::Ty(ty) == normalized_ty)
281 } else if trait_predicate.def_id() == as_ref_trait_id {
282 let composed_substs = compose_substs(
284 &trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
287 // if `expr` is a `String` and generic target is [u8], skip
288 // (https://github.com/rust-lang/rust-clippy/issues/9317).
289 if let [subst] = composed_substs[..]
290 && let GenericArgKind::Type(arg_ty) = subst.unpack()
292 && let inner_ty = arg_ty.builtin_index().unwrap()
293 && let ty::Uint(ty::UintTy::U8) = inner_ty.kind()
294 && let self_ty = cx.typeck_results().expr_ty(expr).peel_refs()
295 && is_type_diagnostic_item(cx, self_ty, sym::String) {
298 implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
303 // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
305 if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
306 let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 });
307 // If the trait is `AsRef` and the input type variable `T` occurs in the output type, then
308 // `T` must not be instantiated with a reference
309 // (https://github.com/rust-lang/rust-clippy/issues/8507).
310 if (n_refs == 0 && !receiver_ty.is_ref())
311 || trait_predicate.def_id() != as_ref_trait_id
312 || !fn_sig.output().contains(input);
313 if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
317 UNNECESSARY_TO_OWNED,
319 &format!("unnecessary use of `{}`", method_name),
321 format!("{:&>width$}{}", "", receiver_snippet, width = n_refs),
322 Applicability::MachineApplicable,
330 /// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such
331 /// expression found (if any) along with the immediately prior expression.
332 fn skip_addr_of_ancestors<'tcx>(
333 cx: &LateContext<'tcx>,
334 mut expr: &'tcx Expr<'tcx>,
335 ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
336 while let Some(parent) = get_parent_expr(cx, expr) {
337 if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind {
340 return Some((parent, expr));
346 /// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
347 /// `Substs`, and arguments.
348 fn get_callee_substs_and_args<'tcx>(
349 cx: &LateContext<'tcx>,
350 expr: &'tcx Expr<'tcx>,
351 ) -> Option<(DefId, SubstsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> {
353 if let ExprKind::Call(callee, args) = expr.kind;
354 let callee_ty = cx.typeck_results().expr_ty(callee);
355 if let ty::FnDef(callee_def_id, _) = callee_ty.kind();
357 let substs = cx.typeck_results().node_substs(callee.hir_id);
358 return Some((*callee_def_id, substs, None, args));
362 if let ExprKind::MethodCall(_, receiver, args, _) = expr.kind;
363 if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
365 let substs = cx.typeck_results().node_substs(expr.hir_id);
366 return Some((method_def_id, substs, Some(receiver), args));
372 /// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type.
373 fn get_input_traits_and_projections<'tcx>(
374 cx: &LateContext<'tcx>,
375 callee_def_id: DefId,
377 ) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) {
378 let mut trait_predicates = Vec::new();
379 let mut projection_predicates = Vec::new();
380 for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() {
381 match predicate.kind().skip_binder() {
382 PredicateKind::Trait(trait_predicate) => {
383 if trait_predicate.trait_ref.self_ty() == input {
384 trait_predicates.push(trait_predicate);
387 PredicateKind::Projection(projection_predicate) => {
388 if projection_predicate.projection_ty.self_ty() == input {
389 projection_predicates.push(projection_predicate);
395 (trait_predicates, projection_predicates)
398 /// Composes two substitutions by applying the latter to the types of the former.
399 fn compose_substs<'tcx>(
400 cx: &LateContext<'tcx>,
401 left: &[GenericArg<'tcx>],
402 right: SubstsRef<'tcx>,
403 ) -> Vec<GenericArg<'tcx>> {
406 if let GenericArgKind::Type(arg_ty) = arg.unpack() {
407 let normalized_ty = cx.tcx.subst_and_normalize_erasing_regions(right, cx.param_env, arg_ty);
408 GenericArg::from(normalized_ty)
416 /// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`.
417 fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
418 (method_name.as_str() == "cloned" || method_name.as_str() == "copied")
419 && is_diag_trait_item(cx, method_def_id, sym::Iterator)
422 /// Returns true if the named method can be used to convert the receiver to its "owned"
424 fn is_to_owned_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
425 is_clone_like(cx, method_name.as_str(), method_def_id)
426 || is_cow_into_owned(cx, method_name, method_def_id)
427 || is_to_string(cx, method_name, method_def_id)
430 /// Returns true if the named method is `Cow::into_owned`.
431 fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
432 method_name.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow)
435 /// Returns true if the named method is `ToString::to_string`.
436 fn is_to_string(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
437 method_name == sym::to_string && is_diag_trait_item(cx, method_def_id, sym::ToString)