Skip to content

Commit f8cd666

Browse files
committed
Implement a lint for .clone().into_iter()
1 parent 5873cb9 commit f8cd666

22 files changed

+291
-87
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6056,6 +6056,7 @@ Released 2018-09-13
60566056
[`unnecessary_box_returns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns
60576057
[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
60586058
[`unnecessary_clippy_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_clippy_cfg
6059+
[`unnecessary_collection_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_collection_clone
60596060
[`unnecessary_fallible_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fallible_conversions
60606061
[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
60616062
[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
77

8-
[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
8+
[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
99

1010
Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
1111
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.

book/src/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
A collection of lints to catch common mistakes and improve your
77
[Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
Lints are divided into categories, each with a default [lint
1212
level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
468468
crate::methods::TYPE_ID_ON_BOX_INFO,
469469
crate::methods::UNINIT_ASSUMED_INIT_INFO,
470470
crate::methods::UNIT_HASH_INFO,
471+
crate::methods::UNNECESSARY_COLLECTION_CLONE_INFO,
471472
crate::methods::UNNECESSARY_FALLIBLE_CONVERSIONS_INFO,
472473
crate::methods::UNNECESSARY_FILTER_MAP_INFO,
473474
crate::methods::UNNECESSARY_FIND_MAP_INFO,

clippy_lints/src/methods/mod.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ mod suspicious_to_owned;
109109
mod type_id_on_box;
110110
mod uninit_assumed_init;
111111
mod unit_hash;
112+
mod unnecessary_collection_clone;
112113
mod unnecessary_fallible_conversions;
113114
mod unnecessary_filter_map;
114115
mod unnecessary_first_then_check;
@@ -4166,6 +4167,36 @@ declare_clippy_lint! {
41664167
"calling `.first().is_some()` or `.first().is_none()` instead of `.is_empty()`"
41674168
}
41684169

4170+
declare_clippy_lint! {
4171+
/// ### What it does
4172+
///
4173+
/// Detects when an entire collection is being cloned eagerly, instead of each item lazily.
4174+
///
4175+
/// ### Why is this bad?
4176+
///
4177+
/// Cloning a collection requires allocating space for all elements and cloning each element into this new space,
4178+
/// whereas using `Iterator::cloned` does not allocate any more space and only requires cloning each element as they are consumed.
4179+
///
4180+
/// ### Example
4181+
/// ```no_run
4182+
/// fn process_string(val: String) -> String { val }
4183+
/// fn process_strings(strings: &Vec<String>) -> Vec<String> {
4184+
/// strings.clone().into_iter().filter(|s| s.len() < 10).map(process_string).collect()
4185+
/// }
4186+
/// ```
4187+
/// Use instead:
4188+
/// ```no_run
4189+
/// fn process_string(val: String) -> String { val }
4190+
/// fn process_strings(strings: &Vec<String>) -> Vec<String> {
4191+
/// strings.iter().cloned().filter(|s| s.len() < 10).map(process_string).collect()
4192+
/// }
4193+
/// ```
4194+
#[clippy::version = "1.84.0"]
4195+
pub UNNECESSARY_COLLECTION_CLONE,
4196+
perf,
4197+
"calling `.clone().into_iter()` instead of `.iter().cloned()`"
4198+
}
4199+
41694200
pub struct Methods {
41704201
avoid_breaking_exported_api: bool,
41714202
msrv: Msrv,
@@ -4327,6 +4358,7 @@ impl_lint_pass!(Methods => [
43274358
NEEDLESS_CHARACTER_ITERATION,
43284359
MANUAL_INSPECT,
43294360
UNNECESSARY_MIN_OR_MAX,
4361+
UNNECESSARY_COLLECTION_CLONE,
43304362
]);
43314363

43324364
/// Extracts a method call name, args, and `Span` of the method name.
@@ -4774,6 +4806,10 @@ impl Methods {
47744806
("is_none", []) => check_is_some_is_none(cx, expr, recv, call_span, false),
47754807
("is_some", []) => check_is_some_is_none(cx, expr, recv, call_span, true),
47764808
("iter" | "iter_mut" | "into_iter", []) => {
4809+
if name == "into_iter" {
4810+
unnecessary_collection_clone::check(cx, expr, recv);
4811+
}
4812+
47774813
iter_on_single_or_empty_collections::check(cx, expr, name, recv);
47784814
},
47794815
("join", [join_arg]) => {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::snippet_with_applicability;
3+
use clippy_utils::ty::{deref_chain, get_inherent_method, implements_trait, make_normalized_projection};
4+
use rustc_errors::Applicability;
5+
use rustc_hir::Expr;
6+
use rustc_lint::LateContext;
7+
use rustc_middle::ty::{self, Ty};
8+
use rustc_span::sym;
9+
10+
use super::{UNNECESSARY_COLLECTION_CLONE, method_call};
11+
12+
// FIXME: This does not check if the iter method is actually compatible with the replacement, but
13+
// you have to be actively evil to have an `IntoIterator` impl that returns one type and an `iter`
14+
// method that returns something other than references of that type.... and it is a massive
15+
// complicated hassle to check this
16+
fn has_iter_method<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
17+
deref_chain(cx, ty).any(|ty| match ty.kind() {
18+
ty::Adt(adt_def, _) => get_inherent_method(cx, adt_def.did(), sym::iter).is_some(),
19+
ty::Slice(_) => true,
20+
_ => false,
21+
})
22+
}
23+
24+
/// Check for `x.clone().into_iter()` to suggest `x.iter().cloned()`.
25+
// ^^^^^^^^^ is recv
26+
// ^^^^^^^^^^^^^^^^^^^^^ is expr
27+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) {
28+
let typeck_results = cx.typeck_results();
29+
let diagnostic_items = cx.tcx.all_diagnostic_items(());
30+
31+
// If the call before `into_iter` is `.clone()`
32+
if let Some(("clone", collection_expr, [], _, _)) = method_call(recv)
33+
// and the result of `into_iter` is an Iterator
34+
&& let Some(&iterator_def_id) = diagnostic_items.name_to_id.get(&sym::Iterator)
35+
&& let expr_ty = typeck_results.expr_ty(expr)
36+
&& implements_trait(cx, expr_ty, iterator_def_id, &[])
37+
// with an item that implements clone
38+
&& let Some(&clone_def_id) = diagnostic_items.name_to_id.get(&sym::Clone)
39+
&& let Some(item_ty) = make_normalized_projection(cx.tcx, cx.param_env, iterator_def_id, sym::Item, [expr_ty])
40+
&& implements_trait(cx, item_ty, clone_def_id, &[])
41+
// and the type has an `iter` method
42+
&& has_iter_method(cx, typeck_results.expr_ty(collection_expr))
43+
{
44+
let mut applicability = Applicability::MachineApplicable;
45+
let collection_expr_snippet = snippet_with_applicability(cx, collection_expr.span, "...", &mut applicability);
46+
span_lint_and_sugg(
47+
cx,
48+
UNNECESSARY_COLLECTION_CLONE,
49+
expr.span,
50+
"using clone on collection to own iterated items",
51+
"replace with",
52+
format!("{collection_expr_snippet}.iter().cloned()"),
53+
applicability,
54+
);
55+
}
56+
}

clippy_utils/src/ty.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,22 +1323,29 @@ pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl
13231323

13241324
/// Checks if a Ty<'_> has some inherent method Symbol.
13251325
///
1326-
/// This does not look for impls in the type's `Deref::Target` type.
1327-
/// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`.
1326+
/// This is a helper for [`get_inherent_method`].
13281327
pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<&'a AssocItem> {
13291328
if let Some(ty_did) = ty.ty_adt_def().map(AdtDef::did) {
1330-
cx.tcx.inherent_impls(ty_did).iter().find_map(|&did| {
1331-
cx.tcx
1332-
.associated_items(did)
1333-
.filter_by_name_unhygienic(method_name)
1334-
.next()
1335-
.filter(|item| item.kind == AssocKind::Fn)
1336-
})
1329+
get_inherent_method(cx, ty_did, method_name)
13371330
} else {
13381331
None
13391332
}
13401333
}
13411334

1335+
/// Checks if the [`DefId`] of a Ty has some inherent method Symbol.
1336+
///
1337+
/// This does not look for impls in the type's `Deref::Target` type.
1338+
/// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`.
1339+
pub fn get_inherent_method<'a>(cx: &'a LateContext<'_>, ty_did: DefId, method_name: Symbol) -> Option<&'a AssocItem> {
1340+
cx.tcx.inherent_impls(ty_did).iter().find_map(|&did| {
1341+
cx.tcx
1342+
.associated_items(did)
1343+
.filter_by_name_unhygienic(method_name)
1344+
.next()
1345+
.filter(|item| item.kind == AssocKind::Fn)
1346+
})
1347+
}
1348+
13421349
/// Get's the type of a field by name.
13431350
pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> Option<Ty<'tcx>> {
13441351
match *ty.kind() {

tests/ui/filter_map_bool_then.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
clippy::map_identity,
55
clippy::unnecessary_lazy_evaluations,
66
clippy::unnecessary_filter_map,
7+
clippy::unnecessary_collection_clone,
78
unused
89
)]
910
#![warn(clippy::filter_map_bool_then)]

tests/ui/filter_map_bool_then.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
clippy::map_identity,
55
clippy::unnecessary_lazy_evaluations,
66
clippy::unnecessary_filter_map,
7+
clippy::unnecessary_collection_clone,
78
unused
89
)]
910
#![warn(clippy::filter_map_bool_then)]

tests/ui/filter_map_bool_then.stderr

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: usage of `bool::then` in `filter_map`
2-
--> tests/ui/filter_map_bool_then.rs:19:22
2+
--> tests/ui/filter_map_bool_then.rs:20:22
33
|
44
LL | v.clone().iter().filter_map(|i| (i % 2 == 0).then(|| i + 1));
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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));
88
= help: to override `-D warnings` add `#[allow(clippy::filter_map_bool_then)]`
99

1010
error: usage of `bool::then` in `filter_map`
11-
--> tests/ui/filter_map_bool_then.rs:20:27
11+
--> tests/ui/filter_map_bool_then.rs:21:27
1212
|
1313
LL | v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1));
1414
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i % 2 == 0)).map(|i| i + 1)`
1515

1616
error: usage of `bool::then` in `filter_map`
17-
--> tests/ui/filter_map_bool_then.rs:23:10
17+
--> tests/ui/filter_map_bool_then.rs:24:10
1818
|
1919
LL | .filter_map(|i| -> Option<_> { (i % 2 == 0).then(|| i + 1) });
2020
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i % 2 == 0)).map(|i| i + 1)`
2121

