Skip to content

Commit 304793d

Browse files
authored
filter_map_next: clean-up, overhaul suggestions (#17237)
changelog: [`filter_map_next`]: overhaul suggestions
2 parents 5b825ad + 473f7cd commit 304793d

6 files changed

Lines changed: 137 additions & 76 deletions

File tree

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,33 @@
1-
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
1+
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::msrvs::{self, Msrv};
33
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
4-
use clippy_utils::source::snippet;
4+
use clippy_utils::source::snippet_with_applicability;
55
use rustc_errors::Applicability;
6-
use rustc_hir as hir;
6+
use rustc_hir::Expr;
77
use rustc_lint::LateContext;
88
use rustc_span::sym;
99

1010
use super::FILTER_MAP_NEXT;
1111

12-
pub(super) fn check<'tcx>(
13-
cx: &LateContext<'tcx>,
14-
expr: &'tcx hir::Expr<'_>,
15-
recv: &'tcx hir::Expr<'_>,
16-
arg: &'tcx hir::Expr<'_>,
17-
msrv: Msrv,
18-
) {
19-
if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) {
20-
if !msrv.meets(cx, msrvs::ITERATOR_FIND_MAP) {
21-
return;
22-
}
23-
24-
let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
25-
`.find_map(..)` instead";
26-
let filter_snippet = snippet(cx, arg.span, "..");
27-
if filter_snippet.lines().count() <= 1 {
28-
let iter_snippet = snippet(cx, recv.span, "..");
29-
span_lint_and_sugg(
30-
cx,
31-
FILTER_MAP_NEXT,
32-
expr.span,
33-
msg,
34-
"try",
35-
format!("{iter_snippet}.find_map({filter_snippet})"),
36-
Applicability::MachineApplicable,
37-
);
38-
} else {
39-
span_lint(cx, FILTER_MAP_NEXT, expr.span, msg);
40-
}
12+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>, msrv: Msrv) {
13+
if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && msrv.meets(cx, msrvs::ITERATOR_FIND_MAP)
14+
{
15+
span_lint_and_then(
16+
cx,
17+
FILTER_MAP_NEXT,
18+
expr.span,
19+
"called `filter_map(..).next()` on an `Iterator`",
20+
|diag| {
21+
let mut app = Applicability::MachineApplicable;
22+
let iter_snippet = snippet_with_applicability(cx, recv.span, "_", &mut app);
23+
let filter_snippet = snippet_with_applicability(cx, arg.span, "..", &mut app);
24+
diag.span_suggestion_verbose(
25+
expr.span,
26+
"use `.find_map(..)` instead",
27+
format!("{iter_snippet}.find_map({filter_snippet})"),
28+
app,
29+
);
30+
},
31+
);
4132
}
4233
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,27 @@ fn main() {
66
let element: Option<i32> = a.iter().find_map(|s| s.parse().ok());
77
//~^ filter_map_next
88
assert_eq!(element, Some(1));
9+
10+
#[rustfmt::skip]
11+
let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
12+
//~^ filter_map_next
13+
14+
15+
.into_iter().find_map(|x| {
16+
if x == 2 {
17+
Some(x * 2)
18+
} else {
19+
None
20+
}
21+
});
22+
23+
let element: Option<i32> = a
24+
// very important comment -- don't delete!
25+
.iter().find_map(|s| {
26+
// another extremely important comment
27+
s.parse().ok()
28+
});
29+
//~^^^^^^^^ filter_map_next
930
}
1031

1132
#[clippy::msrv = "1.29"]

tests/ui/filter_map_next.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
fn main() {
44
let a = ["1", "lol", "3", "NaN", "5"];
55

6+
let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
7+
//~^ filter_map_next
8+
assert_eq!(element, Some(1));
9+
610
#[rustfmt::skip]
711
let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
812
//~^ filter_map_next
@@ -17,4 +21,27 @@ fn main() {
1721
}
1822
})
1923
.next();
24+
25+
let element: Option<i32> = a
26+
// very important comment -- don't delete!
27+
.iter()
28+
.filter_map(|s| {
29+
// another extremely important comment
30+
s.parse().ok()
31+
})
32+
.next();
33+
//~^^^^^^^^ filter_map_next
34+
}
35+
36+
#[clippy::msrv = "1.29"]
37+
fn msrv_1_29() {
38+
let a = ["1", "lol", "3", "NaN", "5"];
39+
let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
40+
}
41+
42+
#[clippy::msrv = "1.30"]
43+
fn msrv_1_30() {
44+
let a = ["1", "lol", "3", "NaN", "5"];
45+
let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
46+
//~^ filter_map_next
2047
}

tests/ui/filter_map_next.stderr

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
1-
error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead
2-
--> tests/ui/filter_map_next.rs:7:26
1+
error: called `filter_map(..).next()` on an `Iterator`
2+
--> tests/ui/filter_map_next.rs:6:32
3+
|
4+
LL | let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::filter-map-next` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::filter_map_next)]`
9+
help: use `.find_map(..)` instead
10+
|
11+
LL - let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
12+
LL + let element: Option<i32> = a.iter().find_map(|s| s.parse().ok());
13+
|
14+
15+
error: called `filter_map(..).next()` on an `Iterator`
16+
--> tests/ui/filter_map_next.rs:11:26
317
|
418
LL | let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
519
| __________________________^
@@ -8,8 +22,55 @@ LL | | })
822
LL | | .next();
923
| |_______________^
1024
|
11-
= note: `-D clippy::filter-map-next` implied by `-D warnings`
12-
= help: to override `-D warnings` add `#[allow(clippy::filter_map_next)]`
25+
help: use `.find_map(..)` instead
26+
|
27+
LL ~ let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
28+
LL +
29+
LL +
30+
LL +
31+
LL + .into_iter().find_map(|x| {
32+
LL + if x == 2 {
33+
LL + Some(x * 2)
34+
LL + } else {
35+
LL + None
36+
LL + }
37+
LL ~ });
38+
|
39+
40+
error: called `filter_map(..).next()` on an `Iterator`
41+
--> tests/ui/filter_map_next.rs:25:32
42+
|
43+
LL | let element: Option<i32> = a
44+
| ________________________________^
45+
LL | | // very important comment -- don't delete!
46+
LL | | .iter()
47+
LL | | .filter_map(|s| {
48+
... |
49+
LL | | })
50+
LL | | .next();
51+
| |_______________^
52+
|
53+
help: use `.find_map(..)` instead
54+
|
55+
LL ~ let element: Option<i32> = a
56+
LL + // very important comment -- don't delete!
57+
LL + .iter().find_map(|s| {
58+
LL + // another extremely important comment
59+
LL + s.parse().ok()
60+
LL ~ });
61+
|
62+
63+
error: called `filter_map(..).next()` on an `Iterator`
64+
--> tests/ui/filter_map_next.rs:45:26
65+
|
66+
LL | let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
67+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
68+
|
69+
help: use `.find_map(..)` instead
70+
|
71+
LL - let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
72+
LL + let _: Option<i32> = a.iter().find_map(|s| s.parse().ok());
73+
|
1374

14-
error: aborting due to 1 previous error
75+
error: aborting due to 4 previous errors
1576

tests/ui/filter_map_next_fixable.rs

Lines changed: 0 additions & 22 deletions
This file was deleted.

tests/ui/filter_map_next_fixable.stderr

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)