From e7186d04d4db1d3f2aca00e3ea2d8b6e2af8b958 Mon Sep 17 00:00:00 2001 From: GnomedDev Date: Wed, 23 Oct 2024 14:02:35 +0100 Subject: [PATCH] Implement a lint for `.clone().into_iter()` --- CHANGELOG.md | 1 + README.md | 2 +- book/src/README.md | 2 +- clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/mod.rs | 36 +++++++++ .../methods/unnecessary_collection_clone.rs | 59 ++++++++++++++ .../src/methods/unnecessary_iter_cloned.rs | 26 ++----- .../src/unnecessary_struct_initialization.rs | 16 +--- clippy_utils/src/lib.rs | 13 ++++ clippy_utils/src/ty.rs | 25 +++--- tests/ui/filter_map_bool_then.fixed | 1 + tests/ui/filter_map_bool_then.rs | 1 + tests/ui/filter_map_bool_then.stderr | 20 ++--- tests/ui/iter_filter_is_ok.fixed | 3 +- tests/ui/iter_filter_is_ok.rs | 3 +- tests/ui/iter_filter_is_ok.stderr | 24 +++--- tests/ui/iter_filter_is_some.fixed | 3 +- tests/ui/iter_filter_is_some.rs | 3 +- tests/ui/iter_filter_is_some.stderr | 20 ++--- tests/ui/iter_kv_map.fixed | 8 +- tests/ui/iter_kv_map.rs | 8 +- tests/ui/iter_kv_map.stderr | 76 +++++++++---------- tests/ui/unnecessary_collection_clone.fixed | 55 ++++++++++++++ tests/ui/unnecessary_collection_clone.rs | 55 ++++++++++++++ tests/ui/unnecessary_collection_clone.stderr | 29 +++++++ 25 files changed, 372 insertions(+), 118 deletions(-) create mode 100644 clippy_lints/src/methods/unnecessary_collection_clone.rs create mode 100644 tests/ui/unnecessary_collection_clone.fixed create mode 100644 tests/ui/unnecessary_collection_clone.rs create mode 100644 tests/ui/unnecessary_collection_clone.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bdbc91db939..a809d3b0a090 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6056,6 +6056,7 @@ Released 2018-09-13 [`unnecessary_box_returns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_clippy_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_clippy_cfg +[`unnecessary_collection_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_collection_clone [`unnecessary_fallible_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fallible_conversions [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map diff --git a/README.md b/README.md index ec76a6dfb08e..1690e2beb16f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/book/src/README.md b/book/src/README.md index 7bdfb97c3acf..23527ba896af 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index d8918d37afa9..e25c8e3ca0c8 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -468,6 +468,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::TYPE_ID_ON_BOX_INFO, crate::methods::UNINIT_ASSUMED_INIT_INFO, crate::methods::UNIT_HASH_INFO, + crate::methods::UNNECESSARY_COLLECTION_CLONE_INFO, crate::methods::UNNECESSARY_FALLIBLE_CONVERSIONS_INFO, crate::methods::UNNECESSARY_FILTER_MAP_INFO, crate::methods::UNNECESSARY_FIND_MAP_INFO, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 722290fb68e5..26416ce074c5 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -109,6 +109,7 @@ mod suspicious_to_owned; mod type_id_on_box; mod uninit_assumed_init; mod unit_hash; +mod unnecessary_collection_clone; mod unnecessary_fallible_conversions; mod unnecessary_filter_map; mod unnecessary_first_then_check; @@ -4166,6 +4167,36 @@ declare_clippy_lint! { "calling `.first().is_some()` or `.first().is_none()` instead of `.is_empty()`" } +declare_clippy_lint! { + /// ### What it does + /// + /// Detects when an entire collection is being cloned eagerly, instead of each item lazily. + /// + /// ### Why is this bad? + /// + /// Cloning a collection requires allocating space for all elements and cloning each element into this new space, + /// whereas using `Iterator::cloned` does not allocate any more space and only requires cloning each element as they are consumed. + /// + /// ### Example + /// ```no_run + /// fn process_string(val: String) -> String { val } + /// fn process_strings(strings: &Vec) -> Vec { + /// strings.clone().into_iter().filter(|s| s.len() < 10).map(process_string).collect() + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn process_string(val: String) -> String { val } + /// fn process_strings(strings: &Vec) -> Vec { + /// strings.iter().cloned().filter(|s| s.len() < 10).map(process_string).collect() + /// } + /// ``` + #[clippy::version = "1.84.0"] + pub UNNECESSARY_COLLECTION_CLONE, + perf, + "calling `.clone().into_iter()` instead of `.iter().cloned()`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4327,6 +4358,7 @@ impl_lint_pass!(Methods => [ NEEDLESS_CHARACTER_ITERATION, MANUAL_INSPECT, UNNECESSARY_MIN_OR_MAX, + UNNECESSARY_COLLECTION_CLONE, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4774,6 +4806,10 @@ impl Methods { ("is_none", []) => check_is_some_is_none(cx, expr, recv, call_span, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, call_span, true), ("iter" | "iter_mut" | "into_iter", []) => { + if name == "into_iter" { + unnecessary_collection_clone::check(cx, expr, recv); + } + iter_on_single_or_empty_collections::check(cx, expr, name, recv); }, ("join", [join_arg]) => { diff --git a/clippy_lints/src/methods/unnecessary_collection_clone.rs b/clippy_lints/src/methods/unnecessary_collection_clone.rs new file mode 100644 index 000000000000..c0c1ad479800 --- /dev/null +++ b/clippy_lints/src/methods/unnecessary_collection_clone.rs @@ -0,0 +1,59 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_path_mutable; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::{deref_chain, get_inherent_method, implements_trait, make_normalized_projection}; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; + +use super::{UNNECESSARY_COLLECTION_CLONE, method_call}; + +// FIXME: This does not check if the iter method is actually compatible with the replacement, but +// you have to be actively evil to have an `IntoIterator` impl that returns one type and an `iter` +// method that returns something other than references of that type.... and it is a massive +// complicated hassle to check this +fn has_iter_method<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + deref_chain(cx, ty).any(|ty| match ty.kind() { + ty::Adt(adt_def, _) => get_inherent_method(cx, adt_def.did(), sym::iter).is_some(), + ty::Slice(_) => true, + _ => false, + }) +} + +/// Check for `x.clone().into_iter()` to suggest `x.iter().cloned()`. +// ^^^^^^^^^ is recv +// ^^^^^^^^^^^^^^^^^^^^^ is expr +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { + let typeck_results = cx.typeck_results(); + let diagnostic_items = cx.tcx.all_diagnostic_items(()); + + // If the call before `into_iter` is `.clone()` + if let Some(("clone", collection_expr, [], _, _)) = method_call(recv) + // and the binding being cloned is not mutable + && let Some(false) = is_path_mutable(cx, collection_expr) + // and the result of `into_iter` is an Iterator + && let Some(&iterator_def_id) = diagnostic_items.name_to_id.get(&sym::Iterator) + && let expr_ty = typeck_results.expr_ty(expr) + && implements_trait(cx, expr_ty, iterator_def_id, &[]) + // with an item that implements clone + && let Some(&clone_def_id) = diagnostic_items.name_to_id.get(&sym::Clone) + && let Some(item_ty) = make_normalized_projection(cx.tcx, cx.param_env, iterator_def_id, sym::Item, [expr_ty]) + && implements_trait(cx, item_ty, clone_def_id, &[]) + // and the type has an `iter` method + && has_iter_method(cx, typeck_results.expr_ty(collection_expr)) + { + let mut applicability = Applicability::MachineApplicable; + let collection_expr_snippet = snippet_with_applicability(cx, collection_expr.span, "...", &mut applicability); + span_lint_and_sugg( + cx, + UNNECESSARY_COLLECTION_CLONE, + expr.span, + "using clone on collection to own iterated items", + "replace with", + format!("{collection_expr_snippet}.iter().cloned()"), + applicability, + ); + } +} diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 029704882dde..991bf06bf6d2 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -4,11 +4,11 @@ use clippy_utils::higher::ForLoop; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local}; +use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, is_path_mutable}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_span::{Symbol, sym}; @@ -42,22 +42,7 @@ pub fn check_for_loop_iter( && !clone_or_copy_needed && let Some(receiver_snippet) = receiver.span.get_source_text(cx) { - // Issue 12098 - // https://github.com/rust-lang/rust-clippy/issues/12098 - // if the assignee have `mut borrow` conflict with the iteratee - // the lint should not execute, former didn't consider the mut case - // check whether `expr` is mutable - fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(hir_id) = path_to_local(expr) - && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) - { - matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) - } else { - true - } - } - fn is_caller_or_fields_change(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Expr<'_>) -> bool { let mut change = false; if let ExprKind::Block(block, ..) = body.kind { @@ -82,7 +67,12 @@ pub fn check_for_loop_iter( while let ExprKind::MethodCall(_, caller, _, _) = child.kind { child = caller; } - if is_mutable(cx, child) && is_caller_or_fields_change(cx, body, child) { + + // Issue 12098 + // https://github.com/rust-lang/rust-clippy/issues/12098 + // if the assignee have `mut borrow` conflict with the iteratee + // the lint should not execute, former didn't consider the mut case + if is_path_mutable(cx, child).unwrap_or(true) && is_caller_or_fields_change(cx, body, child) { // skip lint return true; } diff --git a/clippy_lints/src/unnecessary_struct_initialization.rs b/clippy_lints/src/unnecessary_struct_initialization.rs index afdd3505cdd1..7ca0a2ca9e15 100644 --- a/clippy_lints/src/unnecessary_struct_initialization.rs +++ b/clippy_lints/src/unnecessary_struct_initialization.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_copy; -use clippy_utils::{get_parent_expr, path_to_local}; -use rustc_hir::{BindingMode, Expr, ExprField, ExprKind, Node, PatKind, Path, QPath, UnOp}; +use clippy_utils::{get_parent_expr, is_path_mutable, path_to_local}; +use rustc_hir::{Expr, ExprField, ExprKind, Path, QPath, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -155,16 +155,6 @@ fn same_path_in_all_fields<'tcx>( } } -fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(hir_id) = path_to_local(expr) - && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) - { - matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) - } else { - true - } -} - fn check_references(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { if let Some(parent) = get_parent_expr(cx, expr_a) && let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) @@ -176,7 +166,7 @@ fn check_references(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) return false; } - if parent_ty.is_mutable_ptr() && !is_mutable(cx, expr_b) { + if parent_ty.is_mutable_ptr() && !is_path_mutable(cx, expr_b).unwrap_or(true) { // The original can be used in a mutable reference context only if it is mutable. return false; } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 65e6f8847bca..5d130f2c3233 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -412,6 +412,19 @@ pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) - } } +/// Checks if `expr` is a path to a mutable binding. +pub fn is_path_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if let Some(hir_id) = path_to_local(expr) + && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) + { + Some(matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))) + } else if let ExprKind::Field(recv, _) = expr.kind { + is_path_mutable(cx, recv) + } else { + None + } +} + pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { match *path { QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"), diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index b7a3569ccf0f..99bdccc8b677 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -1323,22 +1323,29 @@ pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl /// Checks if a Ty<'_> has some inherent method Symbol. /// -/// This does not look for impls in the type's `Deref::Target` type. -/// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`. +/// This is a helper for [`get_inherent_method`]. pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<&'a AssocItem> { if let Some(ty_did) = ty.ty_adt_def().map(AdtDef::did) { - cx.tcx.inherent_impls(ty_did).iter().find_map(|&did| { - cx.tcx - .associated_items(did) - .filter_by_name_unhygienic(method_name) - .next() - .filter(|item| item.kind == AssocKind::Fn) - }) + get_inherent_method(cx, ty_did, method_name) } else { None } } +/// Checks if the [`DefId`] of a Ty has some inherent method Symbol. +/// +/// This does not look for impls in the type's `Deref::Target` type. +/// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`. +pub fn get_inherent_method<'a>(cx: &'a LateContext<'_>, ty_did: DefId, method_name: Symbol) -> Option<&'a AssocItem> { + cx.tcx.inherent_impls(ty_did).iter().find_map(|&did| { + cx.tcx + .associated_items(did) + .filter_by_name_unhygienic(method_name) + .next() + .filter(|item| item.kind == AssocKind::Fn) + }) +} + /// Get's the type of a field by name. pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> Option> { match *ty.kind() { diff --git a/tests/ui/filter_map_bool_then.fixed b/tests/ui/filter_map_bool_then.fixed index 6a1b81fdbcbe..b61238975218 100644 --- a/tests/ui/filter_map_bool_then.fixed +++ b/tests/ui/filter_map_bool_then.fixed @@ -4,6 +4,7 @@ clippy::map_identity, clippy::unnecessary_lazy_evaluations, clippy::unnecessary_filter_map, + clippy::unnecessary_collection_clone, unused )] #![warn(clippy::filter_map_bool_then)] diff --git a/tests/ui/filter_map_bool_then.rs b/tests/ui/filter_map_bool_then.rs index a41e88f8805d..1902b14f034c 100644 --- a/tests/ui/filter_map_bool_then.rs +++ b/tests/ui/filter_map_bool_then.rs @@ -4,6 +4,7 @@ clippy::map_identity, clippy::unnecessary_lazy_evaluations, clippy::unnecessary_filter_map, + clippy::unnecessary_collection_clone, unused )] #![warn(clippy::filter_map_bool_then)] diff --git a/tests/ui/filter_map_bool_then.stderr b/tests/ui/filter_map_bool_then.stderr index 088b9ba12867..903b6c89c108 100644 --- a/tests/ui/filter_map_bool_then.stderr +++ b/tests/ui/filter_map_bool_then.stderr @@ -1,5 +1,5 @@ error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then.rs:19:22 + --> tests/ui/filter_map_bool_then.rs:20:22 | LL | v.clone().iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i % 2 == 0)).map(|i| i + 1)` @@ -8,55 +8,55 @@ LL | v.clone().iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); = help: to override `-D warnings` add `#[allow(clippy::filter_map_bool_then)]` error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then.rs:20:27 + --> tests/ui/filter_map_bool_then.rs:21:27 | LL | v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i % 2 == 0)).map(|i| i + 1)` error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then.rs:23:10 + --> tests/ui/filter_map_bool_then.rs:24:10 | LL | .filter_map(|i| -> Option<_> { (i % 2 == 0).then(|| i + 1) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i % 2 == 0)).map(|i| i + 1)` error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then.rs:27:10 + --> tests/ui/filter_map_bool_then.rs:28:10 | LL | .filter_map(|i| (i % 2 == 0).then(|| i + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i % 2 == 0)).map(|i| i + 1)` error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then.rs:31:10 + --> tests/ui/filter_map_bool_then.rs:32:10 | LL | .filter_map(|i| (i.clone() % 2 == 0).then(|| i + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i.clone() % 2 == 0)).map(|i| i + 1)` error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then.rs:37:22 + --> tests/ui/filter_map_bool_then.rs:38:22 | LL | v.clone().iter().filter_map(|i| (i == &NonCopy).then(|| i)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i == &NonCopy)).map(|i| i)` error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then.rs:61:50 + --> tests/ui/filter_map_bool_then.rs:62:50 | LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| *b).map(|(i, b)| i)` error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then.rs:65:50 + --> tests/ui/filter_map_bool_then.rs:66:50 | LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| ***b).map(|(i, b)| i)` error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then.rs:69:50 + --> tests/ui/filter_map_bool_then.rs:70:50 | LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| **b).map(|(i, b)| i)` error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then.rs:80:50 + --> tests/ui/filter_map_bool_then.rs:81:50 | LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| ****b).map(|(i, b)| i)` diff --git a/tests/ui/iter_filter_is_ok.fixed b/tests/ui/iter_filter_is_ok.fixed index 80db8b29c18e..f5e0c26f3aba 100644 --- a/tests/ui/iter_filter_is_ok.fixed +++ b/tests/ui/iter_filter_is_ok.fixed @@ -3,7 +3,8 @@ clippy::map_identity, clippy::result_filter_map, clippy::needless_borrow, - clippy::redundant_closure + clippy::redundant_closure, + clippy::unnecessary_collection_clone )] fn main() { diff --git a/tests/ui/iter_filter_is_ok.rs b/tests/ui/iter_filter_is_ok.rs index 89b083b84f32..ee66ac5850d0 100644 --- a/tests/ui/iter_filter_is_ok.rs +++ b/tests/ui/iter_filter_is_ok.rs @@ -3,7 +3,8 @@ clippy::map_identity, clippy::result_filter_map, clippy::needless_borrow, - clippy::redundant_closure + clippy::redundant_closure, + clippy::unnecessary_collection_clone )] fn main() { diff --git a/tests/ui/iter_filter_is_ok.stderr b/tests/ui/iter_filter_is_ok.stderr index 0aff60224e0e..453eba928dbc 100644 --- a/tests/ui/iter_filter_is_ok.stderr +++ b/tests/ui/iter_filter_is_ok.stderr @@ -1,5 +1,5 @@ error: `filter` for `is_ok` on iterator over `Result`s - --> tests/ui/iter_filter_is_ok.rs:11:56 + --> tests/ui/iter_filter_is_ok.rs:12:56 | LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(Result::is_ok); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` @@ -8,67 +8,67 @@ LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(Result::is_ok = help: to override `-D warnings` add `#[allow(clippy::iter_filter_is_ok)]` error: `filter` for `is_ok` on iterator over `Result`s - --> tests/ui/iter_filter_is_ok.rs:13:56 + --> tests/ui/iter_filter_is_ok.rs:14:56 | LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|a| a.is_ok()); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_ok` on iterator over `Result`s - --> tests/ui/iter_filter_is_ok.rs:16:49 + --> tests/ui/iter_filter_is_ok.rs:17:49 | LL | let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| { o.is_ok() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_ok` on iterator over `Result`s - --> tests/ui/iter_filter_is_ok.rs:21:56 + --> tests/ui/iter_filter_is_ok.rs:22:56 | LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|&a| a.is_ok()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_ok` on iterator over `Result`s - --> tests/ui/iter_filter_is_ok.rs:24:56 + --> tests/ui/iter_filter_is_ok.rs:25:56 | LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|&a| a.is_ok()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_ok` on iterator over `Result`s - --> tests/ui/iter_filter_is_ok.rs:28:49 + --> tests/ui/iter_filter_is_ok.rs:29:49 | LL | let _ = vec![Ok(1), Err(2)].into_iter().filter(|&o| { o.is_ok() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_ok` on iterator over `Result`s - --> tests/ui/iter_filter_is_ok.rs:35:14 + --> tests/ui/iter_filter_is_ok.rs:36:14 | LL | .filter(std::result::Result::is_ok); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_ok` on iterator over `Result`s - --> tests/ui/iter_filter_is_ok.rs:40:14 + --> tests/ui/iter_filter_is_ok.rs:41:14 | LL | .filter(|a| std::result::Result::is_ok(a)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_ok` on iterator over `Result`s - --> tests/ui/iter_filter_is_ok.rs:43:56 + --> tests/ui/iter_filter_is_ok.rs:44:56 | LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|a| { std::result::Result::is_ok(a) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_ok` on iterator over `Result`s - --> tests/ui/iter_filter_is_ok.rs:48:56 + --> tests/ui/iter_filter_is_ok.rs:49:56 | LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|ref a| a.is_ok()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_ok` on iterator over `Result`s - --> tests/ui/iter_filter_is_ok.rs:51:56 + --> tests/ui/iter_filter_is_ok.rs:52:56 | LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|ref a| a.is_ok()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_ok` on iterator over `Result`s - --> tests/ui/iter_filter_is_ok.rs:55:49 + --> tests/ui/iter_filter_is_ok.rs:56:49 | LL | let _ = vec![Ok(1), Err(2)].into_iter().filter(|ref o| { o.is_ok() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` diff --git a/tests/ui/iter_filter_is_some.fixed b/tests/ui/iter_filter_is_some.fixed index 8a818c0c6728..54d83340a478 100644 --- a/tests/ui/iter_filter_is_some.fixed +++ b/tests/ui/iter_filter_is_some.fixed @@ -5,7 +5,8 @@ clippy::needless_borrow, clippy::option_filter_map, clippy::redundant_closure, - clippy::unnecessary_get_then_check + clippy::unnecessary_get_then_check, + clippy::unnecessary_collection_clone )] use std::collections::HashMap; diff --git a/tests/ui/iter_filter_is_some.rs b/tests/ui/iter_filter_is_some.rs index 9eda93a25921..06304f3d312e 100644 --- a/tests/ui/iter_filter_is_some.rs +++ b/tests/ui/iter_filter_is_some.rs @@ -5,7 +5,8 @@ clippy::needless_borrow, clippy::option_filter_map, clippy::redundant_closure, - clippy::unnecessary_get_then_check + clippy::unnecessary_get_then_check, + clippy::unnecessary_collection_clone )] use std::collections::HashMap; diff --git a/tests/ui/iter_filter_is_some.stderr b/tests/ui/iter_filter_is_some.stderr index 54aff892b1f0..f41725142fbf 100644 --- a/tests/ui/iter_filter_is_some.stderr +++ b/tests/ui/iter_filter_is_some.stderr @@ -1,5 +1,5 @@ error: `filter` for `is_some` on iterator over `Option` - --> tests/ui/iter_filter_is_some.rs:15:58 + --> tests/ui/iter_filter_is_some.rs:16:58 | LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(Option::is_some); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` @@ -8,55 +8,55 @@ LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(Option::is_ = help: to override `-D warnings` add `#[allow(clippy::iter_filter_is_some)]` error: `filter` for `is_some` on iterator over `Option` - --> tests/ui/iter_filter_is_some.rs:17:58 + --> tests/ui/iter_filter_is_some.rs:18:58 | LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(|a| a.is_some()); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_some` on iterator over `Option` - --> tests/ui/iter_filter_is_some.rs:20:58 + --> tests/ui/iter_filter_is_some.rs:21:58 | LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(|o| { o.is_some() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_some` on iterator over `Option` - --> tests/ui/iter_filter_is_some.rs:27:14 + --> tests/ui/iter_filter_is_some.rs:28:14 | LL | .filter(std::option::Option::is_some); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_some` on iterator over `Option` - --> tests/ui/iter_filter_is_some.rs:32:14 + --> tests/ui/iter_filter_is_some.rs:33:14 | LL | .filter(|a| std::option::Option::is_some(a)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_some` on iterator over `Option` - --> tests/ui/iter_filter_is_some.rs:35:58 + --> tests/ui/iter_filter_is_some.rs:36:58 | LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(|a| { std::option::Option::is_some(a) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_some` on iterator over `Option` - --> tests/ui/iter_filter_is_some.rs:40:58 + --> tests/ui/iter_filter_is_some.rs:41:58 | LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(|&a| a.is_some()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_some` on iterator over `Option` - --> tests/ui/iter_filter_is_some.rs:44:58 + --> tests/ui/iter_filter_is_some.rs:45:58 | LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(|&o| { o.is_some() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_some` on iterator over `Option` - --> tests/ui/iter_filter_is_some.rs:49:58 + --> tests/ui/iter_filter_is_some.rs:50:58 | LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(|ref a| a.is_some()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_some` on iterator over `Option` - --> tests/ui/iter_filter_is_some.rs:53:58 + --> tests/ui/iter_filter_is_some.rs:54:58 | LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(|ref o| { o.is_some() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` diff --git a/tests/ui/iter_kv_map.fixed b/tests/ui/iter_kv_map.fixed index 2cbf972fca5f..fd95843bfc5b 100644 --- a/tests/ui/iter_kv_map.fixed +++ b/tests/ui/iter_kv_map.fixed @@ -1,5 +1,11 @@ #![warn(clippy::iter_kv_map)] -#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)] +#![allow( + unused_mut, + clippy::redundant_clone, + clippy::suspicious_map, + clippy::map_identity, + clippy::unnecessary_collection_clone +)] use std::collections::{BTreeMap, HashMap}; diff --git a/tests/ui/iter_kv_map.rs b/tests/ui/iter_kv_map.rs index 6a9a4267a765..541a3bb60d6f 100644 --- a/tests/ui/iter_kv_map.rs +++ b/tests/ui/iter_kv_map.rs @@ -1,5 +1,11 @@ #![warn(clippy::iter_kv_map)] -#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)] +#![allow( + unused_mut, + clippy::redundant_clone, + clippy::suspicious_map, + clippy::map_identity, + clippy::unnecessary_collection_clone +)] use std::collections::{BTreeMap, HashMap}; diff --git a/tests/ui/iter_kv_map.stderr b/tests/ui/iter_kv_map.stderr index ad23dba55cb6..c429c060e534 100644 --- a/tests/ui/iter_kv_map.stderr +++ b/tests/ui/iter_kv_map.stderr @@ -1,5 +1,5 @@ error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:14:13 + --> tests/ui/iter_kv_map.rs:20:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` @@ -8,73 +8,73 @@ LL | let _ = map.iter().map(|(key, _)| key).collect::>(); = help: to override `-D warnings` add `#[allow(clippy::iter_kv_map)]` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:15:13 + --> tests/ui/iter_kv_map.rs:21:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:16:13 + --> tests/ui/iter_kv_map.rs:22:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:18:13 + --> tests/ui/iter_kv_map.rs:24:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:19:13 + --> tests/ui/iter_kv_map.rs:25:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:21:13 + --> tests/ui/iter_kv_map.rs:27:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:22:13 + --> tests/ui/iter_kv_map.rs:28:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:24:13 + --> tests/ui/iter_kv_map.rs:30:13 | LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:25:13 + --> tests/ui/iter_kv_map.rs:31:13 | LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:35:13 + --> tests/ui/iter_kv_map.rs:41:13 | LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:36:13 + --> tests/ui/iter_kv_map.rs:42:13 | LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:39:13 + --> tests/ui/iter_kv_map.rs:45:13 | LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:42:13 + --> tests/ui/iter_kv_map.rs:48:13 | LL | let _ = map | _____________^ @@ -96,85 +96,85 @@ LL + }) | error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:52:13 + --> tests/ui/iter_kv_map.rs:58:13 | LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:56:13 + --> tests/ui/iter_kv_map.rs:62:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:57:13 + --> tests/ui/iter_kv_map.rs:63:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:58:13 + --> tests/ui/iter_kv_map.rs:64:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:60:13 + --> tests/ui/iter_kv_map.rs:66:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:61:13 + --> tests/ui/iter_kv_map.rs:67:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:63:13 + --> tests/ui/iter_kv_map.rs:69:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:64:13 + --> tests/ui/iter_kv_map.rs:70:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:66:13 + --> tests/ui/iter_kv_map.rs:72:13 | LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:67:13 + --> tests/ui/iter_kv_map.rs:73:13 | LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:77:13 + --> tests/ui/iter_kv_map.rs:83:13 | LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:78:13 + --> tests/ui/iter_kv_map.rs:84:13 | LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:81:13 + --> tests/ui/iter_kv_map.rs:87:13 | LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:84:13 + --> tests/ui/iter_kv_map.rs:90:13 | LL | let _ = map | _____________^ @@ -196,67 +196,67 @@ LL + }) | error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:94:13 + --> tests/ui/iter_kv_map.rs:100:13 | LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:109:13 + --> tests/ui/iter_kv_map.rs:115:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:111:13 + --> tests/ui/iter_kv_map.rs:117:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:113:13 + --> tests/ui/iter_kv_map.rs:119:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:122:13 + --> tests/ui/iter_kv_map.rs:128:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:124:13 + --> tests/ui/iter_kv_map.rs:130:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:127:13 + --> tests/ui/iter_kv_map.rs:133:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:129:13 + --> tests/ui/iter_kv_map.rs:135:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:132:13 + --> tests/ui/iter_kv_map.rs:138:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:134:13 + --> tests/ui/iter_kv_map.rs:140:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:136:13 + --> tests/ui/iter_kv_map.rs:142:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` diff --git a/tests/ui/unnecessary_collection_clone.fixed b/tests/ui/unnecessary_collection_clone.fixed new file mode 100644 index 000000000000..62dfdbf95c5f --- /dev/null +++ b/tests/ui/unnecessary_collection_clone.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::unnecessary_collection_clone)] +#![allow(clippy::iter_cloned_collect)] + +use std::collections::LinkedList; +use std::marker::PhantomData; + +fn basic(val: Vec) -> Vec { + val.iter().cloned().collect() + //~^ error: using clone on collection to own iterated items +} + +fn non_deref_to_slice(val: LinkedList) -> Vec { + val.iter().cloned().collect() + //~^ error: using clone on collection to own iterated items +} + +fn partial_borrow(vals: (Vec,)) -> Vec { + vals.0.iter().cloned().collect() + //~^ error: using clone on collection to own iterated items +} + +fn generic(val: Vec) -> Vec { + val.iter().cloned().collect() + //~^ error: using clone on collection to own iterated items +} + +fn use_mutable(_: &mut T) {} + +// Should not lint, as the replacement causes the mutable borrow to overlap +fn used_mutably(mut vals: Vec) { + for val in vals.clone().into_iter() { + use_mutable(&mut vals) + } +} + +// Should not lint, as the replacement causes the mutable borrow to overlap +fn used_mutably_chain(mut vals: Vec) { + vals.clone().into_iter().for_each(|_| use_mutable(&mut vals)); +} + +// Should not lint, as the replacement causes the mutable borrow to overlap +fn used_mutably_partial_borrow(mut vals: (Vec,)) { + vals.0.clone().into_iter().for_each(|_| use_mutable(&mut vals.0)) +} + +// Should not lint, as `Src` has no `iter` method to use. +fn too_generic(val: Src) -> Dst +where + Src: IntoIterator + Clone, + Dst: FromIterator, +{ + val.clone().into_iter().collect() +} + +fn main() {} diff --git a/tests/ui/unnecessary_collection_clone.rs b/tests/ui/unnecessary_collection_clone.rs new file mode 100644 index 000000000000..6a516ae725c9 --- /dev/null +++ b/tests/ui/unnecessary_collection_clone.rs @@ -0,0 +1,55 @@ +#![warn(clippy::unnecessary_collection_clone)] +#![allow(clippy::iter_cloned_collect)] + +use std::collections::LinkedList; +use std::marker::PhantomData; + +fn basic(val: Vec) -> Vec { + val.clone().into_iter().collect() + //~^ error: using clone on collection to own iterated items +} + +fn non_deref_to_slice(val: LinkedList) -> Vec { + val.clone().into_iter().collect() + //~^ error: using clone on collection to own iterated items +} + +fn partial_borrow(vals: (Vec,)) -> Vec { + vals.0.clone().into_iter().collect() + //~^ error: using clone on collection to own iterated items +} + +fn generic(val: Vec) -> Vec { + val.clone().into_iter().collect() + //~^ error: using clone on collection to own iterated items +} + +fn use_mutable(_: &mut T) {} + +// Should not lint, as the replacement causes the mutable borrow to overlap +fn used_mutably(mut vals: Vec) { + for val in vals.clone().into_iter() { + use_mutable(&mut vals) + } +} + +// Should not lint, as the replacement causes the mutable borrow to overlap +fn used_mutably_chain(mut vals: Vec) { + vals.clone().into_iter().for_each(|_| use_mutable(&mut vals)); +} + +// Should not lint, as the replacement causes the mutable borrow to overlap +fn used_mutably_partial_borrow(mut vals: (Vec,)) { + vals.0.clone().into_iter().for_each(|_| use_mutable(&mut vals.0)) +} + +// Should not lint, as `Src` has no `iter` method to use. +fn too_generic(val: Src) -> Dst +where + Src: IntoIterator + Clone, + Dst: FromIterator, +{ + val.clone().into_iter().collect() +} + +fn main() {} diff --git a/tests/ui/unnecessary_collection_clone.stderr b/tests/ui/unnecessary_collection_clone.stderr new file mode 100644 index 000000000000..d6e07f3165a8 --- /dev/null +++ b/tests/ui/unnecessary_collection_clone.stderr @@ -0,0 +1,29 @@ +error: using clone on collection to own iterated items + --> tests/ui/unnecessary_collection_clone.rs:8:5 + | +LL | val.clone().into_iter().collect() + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `val.iter().cloned()` + | + = note: `-D clippy::unnecessary-collection-clone` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_collection_clone)]` + +error: using clone on collection to own iterated items + --> tests/ui/unnecessary_collection_clone.rs:13:5 + | +LL | val.clone().into_iter().collect() + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `val.iter().cloned()` + +error: using clone on collection to own iterated items + --> tests/ui/unnecessary_collection_clone.rs:18:5 + | +LL | vals.0.clone().into_iter().collect() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `vals.0.iter().cloned()` + +error: using clone on collection to own iterated items + --> tests/ui/unnecessary_collection_clone.rs:23:5 + | +LL | val.clone().into_iter().collect() + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `val.iter().cloned()` + +error: aborting due to 4 previous errors +