2222
error: usage of `bool::then` in `filter_map`
23-
--> tests/ui/filter_map_bool_then.rs:27:10
23+
--> tests/ui/filter_map_bool_then.rs:28:10
2424
|
2525
LL | .filter_map(|i| (i % 2 == 0).then(|| i + 1));
2626
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i % 2 == 0)).map(|i| i + 1)`
2727

2828
error: usage of `bool::then` in `filter_map`
29-
--> tests/ui/filter_map_bool_then.rs:31:10
29+
--> tests/ui/filter_map_bool_then.rs:32:10
3030
|
3131
LL | .filter_map(|i| (i.clone() % 2 == 0).then(|| i + 1));
3232
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i.clone() % 2 == 0)).map(|i| i + 1)`
3333

3434
error: usage of `bool::then` in `filter_map`
35-
--> tests/ui/filter_map_bool_then.rs:37:22
35+
--> tests/ui/filter_map_bool_then.rs:38:22
3636
|
3737
LL | v.clone().iter().filter_map(|i| (i == &NonCopy).then(|| i));
3838
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i == &NonCopy)).map(|i| i)`
3939

4040
error: usage of `bool::then` in `filter_map`
41-
--> tests/ui/filter_map_bool_then.rs:61:50
41+
--> tests/ui/filter_map_bool_then.rs:62:50
4242
|
4343
LL | let _: Vec<usize> = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect();
4444
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| *b).map(|(i, b)| i)`
4545

