diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f64cd0e914e..7b5279cda6ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3210,6 +3210,7 @@ Released 2018-09-13 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by +[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap [`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 9ac77d638799..3d3999d4cc0d 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -181,6 +181,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::UNNECESSARY_FILTER_MAP), LintId::of(methods::UNNECESSARY_FOLD), LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), + LintId::of(methods::UNNECESSARY_TO_OWNED), LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT), LintId::of(methods::USELESS_ASREF), LintId::of(methods::WRONG_SELF_CONVENTION), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 68889f4f50a4..766c5ba1bcb0 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -315,6 +315,7 @@ store.register_lints(&[ methods::UNNECESSARY_FILTER_MAP, methods::UNNECESSARY_FOLD, methods::UNNECESSARY_LAZY_EVALUATIONS, + methods::UNNECESSARY_TO_OWNED, methods::UNWRAP_OR_ELSE_DEFAULT, methods::UNWRAP_USED, methods::USELESS_ASREF, diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs index a0d5cf9418e0..2ea0b696f1fe 100644 --- a/clippy_lints/src/lib.register_perf.rs +++ b/clippy_lints/src/lib.register_perf.rs @@ -17,6 +17,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(methods::MANUAL_STR_REPEAT), LintId::of(methods::OR_FUN_CALL), LintId::of(methods::SINGLE_CHAR_PATTERN), + LintId::of(methods::UNNECESSARY_TO_OWNED), LintId::of(misc::CMP_OWNED), LintId::of(mutex_atomic::MUTEX_ATOMIC), LintId::of(redundant_clone::REDUNDANT_CLONE), diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 81c42de145f6..90492ffda3cc 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -12,15 +12,7 @@ use super::IMPLICIT_CLONE; pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, span: Span) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if match method_name { - "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr), - "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned), - "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path), - "to_vec" => cx.tcx.impl_of_method(method_def_id) - .map(|impl_did| Some(impl_did) == cx.tcx.lang_items().slice_alloc_impl()) - == Some(true), - _ => false, - }; + if is_clone_like(cx, method_name, method_def_id); let return_type = cx.typeck_results().expr_ty(expr); let input_type = cx.typeck_results().expr_ty(recv).peel_refs(); if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did)); @@ -38,3 +30,22 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv } } } + +/// Returns true if the named method can be used to clone the receiver. +/// Note that `to_string` is not flagged by `implicit_clone`. So other lints that call +/// `is_clone_like` and that do flag `to_string` must handle it separately. See, e.g., +/// `is_to_owned_like` in `unnecessary_to_owned.rs`. +pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir::def_id::DefId) -> bool { + match method_name { + "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr), + "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned), + "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path), + "to_vec" => { + cx.tcx + .impl_of_method(method_def_id) + .map(|impl_did| Some(impl_did) == cx.tcx.lang_items().slice_alloc_impl()) + == Some(true) + }, + _ => false, + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 58ec22135356..3c43671dd34c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -56,7 +56,9 @@ mod suspicious_splitn; mod uninit_assumed_init; mod unnecessary_filter_map; mod unnecessary_fold; +mod unnecessary_iter_cloned; mod unnecessary_lazy_eval; +mod unnecessary_to_owned; mod unwrap_or_else_default; mod unwrap_used; mod useless_asref; @@ -1885,6 +1887,32 @@ declare_clippy_lint! { "usages of `str::splitn` that can be replaced with `str::split`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned) + /// and other `to_owned`-like functions. + /// + /// ### Why is this bad? + /// The unnecessary calls result in useless allocations. + /// + /// ### Example + /// ```rust + /// let path = std::path::Path::new("x"); + /// foo(&path.to_string_lossy().to_string()); + /// fn foo(s: &str) {} + /// ``` + /// Use instead: + /// ```rust + /// let path = std::path::Path::new("x"); + /// foo(&path.to_string_lossy()); + /// fn foo(s: &str) {} + /// ``` + #[clippy::version = "1.58.0"] + pub UNNECESSARY_TO_OWNED, + perf, + "unnecessary calls to `to_owned`-like functions" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -1964,7 +1992,8 @@ impl_lint_pass!(Methods => [ MANUAL_STR_REPEAT, EXTEND_WITH_DRAIN, MANUAL_SPLIT_ONCE, - NEEDLESS_SPLITN + NEEDLESS_SPLITN, + UNNECESSARY_TO_OWNED, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2007,6 +2036,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { single_char_add_str::check(cx, expr, args); into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args); single_char_pattern::check(cx, expr, method_call.ident.name, args); + unnecessary_to_owned::check(cx, expr, method_call.ident.name, args); }, hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs new file mode 100644 index 000000000000..8300df03e993 --- /dev/null +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -0,0 +1,177 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher::ForLoop; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait}; +use clippy_utils::{fn_def_id, get_parent_expr, path_to_local_id, usage}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, HirId, LangItem, Mutability, Pat}; +use rustc_lint::LateContext; +use rustc_middle::{hir::map::Map, ty}; +use rustc_span::{sym, Symbol}; + +use super::UNNECESSARY_TO_OWNED; + +pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, receiver: &'tcx Expr<'tcx>) -> bool { + if_chain! { + if let Some(parent) = get_parent_expr(cx, expr); + if let Some(callee_def_id) = fn_def_id(cx, parent); + if is_into_iter(cx, callee_def_id); + then { + check_for_loop_iter(cx, parent, method_name, receiver) + } else { + false + } + } +} + +/// Checks whether `expr` is an iterator in a `for` loop and, if so, determines whether the +/// iterated-over items could be iterated over by reference. The reason why `check` above does not +/// include this code directly is so that it can be called from +/// `unnecessary_into_owned::check_into_iter_call_arg`. +pub fn check_for_loop_iter( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + method_name: Symbol, + receiver: &'tcx Expr<'tcx>, +) -> bool { + if_chain! { + if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent)); + if let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent); + let (clone_or_copy_needed, addr_of_exprs) = clone_or_copy_needed(cx, pat, body); + if !clone_or_copy_needed; + if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); + then { + let snippet = if_chain! { + if let ExprKind::MethodCall(maybe_iter_method_name, _, [collection], _) = receiver.kind; + if maybe_iter_method_name.ident.name == sym::iter; + + if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); + let receiver_ty = cx.typeck_results().expr_ty(receiver); + if implements_trait(cx, receiver_ty, iterator_trait_id, &[]); + if let Some(iter_item_ty) = get_iterator_item_ty(cx, receiver_ty); + + if let Some(into_iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator); + let collection_ty = cx.typeck_results().expr_ty(collection); + if implements_trait(cx, collection_ty, into_iterator_trait_id, &[]); + if let Some(into_iter_item_ty) = get_associated_type(cx, collection_ty, into_iterator_trait_id, "Item"); + + if iter_item_ty == into_iter_item_ty; + if let Some(collection_snippet) = snippet_opt(cx, collection.span); + then { + collection_snippet + } else { + receiver_snippet + } + }; + span_lint_and_then( + cx, + UNNECESSARY_TO_OWNED, + expr.span, + &format!("unnecessary use of `{}`", method_name), + |diag| { + diag.span_suggestion(expr.span, "use", snippet, Applicability::MachineApplicable); + for addr_of_expr in addr_of_exprs { + match addr_of_expr.kind { + ExprKind::AddrOf(_, _, referent) => { + let span = addr_of_expr.span.with_hi(referent.span.lo()); + diag.span_suggestion(span, "remove this `&`", String::new(), Applicability::MachineApplicable); + } + _ => unreachable!(), + } + } + } + ); + return true; + } + } + false +} + +/// The core logic of `check_for_loop_iter` above, this function wraps a use of +/// `CloneOrCopyVisitor`. +fn clone_or_copy_needed( + cx: &LateContext<'tcx>, + pat: &Pat<'tcx>, + body: &'tcx Expr<'tcx>, +) -> (bool, Vec<&'tcx Expr<'tcx>>) { + let mut visitor = CloneOrCopyVisitor { + cx, + binding_hir_ids: pat_bindings(pat), + clone_or_copy_needed: false, + addr_of_exprs: Vec::new(), + }; + visitor.visit_expr(body); + (visitor.clone_or_copy_needed, visitor.addr_of_exprs) +} + +/// Returns a vector of all `HirId`s bound by the pattern. +fn pat_bindings(pat: &Pat<'_>) -> Vec { + let mut collector = usage::ParamBindingIdCollector { + binding_hir_ids: Vec::new(), + }; + collector.visit_pat(pat); + collector.binding_hir_ids +} + +/// `clone_or_copy_needed` will be false when `CloneOrCopyVisitor` is done visiting if the only +/// operations performed on `binding_hir_ids` are: +/// * to take non-mutable references to them +/// * to use them as non-mutable `&self` in method calls +/// If any of `binding_hir_ids` is used in any other way, then `clone_or_copy_needed` will be true +/// when `CloneOrCopyVisitor` is done visiting. +struct CloneOrCopyVisitor<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + binding_hir_ids: Vec, + clone_or_copy_needed: bool, + addr_of_exprs: Vec<&'tcx Expr<'tcx>>, +} + +impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + walk_expr(self, expr); + if self.is_binding(expr) { + if let Some(parent) = get_parent_expr(self.cx, expr) { + match parent.kind { + ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) => { + self.addr_of_exprs.push(parent); + return; + }, + ExprKind::MethodCall(_, _, args, _) => { + if_chain! { + if args.iter().skip(1).all(|arg| !self.is_binding(arg)); + if let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id); + let method_ty = self.cx.tcx.type_of(method_def_id); + let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder(); + if matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Not)); + then { + return; + } + } + }, + _ => {}, + } + } + self.clone_or_copy_needed = true; + } + } +} + +impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> { + fn is_binding(&self, expr: &Expr<'tcx>) -> bool { + self.binding_hir_ids + .iter() + .any(|hir_id| path_to_local_id(expr, *hir_id)) + } +} + +/// Returns true if the named method is `IntoIterator::into_iter`. +pub fn is_into_iter(cx: &LateContext<'_>, callee_def_id: DefId) -> bool { + cx.tcx.lang_items().require(LangItem::IntoIterIntoIter) == Ok(callee_def_id) +} diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs new file mode 100644 index 000000000000..c48bacfce0d3 --- /dev/null +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -0,0 +1,397 @@ +use super::implicit_clone::is_clone_like; +use super::unnecessary_iter_cloned::{self, is_into_iter}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs}; +use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item}; +use rustc_errors::Applicability; +use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::mir::Mutability; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; +use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty}; +use rustc_span::{sym, Symbol}; +use std::cmp::max; + +use super::UNNECESSARY_TO_OWNED; + +pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) { + if_chain! { + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if let [receiver] = args; + then { + if is_cloned_or_copied(cx, method_name, method_def_id) { + unnecessary_iter_cloned::check(cx, expr, method_name, receiver); + } else if is_to_owned_like(cx, method_name, method_def_id) { + // At this point, we know the call is of a `to_owned`-like function. The functions + // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary + // based on its context, that is, whether it is a referent in an `AddrOf` expression, an + // argument in a `into_iter` call, or an argument in the call of some other function. + if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) { + return; + } + if check_into_iter_call_arg(cx, expr, method_name, receiver) { + return; + } + check_other_call_arg(cx, expr, method_name, receiver); + } + } + } +} + +/// Checks whether `expr` is a referent in an `AddrOf` expression and, if so, determines whether its +/// call of a `to_owned`-like function is unnecessary. +#[allow(clippy::too_many_lines)] +fn check_addr_of_expr( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + method_name: Symbol, + method_def_id: DefId, + receiver: &'tcx Expr<'tcx>, +) -> bool { + if_chain! { + if let Some(parent) = get_parent_expr(cx, expr); + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind; + let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::>(); + if let Some(target_ty) = match adjustments[..] + { + // For matching uses of `Cow::from` + [ + Adjustment { + kind: Adjust::Deref(None), + .. + }, + Adjustment { + kind: Adjust::Borrow(_), + target: target_ty, + }, + ] + // For matching uses of arrays + | [ + Adjustment { + kind: Adjust::Deref(None), + .. + }, + Adjustment { + kind: Adjust::Borrow(_), + .. + }, + Adjustment { + kind: Adjust::Pointer(_), + target: target_ty, + }, + ] + // For matching everything else + | [ + Adjustment { + kind: Adjust::Deref(None), + .. + }, + Adjustment { + kind: Adjust::Deref(Some(OverloadedDeref { .. })), + .. + }, + Adjustment { + kind: Adjust::Borrow(_), + target: target_ty, + }, + ] => Some(target_ty), + _ => None, + }; + let receiver_ty = cx.typeck_results().expr_ty(receiver); + // Only flag cases where the receiver is copyable or the method is `Cow::into_owned`. This + // restriction is to ensure there is not overlap between `redundant_clone` and this lint. + if is_copy(cx, receiver_ty) || is_cow_into_owned(cx, method_name, method_def_id); + if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); + then { + let (target_ty, n_target_refs) = peel_mid_ty_refs(target_ty); + let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty); + if receiver_ty == target_ty && n_target_refs >= n_receiver_refs { + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + parent.span, + &format!("unnecessary use of `{}`", method_name), + "use", + format!("{:&>width$}{}", "", receiver_snippet, width = n_target_refs - n_receiver_refs), + Applicability::MachineApplicable, + ); + return true; + } + if_chain! { + if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref); + if implements_trait(cx, receiver_ty, deref_trait_id, &[]); + if get_associated_type(cx, receiver_ty, deref_trait_id, "Target") == Some(target_ty); + then { + if n_receiver_refs > 0 { + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + parent.span, + &format!("unnecessary use of `{}`", method_name), + "use", + receiver_snippet, + Applicability::MachineApplicable, + ); + } else { + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + expr.span.with_lo(receiver.span.hi()), + &format!("unnecessary use of `{}`", method_name), + "remove this", + String::new(), + Applicability::MachineApplicable, + ); + } + return true; + } + } + if_chain! { + if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef); + if implements_trait(cx, receiver_ty, as_ref_trait_id, &[GenericArg::from(target_ty)]); + then { + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + parent.span, + &format!("unnecessary use of `{}`", method_name), + "use", + format!("{}.as_ref()", receiver_snippet), + Applicability::MachineApplicable, + ); + return true; + } + } + } + } + false +} + +/// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its +/// call of a `to_owned`-like function is unnecessary. +fn check_into_iter_call_arg( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + method_name: Symbol, + receiver: &'tcx Expr<'tcx>, +) -> bool { + if_chain! { + if let Some(parent) = get_parent_expr(cx, expr); + if let Some(callee_def_id) = fn_def_id(cx, parent); + if is_into_iter(cx, callee_def_id); + if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); + let parent_ty = cx.typeck_results().expr_ty(parent); + if implements_trait(cx, parent_ty, iterator_trait_id, &[]); + if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty); + if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); + then { + if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver) { + return true; + } + let cloned_or_copied = if is_copy(cx, item_ty) { + "copied" + } else { + "cloned" + }; + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + parent.span, + &format!("unnecessary use of `{}`", method_name), + "use", + format!("{}.iter().{}()", receiver_snippet, cloned_or_copied), + Applicability::MachineApplicable, + ); + return true; + } + } + false +} + +/// Checks whether `expr` is an argument in a function call and, if so, determines whether its call +/// of a `to_owned`-like function is unnecessary. +fn check_other_call_arg( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + method_name: Symbol, + receiver: &'tcx Expr<'tcx>, +) -> bool { + if_chain! { + if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr); + if let Some((callee_def_id, call_substs, call_args)) = get_callee_substs_and_args(cx, maybe_call); + let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder(); + if let Some(i) = call_args.iter().position(|arg| arg.hir_id == maybe_arg.hir_id); + if let Some(input) = fn_sig.inputs().get(i); + let (input, n_refs) = peel_mid_ty_refs(input); + if let (trait_predicates, projection_predicates) = get_input_traits_and_projections(cx, callee_def_id, input); + if let Some(sized_def_id) = cx.tcx.lang_items().sized_trait(); + if let [trait_predicate] = trait_predicates + .iter() + .filter(|trait_predicate| trait_predicate.def_id() != sized_def_id) + .collect::>()[..]; + if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref); + if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef); + let receiver_ty = cx.typeck_results().expr_ty(receiver); + // If the callee has type parameters, they could appear in `projection_predicate.ty` or the + // types of `trait_predicate.trait_ref.substs`. + if if trait_predicate.def_id() == deref_trait_id { + if let [projection_predicate] = projection_predicates[..] { + let normalized_ty = + cx.tcx.subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.ty); + implements_trait(cx, receiver_ty, deref_trait_id, &[]) + && get_associated_type(cx, receiver_ty, deref_trait_id, "Target") == Some(normalized_ty) + } else { + false + } + } else if trait_predicate.def_id() == as_ref_trait_id { + let composed_substs = compose_substs( + cx, + &trait_predicate.trait_ref.substs.iter().skip(1).collect::>()[..], + call_substs + ); + implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs) + } else { + false + }; + // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match + // `Target = T`. + if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id; + let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 }); + if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); + then { + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + maybe_arg.span, + &format!("unnecessary use of `{}`", method_name), + "use", + format!("{:&>width$}{}", "", receiver_snippet, width = n_refs), + Applicability::MachineApplicable, + ); + return true; + } + } + false +} + +/// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such +/// expression found (if any) along with the immediately prior expression. +fn skip_addr_of_ancestors( + cx: &LateContext<'tcx>, + mut expr: &'tcx Expr<'tcx>, +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + while let Some(parent) = get_parent_expr(cx, expr) { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind { + expr = parent; + } else { + return Some((parent, expr)); + } + } + None +} + +/// Checks whether an expression is a function or method call and, if so, returns its `DefId`, +/// `Substs`, and arguments. +fn get_callee_substs_and_args( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, +) -> Option<(DefId, SubstsRef<'tcx>, &'tcx [Expr<'tcx>])> { + if_chain! { + if let ExprKind::Call(callee, args) = expr.kind; + let callee_ty = cx.typeck_results().expr_ty(callee); + if let ty::FnDef(callee_def_id, _) = callee_ty.kind(); + then { + let substs = cx.typeck_results().node_substs(callee.hir_id); + return Some((*callee_def_id, substs, args)); + } + } + if_chain! { + if let ExprKind::MethodCall(_, _, args, _) = expr.kind; + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + then { + let substs = cx.typeck_results().node_substs(expr.hir_id); + return Some((method_def_id, substs, args)); + } + } + None +} + +/// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type. +fn get_input_traits_and_projections( + cx: &LateContext<'tcx>, + callee_def_id: DefId, + input: Ty<'tcx>, +) -> (Vec>, Vec>) { + let mut trait_predicates = Vec::new(); + let mut projection_predicates = Vec::new(); + for (predicate, _) in cx.tcx.predicates_of(callee_def_id).predicates.iter() { + // `substs` should have 1 + n elements. The first is the type on the left hand side of an + // `as`. The remaining n are trait parameters. + let is_input_substs = |substs: SubstsRef<'tcx>| { + if_chain! { + if let Some(arg) = substs.iter().next(); + if let GenericArgKind::Type(arg_ty) = arg.unpack(); + if arg_ty == input; + then { + true + } else { + false + } + } + }; + match predicate.kind().skip_binder() { + PredicateKind::Trait(trait_predicate) => { + if is_input_substs(trait_predicate.trait_ref.substs) { + trait_predicates.push(trait_predicate); + } + }, + PredicateKind::Projection(projection_predicate) => { + if is_input_substs(projection_predicate.projection_ty.substs) { + projection_predicates.push(projection_predicate); + } + }, + _ => {}, + } + } + (trait_predicates, projection_predicates) +} + +/// Composes two substitutions by applying the latter to the types of the former. +fn compose_substs(cx: &LateContext<'tcx>, left: &[GenericArg<'tcx>], right: SubstsRef<'tcx>) -> Vec> { + left.iter() + .map(|arg| { + if let GenericArgKind::Type(arg_ty) = arg.unpack() { + let normalized_ty = cx.tcx.subst_and_normalize_erasing_regions(right, cx.param_env, arg_ty); + GenericArg::from(normalized_ty) + } else { + *arg + } + }) + .collect() +} + +/// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`. +fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { + (method_name.as_str() == "cloned" || method_name.as_str() == "copied") + && is_diag_trait_item(cx, method_def_id, sym::Iterator) +} + +/// Returns true if the named method can be used to convert the receiver to its "owned" +/// representation. +fn is_to_owned_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { + is_clone_like(cx, &*method_name.as_str(), method_def_id) + || is_cow_into_owned(cx, method_name, method_def_id) + || is_to_string(cx, method_name, method_def_id) +} + +/// Returns true if the named method is `Cow::into_owned`. +fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { + method_name.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow) +} + +/// Returns true if the named method is `ToString::to_string`. +fn is_to_string(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { + method_name.as_str() == "to_string" && is_diag_trait_item(cx, method_def_id, sym::ToString) +} diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index 9fb8f2368994..fe35ff33d35a 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -169,7 +169,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { trait_name ), Some(last_semi), - &"probably caused by this trailing semicolon".to_string(), + "probably caused by this trailing semicolon", ); }, None => {}, diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 438c39bea0a0..6d191d4a59bd 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -58,14 +58,20 @@ pub fn contains_adt_constructor<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, adt: &'tc pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { cx.tcx .get_diagnostic_item(sym::Iterator) - .and_then(|iter_did| { - cx.tcx.associated_items(iter_did).find_by_name_and_kind( - cx.tcx, - Ident::from_str("Item"), - ty::AssocKind::Type, - iter_did, - ) - }) + .and_then(|iter_did| get_associated_type(cx, ty, iter_did, "Item")) +} + +/// Returns the associated type `name` for `ty` as an implementation of `trait_id`. +/// Do not invoke without first verifying that the type implements the trait. +pub fn get_associated_type<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + trait_id: DefId, + name: &str, +) -> Option> { + cx.tcx + .associated_items(trait_id) + .find_by_name_and_kind(cx.tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id) .map(|assoc| { let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[])); cx.tcx.normalize_erasing_regions(cx.param_env, proj) diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index dfe8a66c2a1b..2066915e1d18 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -78,7 +78,7 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { } pub struct ParamBindingIdCollector { - binding_hir_ids: Vec, + pub binding_hir_ids: Vec, } impl<'tcx> ParamBindingIdCollector { fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec { diff --git a/tests/ui/unnecessary_iter_cloned.fixed b/tests/ui/unnecessary_iter_cloned.fixed new file mode 100644 index 000000000000..e01e9f07bafd --- /dev/null +++ b/tests/ui/unnecessary_iter_cloned.fixed @@ -0,0 +1,142 @@ +// run-rustfix + +#![allow(unused_assignments)] +#![warn(clippy::unnecessary_to_owned)] + +#[allow(dead_code)] +#[derive(Clone, Copy)] +enum FileType { + Account, + PrivateKey, + Certificate, +} + +fn main() { + let path = std::path::Path::new("x"); + + let _ = check_files(&[(FileType::Account, path)]); + let _ = check_files_vec(vec![(FileType::Account, path)]); + + // negative tests + let _ = check_files_ref(&[(FileType::Account, path)]); + let _ = check_files_mut(&[(FileType::Account, path)]); + let _ = check_files_ref_mut(&[(FileType::Account, path)]); + let _ = check_files_self_and_arg(&[(FileType::Account, path)]); + let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]); +} + +// `check_files` and its variants are based on: +// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262 +fn check_files(files: &[(FileType, &std::path::Path)]) -> bool { + for (t, path) in files { + let other = match get_file_path(t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +fn check_files_vec(files: Vec<(FileType, &std::path::Path)>) -> bool { + for (t, path) in files.iter() { + let other = match get_file_path(t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +fn check_files_ref(files: &[(FileType, &std::path::Path)]) -> bool { + for (ref t, path) in files.iter().copied() { + let other = match get_file_path(t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +#[allow(unused_assignments)] +fn check_files_mut(files: &[(FileType, &std::path::Path)]) -> bool { + for (mut t, path) in files.iter().copied() { + t = FileType::PrivateKey; + let other = match get_file_path(&t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +fn check_files_ref_mut(files: &[(FileType, &std::path::Path)]) -> bool { + for (ref mut t, path) in files.iter().copied() { + *t = FileType::PrivateKey; + let other = match get_file_path(t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +fn check_files_self_and_arg(files: &[(FileType, &std::path::Path)]) -> bool { + for (t, path) in files.iter().copied() { + let other = match get_file_path(&t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.join(path).is_file() || !other.is_file() { + return false; + } + } + true +} + +#[allow(unused_assignments)] +fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool { + for (mut t, path) in files.iter().cloned() { + t = FileType::PrivateKey; + let other = match get_file_path(&t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +fn get_file_path(_file_type: &FileType) -> Result { + Ok(std::path::PathBuf::new()) +} diff --git a/tests/ui/unnecessary_iter_cloned.rs b/tests/ui/unnecessary_iter_cloned.rs new file mode 100644 index 000000000000..6ef2966c8b76 --- /dev/null +++ b/tests/ui/unnecessary_iter_cloned.rs @@ -0,0 +1,142 @@ +// run-rustfix + +#![allow(unused_assignments)] +#![warn(clippy::unnecessary_to_owned)] + +#[allow(dead_code)] +#[derive(Clone, Copy)] +enum FileType { + Account, + PrivateKey, + Certificate, +} + +fn main() { + let path = std::path::Path::new("x"); + + let _ = check_files(&[(FileType::Account, path)]); + let _ = check_files_vec(vec![(FileType::Account, path)]); + + // negative tests + let _ = check_files_ref(&[(FileType::Account, path)]); + let _ = check_files_mut(&[(FileType::Account, path)]); + let _ = check_files_ref_mut(&[(FileType::Account, path)]); + let _ = check_files_self_and_arg(&[(FileType::Account, path)]); + let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]); +} + +// `check_files` and its variants are based on: +// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262 +fn check_files(files: &[(FileType, &std::path::Path)]) -> bool { + for (t, path) in files.iter().copied() { + let other = match get_file_path(&t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +fn check_files_vec(files: Vec<(FileType, &std::path::Path)>) -> bool { + for (t, path) in files.iter().copied() { + let other = match get_file_path(&t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +fn check_files_ref(files: &[(FileType, &std::path::Path)]) -> bool { + for (ref t, path) in files.iter().copied() { + let other = match get_file_path(t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +#[allow(unused_assignments)] +fn check_files_mut(files: &[(FileType, &std::path::Path)]) -> bool { + for (mut t, path) in files.iter().copied() { + t = FileType::PrivateKey; + let other = match get_file_path(&t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +fn check_files_ref_mut(files: &[(FileType, &std::path::Path)]) -> bool { + for (ref mut t, path) in files.iter().copied() { + *t = FileType::PrivateKey; + let other = match get_file_path(t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +fn check_files_self_and_arg(files: &[(FileType, &std::path::Path)]) -> bool { + for (t, path) in files.iter().copied() { + let other = match get_file_path(&t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.join(path).is_file() || !other.is_file() { + return false; + } + } + true +} + +#[allow(unused_assignments)] +fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool { + for (mut t, path) in files.iter().cloned() { + t = FileType::PrivateKey; + let other = match get_file_path(&t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +fn get_file_path(_file_type: &FileType) -> Result { + Ok(std::path::PathBuf::new()) +} diff --git a/tests/ui/unnecessary_iter_cloned.stderr b/tests/ui/unnecessary_iter_cloned.stderr new file mode 100644 index 000000000000..e44379f8aa04 --- /dev/null +++ b/tests/ui/unnecessary_iter_cloned.stderr @@ -0,0 +1,35 @@ +error: unnecessary use of `copied` + --> $DIR/unnecessary_iter_cloned.rs:31:22 + | +LL | for (t, path) in files.iter().copied() { + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings` +help: use + | +LL | for (t, path) in files { + | ~~~~~ +help: remove this `&` + | +LL - let other = match get_file_path(&t) { +LL + let other = match get_file_path(t) { + | + +error: unnecessary use of `copied` + --> $DIR/unnecessary_iter_cloned.rs:46:22 + | +LL | for (t, path) in files.iter().copied() { + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL | for (t, path) in files.iter() { + | ~~~~~~~~~~~~ +help: remove this `&` + | +LL - let other = match get_file_path(&t) { +LL + let other = match get_file_path(t) { + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed new file mode 100644 index 000000000000..720138db1377 --- /dev/null +++ b/tests/ui/unnecessary_to_owned.fixed @@ -0,0 +1,214 @@ +// run-rustfix + +#![allow(clippy::ptr_arg)] +#![warn(clippy::unnecessary_to_owned)] + +use std::borrow::Cow; +use std::ffi::{CStr, CString, OsStr, OsString}; +use std::ops::Deref; + +#[derive(Clone)] +struct X(String); + +impl Deref for X { + type Target = [u8]; + fn deref(&self) -> &[u8] { + self.0.as_bytes() + } +} + +impl AsRef for X { + fn as_ref(&self) -> &str { + self.0.as_str() + } +} + +impl ToString for X { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl X { + fn join(&self, other: impl AsRef) -> Self { + let mut s = self.0.clone(); + s.push_str(other.as_ref()); + Self(s) + } +} + +#[allow(dead_code)] +#[derive(Clone)] +enum FileType { + Account, + PrivateKey, + Certificate, +} + +fn main() { + let c_str = CStr::from_bytes_with_nul(&[0]).unwrap(); + let os_str = OsStr::new("x"); + let path = std::path::Path::new("x"); + let s = "x"; + let array = ["x"]; + let array_ref = &["x"]; + let slice = &["x"][..]; + let x = X(String::from("x")); + let x_ref = &x; + + require_c_str(&Cow::from(c_str)); + require_c_str(c_str); + + require_os_str(os_str); + require_os_str(&Cow::from(os_str)); + require_os_str(os_str); + + require_path(path); + require_path(&Cow::from(path)); + require_path(path); + + require_str(s); + require_str(&Cow::from(s)); + require_str(s); + require_str(x_ref.as_ref()); + + require_slice(slice); + require_slice(&Cow::from(slice)); + require_slice(array.as_ref()); + require_slice(array_ref.as_ref()); + require_slice(slice); + require_slice(x_ref); + + require_x(&Cow::::Owned(x.clone())); + require_x(x_ref); + + require_deref_c_str(c_str); + require_deref_os_str(os_str); + require_deref_path(path); + require_deref_str(s); + require_deref_slice(slice); + + require_impl_deref_c_str(c_str); + require_impl_deref_os_str(os_str); + require_impl_deref_path(path); + require_impl_deref_str(s); + require_impl_deref_slice(slice); + + require_deref_str_slice(s, slice); + require_deref_slice_str(slice, s); + + require_as_ref_c_str(c_str); + require_as_ref_os_str(os_str); + require_as_ref_path(path); + require_as_ref_str(s); + require_as_ref_str(&x); + require_as_ref_slice(array); + require_as_ref_slice(array_ref); + require_as_ref_slice(slice); + + require_impl_as_ref_c_str(c_str); + require_impl_as_ref_os_str(os_str); + require_impl_as_ref_path(path); + require_impl_as_ref_str(s); + require_impl_as_ref_str(&x); + require_impl_as_ref_slice(array); + require_impl_as_ref_slice(array_ref); + require_impl_as_ref_slice(slice); + + require_as_ref_str_slice(s, array); + require_as_ref_str_slice(s, array_ref); + require_as_ref_str_slice(s, slice); + require_as_ref_slice_str(array, s); + require_as_ref_slice_str(array_ref, s); + require_as_ref_slice_str(slice, s); + + let _ = x.join(x_ref); + + let _ = slice.iter().copied(); + let _ = slice.iter().copied(); + let _ = [std::path::PathBuf::new()][..].iter().cloned(); + let _ = [std::path::PathBuf::new()][..].iter().cloned(); + + let _ = slice.iter().copied(); + let _ = slice.iter().copied(); + let _ = [std::path::PathBuf::new()][..].iter().cloned(); + let _ = [std::path::PathBuf::new()][..].iter().cloned(); + + let _ = check_files(&[FileType::Account]); + + // negative tests + require_string(&s.to_string()); + require_string(&Cow::from(s).into_owned()); + require_string(&s.to_owned()); + require_string(&x_ref.to_string()); + + // `X` isn't copy. + require_slice(&x.to_owned()); + require_deref_slice(x.to_owned()); + + // The following should be flagged by `redundant_clone`, but not by this lint. + require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap()); + require_os_str(&OsString::from("x")); + require_path(&std::path::PathBuf::from("x")); + require_str(&String::from("x")); +} + +fn require_c_str(_: &CStr) {} +fn require_os_str(_: &OsStr) {} +fn require_path(_: &std::path::Path) {} +fn require_str(_: &str) {} +fn require_slice(_: &[T]) {} +fn require_x(_: &X) {} + +fn require_deref_c_str>(_: T) {} +fn require_deref_os_str>(_: T) {} +fn require_deref_path>(_: T) {} +fn require_deref_str>(_: T) {} +fn require_deref_slice>(_: U) {} + +fn require_impl_deref_c_str(_: impl Deref) {} +fn require_impl_deref_os_str(_: impl Deref) {} +fn require_impl_deref_path(_: impl Deref) {} +fn require_impl_deref_str(_: impl Deref) {} +fn require_impl_deref_slice(_: impl Deref) {} + +fn require_deref_str_slice, U, V: Deref>(_: T, _: V) {} +fn require_deref_slice_str, V: Deref>(_: U, _: V) {} + +fn require_as_ref_c_str>(_: T) {} +fn require_as_ref_os_str>(_: T) {} +fn require_as_ref_path>(_: T) {} +fn require_as_ref_str>(_: T) {} +fn require_as_ref_slice>(_: U) {} + +fn require_impl_as_ref_c_str(_: impl AsRef) {} +fn require_impl_as_ref_os_str(_: impl AsRef) {} +fn require_impl_as_ref_path(_: impl AsRef) {} +fn require_impl_as_ref_str(_: impl AsRef) {} +fn require_impl_as_ref_slice(_: impl AsRef<[T]>) {} + +fn require_as_ref_str_slice, U, V: AsRef<[U]>>(_: T, _: V) {} +fn require_as_ref_slice_str, V: AsRef>(_: U, _: V) {} + +// `check_files` is based on: +// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262 +fn check_files(file_types: &[FileType]) -> bool { + for t in file_types { + let path = match get_file_path(t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() { + return false; + } + } + true +} + +fn get_file_path(_file_type: &FileType) -> Result { + Ok(std::path::PathBuf::new()) +} + +fn require_string(_: &String) {} diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs new file mode 100644 index 000000000000..60b2e718f5d4 --- /dev/null +++ b/tests/ui/unnecessary_to_owned.rs @@ -0,0 +1,214 @@ +// run-rustfix + +#![allow(clippy::ptr_arg)] +#![warn(clippy::unnecessary_to_owned)] + +use std::borrow::Cow; +use std::ffi::{CStr, CString, OsStr, OsString}; +use std::ops::Deref; + +#[derive(Clone)] +struct X(String); + +impl Deref for X { + type Target = [u8]; + fn deref(&self) -> &[u8] { + self.0.as_bytes() + } +} + +impl AsRef for X { + fn as_ref(&self) -> &str { + self.0.as_str() + } +} + +impl ToString for X { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl X { + fn join(&self, other: impl AsRef) -> Self { + let mut s = self.0.clone(); + s.push_str(other.as_ref()); + Self(s) + } +} + +#[allow(dead_code)] +#[derive(Clone)] +enum FileType { + Account, + PrivateKey, + Certificate, +} + +fn main() { + let c_str = CStr::from_bytes_with_nul(&[0]).unwrap(); + let os_str = OsStr::new("x"); + let path = std::path::Path::new("x"); + let s = "x"; + let array = ["x"]; + let array_ref = &["x"]; + let slice = &["x"][..]; + let x = X(String::from("x")); + let x_ref = &x; + + require_c_str(&Cow::from(c_str).into_owned()); + require_c_str(&c_str.to_owned()); + + require_os_str(&os_str.to_os_string()); + require_os_str(&Cow::from(os_str).into_owned()); + require_os_str(&os_str.to_owned()); + + require_path(&path.to_path_buf()); + require_path(&Cow::from(path).into_owned()); + require_path(&path.to_owned()); + + require_str(&s.to_string()); + require_str(&Cow::from(s).into_owned()); + require_str(&s.to_owned()); + require_str(&x_ref.to_string()); + + require_slice(&slice.to_vec()); + require_slice(&Cow::from(slice).into_owned()); + require_slice(&array.to_owned()); + require_slice(&array_ref.to_owned()); + require_slice(&slice.to_owned()); + require_slice(&x_ref.to_owned()); + + require_x(&Cow::::Owned(x.clone()).into_owned()); + require_x(&x_ref.to_owned()); + + require_deref_c_str(c_str.to_owned()); + require_deref_os_str(os_str.to_owned()); + require_deref_path(path.to_owned()); + require_deref_str(s.to_owned()); + require_deref_slice(slice.to_owned()); + + require_impl_deref_c_str(c_str.to_owned()); + require_impl_deref_os_str(os_str.to_owned()); + require_impl_deref_path(path.to_owned()); + require_impl_deref_str(s.to_owned()); + require_impl_deref_slice(slice.to_owned()); + + require_deref_str_slice(s.to_owned(), slice.to_owned()); + require_deref_slice_str(slice.to_owned(), s.to_owned()); + + require_as_ref_c_str(c_str.to_owned()); + require_as_ref_os_str(os_str.to_owned()); + require_as_ref_path(path.to_owned()); + require_as_ref_str(s.to_owned()); + require_as_ref_str(x.to_owned()); + require_as_ref_slice(array.to_owned()); + require_as_ref_slice(array_ref.to_owned()); + require_as_ref_slice(slice.to_owned()); + + require_impl_as_ref_c_str(c_str.to_owned()); + require_impl_as_ref_os_str(os_str.to_owned()); + require_impl_as_ref_path(path.to_owned()); + require_impl_as_ref_str(s.to_owned()); + require_impl_as_ref_str(x.to_owned()); + require_impl_as_ref_slice(array.to_owned()); + require_impl_as_ref_slice(array_ref.to_owned()); + require_impl_as_ref_slice(slice.to_owned()); + + require_as_ref_str_slice(s.to_owned(), array.to_owned()); + require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); + require_as_ref_str_slice(s.to_owned(), slice.to_owned()); + require_as_ref_slice_str(array.to_owned(), s.to_owned()); + require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); + require_as_ref_slice_str(slice.to_owned(), s.to_owned()); + + let _ = x.join(&x_ref.to_string()); + + let _ = slice.to_vec().into_iter(); + let _ = slice.to_owned().into_iter(); + let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); + let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); + + let _ = IntoIterator::into_iter(slice.to_vec()); + let _ = IntoIterator::into_iter(slice.to_owned()); + let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); + let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); + + let _ = check_files(&[FileType::Account]); + + // negative tests + require_string(&s.to_string()); + require_string(&Cow::from(s).into_owned()); + require_string(&s.to_owned()); + require_string(&x_ref.to_string()); + + // `X` isn't copy. + require_slice(&x.to_owned()); + require_deref_slice(x.to_owned()); + + // The following should be flagged by `redundant_clone`, but not by this lint. + require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); + require_os_str(&OsString::from("x").to_os_string()); + require_path(&std::path::PathBuf::from("x").to_path_buf()); + require_str(&String::from("x").to_string()); +} + +fn require_c_str(_: &CStr) {} +fn require_os_str(_: &OsStr) {} +fn require_path(_: &std::path::Path) {} +fn require_str(_: &str) {} +fn require_slice(_: &[T]) {} +fn require_x(_: &X) {} + +fn require_deref_c_str>(_: T) {} +fn require_deref_os_str>(_: T) {} +fn require_deref_path>(_: T) {} +fn require_deref_str>(_: T) {} +fn require_deref_slice>(_: U) {} + +fn require_impl_deref_c_str(_: impl Deref) {} +fn require_impl_deref_os_str(_: impl Deref) {} +fn require_impl_deref_path(_: impl Deref) {} +fn require_impl_deref_str(_: impl Deref) {} +fn require_impl_deref_slice(_: impl Deref) {} + +fn require_deref_str_slice, U, V: Deref>(_: T, _: V) {} +fn require_deref_slice_str, V: Deref>(_: U, _: V) {} + +fn require_as_ref_c_str>(_: T) {} +fn require_as_ref_os_str>(_: T) {} +fn require_as_ref_path>(_: T) {} +fn require_as_ref_str>(_: T) {} +fn require_as_ref_slice>(_: U) {} + +fn require_impl_as_ref_c_str(_: impl AsRef) {} +fn require_impl_as_ref_os_str(_: impl AsRef) {} +fn require_impl_as_ref_path(_: impl AsRef) {} +fn require_impl_as_ref_str(_: impl AsRef) {} +fn require_impl_as_ref_slice(_: impl AsRef<[T]>) {} + +fn require_as_ref_str_slice, U, V: AsRef<[U]>>(_: T, _: V) {} +fn require_as_ref_slice_str, V: AsRef>(_: U, _: V) {} + +// `check_files` is based on: +// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262 +fn check_files(file_types: &[FileType]) -> bool { + for t in file_types.to_vec() { + let path = match get_file_path(&t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() { + return false; + } + } + true +} + +fn get_file_path(_file_type: &FileType) -> Result { + Ok(std::path::PathBuf::new()) +} + +fn require_string(_: &String) {} diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr new file mode 100644 index 000000000000..1dfc65e22e2b --- /dev/null +++ b/tests/ui/unnecessary_to_owned.stderr @@ -0,0 +1,495 @@ +error: redundant clone + --> $DIR/unnecessary_to_owned.rs:150:64 + | +LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); + | ^^^^^^^^^^^ help: remove this + | + = note: `-D clippy::redundant-clone` implied by `-D warnings` +note: this value is dropped without further use + --> $DIR/unnecessary_to_owned.rs:150:20 + | +LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: redundant clone + --> $DIR/unnecessary_to_owned.rs:151:40 + | +LL | require_os_str(&OsString::from("x").to_os_string()); + | ^^^^^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/unnecessary_to_owned.rs:151:21 + | +LL | require_os_str(&OsString::from("x").to_os_string()); + | ^^^^^^^^^^^^^^^^^^^ + +error: redundant clone + --> $DIR/unnecessary_to_owned.rs:152:48 + | +LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); + | ^^^^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/unnecessary_to_owned.rs:152:19 + | +LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: redundant clone + --> $DIR/unnecessary_to_owned.rs:153:35 + | +LL | require_str(&String::from("x").to_string()); + | ^^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/unnecessary_to_owned.rs:153:18 + | +LL | require_str(&String::from("x").to_string()); + | ^^^^^^^^^^^^^^^^^ + +error: unnecessary use of `into_owned` + --> $DIR/unnecessary_to_owned.rs:59:36 + | +LL | require_c_str(&Cow::from(c_str).into_owned()); + | ^^^^^^^^^^^^^ help: remove this + | + = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:60:19 + | +LL | require_c_str(&c_str.to_owned()); + | ^^^^^^^^^^^^^^^^^ help: use: `c_str` + +error: unnecessary use of `to_os_string` + --> $DIR/unnecessary_to_owned.rs:62:20 + | +LL | require_os_str(&os_str.to_os_string()); + | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` + +error: unnecessary use of `into_owned` + --> $DIR/unnecessary_to_owned.rs:63:38 + | +LL | require_os_str(&Cow::from(os_str).into_owned()); + | ^^^^^^^^^^^^^ help: remove this + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:64:20 + | +LL | require_os_str(&os_str.to_owned()); + | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` + +error: unnecessary use of `to_path_buf` + --> $DIR/unnecessary_to_owned.rs:66:18 + | +LL | require_path(&path.to_path_buf()); + | ^^^^^^^^^^^^^^^^^^^ help: use: `path` + +error: unnecessary use of `into_owned` + --> $DIR/unnecessary_to_owned.rs:67:34 + | +LL | require_path(&Cow::from(path).into_owned()); + | ^^^^^^^^^^^^^ help: remove this + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:68:18 + | +LL | require_path(&path.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `path` + +error: unnecessary use of `to_string` + --> $DIR/unnecessary_to_owned.rs:70:17 + | +LL | require_str(&s.to_string()); + | ^^^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `into_owned` + --> $DIR/unnecessary_to_owned.rs:71:30 + | +LL | require_str(&Cow::from(s).into_owned()); + | ^^^^^^^^^^^^^ help: remove this + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:72:17 + | +LL | require_str(&s.to_owned()); + | ^^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `to_string` + --> $DIR/unnecessary_to_owned.rs:73:17 + | +LL | require_str(&x_ref.to_string()); + | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` + +error: unnecessary use of `to_vec` + --> $DIR/unnecessary_to_owned.rs:75:19 + | +LL | require_slice(&slice.to_vec()); + | ^^^^^^^^^^^^^^^ help: use: `slice` + +error: unnecessary use of `into_owned` + --> $DIR/unnecessary_to_owned.rs:76:36 + | +LL | require_slice(&Cow::from(slice).into_owned()); + | ^^^^^^^^^^^^^ help: remove this + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:77:19 + | +LL | require_slice(&array.to_owned()); + | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:78:19 + | +LL | require_slice(&array_ref.to_owned()); + | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:79:19 + | +LL | require_slice(&slice.to_owned()); + | ^^^^^^^^^^^^^^^^^ help: use: `slice` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:80:19 + | +LL | require_slice(&x_ref.to_owned()); + | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` + +error: unnecessary use of `into_owned` + --> $DIR/unnecessary_to_owned.rs:82:42 + | +LL | require_x(&Cow::::Owned(x.clone()).into_owned()); + | ^^^^^^^^^^^^^ help: remove this + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:83:15 + | +LL | require_x(&x_ref.to_owned()); + | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:85:25 + | +LL | require_deref_c_str(c_str.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `c_str` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:86:26 + | +LL | require_deref_os_str(os_str.to_owned()); + | ^^^^^^^^^^^^^^^^^ help: use: `os_str` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:87:24 + | +LL | require_deref_path(path.to_owned()); + | ^^^^^^^^^^^^^^^ help: use: `path` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:88:23 + | +LL | require_deref_str(s.to_owned()); + | ^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:89:25 + | +LL | require_deref_slice(slice.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `slice` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:91:30 + | +LL | require_impl_deref_c_str(c_str.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `c_str` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:92:31 + | +LL | require_impl_deref_os_str(os_str.to_owned()); + | ^^^^^^^^^^^^^^^^^ help: use: `os_str` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:93:29 + | +LL | require_impl_deref_path(path.to_owned()); + | ^^^^^^^^^^^^^^^ help: use: `path` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:94:28 + | +LL | require_impl_deref_str(s.to_owned()); + | ^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:95:30 + | +LL | require_impl_deref_slice(slice.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `slice` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:97:29 + | +LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); + | ^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:97:43 + | +LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `slice` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:98:29 + | +LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `slice` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:98:47 + | +LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); + | ^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:100:26 + | +LL | require_as_ref_c_str(c_str.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `c_str` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:101:27 + | +LL | require_as_ref_os_str(os_str.to_owned()); + | ^^^^^^^^^^^^^^^^^ help: use: `os_str` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:102:25 + | +LL | require_as_ref_path(path.to_owned()); + | ^^^^^^^^^^^^^^^ help: use: `path` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:103:24 + | +LL | require_as_ref_str(s.to_owned()); + | ^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:104:24 + | +LL | require_as_ref_str(x.to_owned()); + | ^^^^^^^^^^^^ help: use: `&x` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:105:26 + | +LL | require_as_ref_slice(array.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `array` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:106:26 + | +LL | require_as_ref_slice(array_ref.to_owned()); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:107:26 + | +LL | require_as_ref_slice(slice.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `slice` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:109:31 + | +LL | require_impl_as_ref_c_str(c_str.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `c_str` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:110:32 + | +LL | require_impl_as_ref_os_str(os_str.to_owned()); + | ^^^^^^^^^^^^^^^^^ help: use: `os_str` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:111:30 + | +LL | require_impl_as_ref_path(path.to_owned()); + | ^^^^^^^^^^^^^^^ help: use: `path` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:112:29 + | +LL | require_impl_as_ref_str(s.to_owned()); + | ^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:113:29 + | +LL | require_impl_as_ref_str(x.to_owned()); + | ^^^^^^^^^^^^ help: use: `&x` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:114:31 + | +LL | require_impl_as_ref_slice(array.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `array` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:115:31 + | +LL | require_impl_as_ref_slice(array_ref.to_owned()); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:116:31 + | +LL | require_impl_as_ref_slice(slice.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `slice` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:118:30 + | +LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); + | ^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:118:44 + | +LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `array` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:119:30 + | +LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); + | ^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:119:44 + | +LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:120:30 + | +LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); + | ^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:120:44 + | +LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `slice` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:121:30 + | +LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `array` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:121:48 + | +LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); + | ^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:122:30 + | +LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:122:52 + | +LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); + | ^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:123:30 + | +LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `slice` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:123:48 + | +LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); + | ^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `to_string` + --> $DIR/unnecessary_to_owned.rs:125:20 + | +LL | let _ = x.join(&x_ref.to_string()); + | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` + +error: unnecessary use of `to_vec` + --> $DIR/unnecessary_to_owned.rs:127:13 + | +LL | let _ = slice.to_vec().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:128:13 + | +LL | let _ = slice.to_owned().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` + +error: unnecessary use of `to_vec` + --> $DIR/unnecessary_to_owned.rs:129:13 + | +LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:130:13 + | +LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` + +error: unnecessary use of `to_vec` + --> $DIR/unnecessary_to_owned.rs:132:13 + | +LL | let _ = IntoIterator::into_iter(slice.to_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:133:13 + | +LL | let _ = IntoIterator::into_iter(slice.to_owned()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` + +error: unnecessary use of `to_vec` + --> $DIR/unnecessary_to_owned.rs:134:13 + | +LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:135:13 + | +LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` + +error: unnecessary use of `to_vec` + --> $DIR/unnecessary_to_owned.rs:196:14 + | +LL | for t in file_types.to_vec() { + | ^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL | for t in file_types { + | ~~~~~~~~~~ +help: remove this `&` + | +LL - let path = match get_file_path(&t) { +LL + let path = match get_file_path(t) { + | + +error: aborting due to 76 previous errors +