4646
error: usage of `bool::then` in `filter_map`
47-
--> tests/ui/filter_map_bool_then.rs:65:50
47+
--> tests/ui/filter_map_bool_then.rs:66:50
4848
|
4949
LL | let _: Vec<usize> = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect();
5050
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| ***b).map(|(i, b)| i)`
5151

5252
error: usage of `bool::then` in `filter_map`
53-
--> tests/ui/filter_map_bool_then.rs:69:50
53+
--> tests/ui/filter_map_bool_then.rs:70:50
5454
|
5555
LL | let _: Vec<usize> = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect();
5656
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| **b).map(|(i, b)| i)`
5757

5858
error: usage of `bool::then` in `filter_map`
59-
--> tests/ui/filter_map_bool_then.rs:80:50
59+
--> tests/ui/filter_map_bool_then.rs:81:50
6060
|
6161
LL | let _: Vec<usize> = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect();
6262
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| ****b).map(|(i, b)| i)`

tests/ui/iter_filter_is_ok.fixed

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
clippy::map_identity,
44
clippy::result_filter_map,
55
clippy::needless_borrow,
6-
clippy::redundant_closure
6+
clippy::redundant_closure,
7+
clippy::unnecessary_collection_clone
78
)]
89

910
fn main() {

tests/ui/iter_filter_is_ok.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
clippy::map_identity,
44
clippy::result_filter_map,
55
clippy::needless_borrow,
6-
clippy::redundant_closure
6+
clippy::redundant_closure,
7+
clippy::unnecessary_collection_clone
78
)]
89

910
fn main() {

tests/ui/iter_filter_is_ok.stderr

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: `filter` for `is_ok` on iterator over `Result`s
2-
--> tests/ui/iter_filter_is_ok.rs:11:56
2+
--> tests/ui/iter_filter_is_ok.rs:12:56
33
|
44
LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(Result::is_ok);
55
| ^^^^^^^^^^^^^^^^^^^^^ 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
88
= help: to override `-D warnings` add `#[allow(clippy::iter_filter_is_ok)]`
99

1010
error: `filter` for `is_ok` on iterator over `Result`s
11-
--> tests/ui/iter_filter_is_ok.rs:13:56
11+
--> tests/ui/iter_filter_is_ok.rs:14:56
1212
|
1313
LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|a| a.is_ok());
1414
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
1515

1616
error: `filter` for `is_ok` on iterator over `Result`s
17-
--> tests/ui/iter_filter_is_ok.rs:16:49
17+
--> tests/ui/iter_filter_is_ok.rs:17:49
1818
|
1919
LL | let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| { o.is_ok() });
2020
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
2121

2222
error: `filter` for `is_ok` on iterator over `Result`s
23-
--> tests/ui/iter_filter_is_ok.rs:21:56
23+
--> tests/ui/iter_filter_is_ok.rs:22:56
2424
|
2525
LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|&a| a.is_ok());
2626
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
2727

2828
error: `filter` for `is_ok` on iterator over `Result`s
29-
--> tests/ui/iter_filter_is_ok.rs:24:56
29+
--> tests/ui/iter_filter_is_ok.rs:25:56
3030
|
3131
LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|&a| a.is_ok());
3232
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
3333

3434
error: `filter` for `is_ok` on iterator over `Result`s
35-
--> tests/ui/iter_filter_is_ok.rs:28:49
35+
--> tests/ui/iter_filter_is_ok.rs:29:49
3636
|
3737
LL | let _ = vec![Ok(1), Err(2)].into_iter().filter(|&o| { o.is_ok() });
3838
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
3939

4040
error: `filter` for `is_ok` on iterator over `Result`s
41-
--> tests/ui/iter_filter_is_ok.rs:35:14
41+
--> tests/ui/iter_filter_is_ok.rs:36:14
4242
|
4343
LL | .filter(std::result::Result::is_ok);
4444
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
4545

4646
error: `filter` for `is_ok` on iterator over `Result`s
47-
--> tests/ui/iter_filter_is_ok.rs:40:14
47+
--> tests/ui/iter_filter_is_ok.rs:41:14
4848
|
4949
LL | .filter(|a| std::result::Result::is_ok(a));
5050
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
5151

5252
error: `filter` for `is_ok` on iterator over `Result`s
53-
--> tests/ui/iter_filter_is_ok.rs:43:56
53+
--> tests/ui/iter_filter_is_ok.rs:44:56
5454
|
5555
LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|a| { std::result::Result::is_ok(a) });
5656
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
5757

5858
error: `filter` for `is_ok` on iterator over `Result`s
59-
--> tests/ui/iter_filter_is_ok.rs:48:56
59+
--> tests/ui/iter_filter_is_ok.rs:49:56
6060
|
6161
LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|ref a| a.is_ok());
6262
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
6363

6464
error: `filter` for `is_ok` on iterator over `Result`s
65-
--> tests/ui/iter_filter_is_ok.rs:51:56
65+
--> tests/ui/iter_filter_is_ok.rs:52:56
6666
|
6767
LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|ref a| a.is_ok());
6868
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
6969

7070
error: `filter` for `is_ok` on iterator over `Result`s
71-
--> tests/ui/iter_filter_is_ok.rs:55:49
71+
--> tests/ui/iter_filter_is_ok.rs:56:49
7272
|
7373
LL | let _ = vec![Ok(1), Err(2)].into_iter().filter(|ref o| { o.is_ok() });
7474
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`

tests/ui/iter_filter_is_some.fixed

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
clippy::needless_borrow,
66
clippy::option_filter_map,
77
clippy::redundant_closure,
8-
clippy::unnecessary_get_then_check
8+
clippy::unnecessary_get_then_check,
9+
clippy::unnecessary_collection_clone
910
)]
1011

1112
use std::collections::HashMap;

0 commit comments

Comments
 (0)