Skip to content

Commit 6c197cb

Browse files
committed
Remove filter + flat_map lint
1 parent 28eb1e8 commit 6c197cb

File tree

2 files changed

+40
-125
lines changed

2 files changed

+40
-125
lines changed

clippy_lints/src/methods/mod.rs

Lines changed: 38 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -280,32 +280,13 @@ declare_clippy_lint! {
280280
"using combinations of `flatten` and `map` which can usually be written as a single method call"
281281
}
282282

283-
/// **What it does:** Checks for usage of `_.filter(_).map(_)`,
284-
/// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.
283+
/// **What it does:** Checks for usage of `_.filter_map(_).map(_)`, which can be more concisely
284+
/// written as `_.filter_map(_)`.
285285
///
286286
/// **Why is this bad?** Readability, this can be written more concisely as a
287287
/// single method call.
288288
///
289-
/// **Known problems:** Often requires a condition + Option/Iterator creation
290-
/// inside the closure.
291-
///
292-
/// ** filter + map Example:**
293-
/// let ns = [-9, 2, 3, 4, 5, 6];
294-
/// let correct: Vec<i32> = ns.iter()
295-
/// .cloned()
296-
/// .filter(|&x| x > 0)
297-
/// .map(|x| x * 2)
298-
/// .collect();
299-
///
300-
/// let more_correct: Vec<i32> = ns.iter()
301-
/// .cloned()
302-
/// .filter_map(|x| if x > 0 { Some(x * 2) } else { None })
303-
/// .collect();
304-
///
305-
/// println!("{:?}", more_correct);
306-
/// assert_eq!(correct, more_correct);
307-
///
308-
/// ** filter_map + map Example:**
289+
/// ** Example:**
309290
/// let ws: Vec<String> = ["is", "this", "shizzle", "for", "rizzle"]
310291
/// .iter()
311292
/// .map(|s| s.to_string())
@@ -331,57 +312,36 @@ declare_clippy_lint! {
331312
/// println!("{:?}", correct);
332313
/// assert_eq!(correct, more_correct);
333314
///
334-
/// ** filter + flat_map Example:**
335-
/// let ws: Vec<String> = ["is", "this", "shizzle", "for", "rizzle"]
336-
/// .iter()
337-
/// .map(|s| s.to_string())
338-
/// .collect();
339-
///
340-
/// let correct: String = ws.iter()
341-
/// .filter(|s| s.contains("izzle"))
342-
/// .flat_map(|s| s.chars())
343-
/// .collect();
344-
///
345-
/// let more_correct: String = ws.iter()
346-
/// .flat_map(|s| if s.contains("izzle") {
347-
/// return s.chars()
348-
/// } else {
349-
/// "".chars()
350-
/// })
351-
/// .collect();
352-
///
353-
/// println!("{:?}", correct);
354-
/// assert_eq!(correct, more_correct);
315+
declare_clippy_lint! {
316+
pub FILTER_MAP_MAP,
317+
pedantic,
318+
"using `filter_map` followed by `map` can more concisely be written as a single method call"
319+
}
320+
321+
/// **What it does:** Checks for usage of `_.filter(_).map(_)` used to destructure
322+
/// `Iterator<Option<_>>` and `Iterator<Result<_>>` into `Iterator<_>` (an iterator of values).
355323
///
356-
/// ** filter_map + flat_map Example:**
357-
/// let ws: Vec<String> = ["is", "this", "shizzle", "for", "rizzle"]
358-
/// .iter()
359-
/// .map(|s| s.to_string())
360-
/// .collect();
324+
/// **Why is this bad?** Readability, this can be written more concisely as `_.filter_map(|x| x)`.
361325
///
362-
/// let correct: String = ws.iter()
363-
/// .filter_map(|s| if s.contains("izzle"){
364-
/// Some(s)
365-
/// } else {
366-
/// None
367-
/// })
368-
/// .flat_map(|s| s.chars())
326+
/// ** Example:**
327+
/// let ns = [Some(-9), Some(2), None, Some(3), Some(4), None, Some(5), Some(6)];
328+
/// let correct: Vec<i32> = ns.clone().iter()
329+
/// .filter(|x| x.is_some())
330+
/// .map(|x| x.unwrap() )
369331
/// .collect();
370332
///
371-
/// let more_correct: String = ws.iter()
372-
/// .flat_map(|s| if s.contains("izzle") {
373-
/// return s.chars()
374-
/// } else {
375-
/// "".chars()
376-
/// })
333+
/// let more_correct: Vec<i32> = ns.iter()
334+
/// .cloned()
335+
/// .filter_map(|x| x)
377336
/// .collect();
378337
///
379-
/// println!("{:?}", correct);
338+
/// println!("{:?}", more_correct);
380339
/// assert_eq!(correct, more_correct);
340+
///
381341
declare_clippy_lint! {
382342
pub FILTER_MAP,
383343
pedantic,
384-
"using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call"
344+
"using `filter` and `map` to destructure an iterator of Options or a Results instead of a single `filter_map` call"
385345
}
386346

387347
/// **What it does:** Checks for an iterator search (such as `find()`,
@@ -933,7 +893,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
933893
["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
934894
["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]),
935895
["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]),
936-
["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
937896
["flatten", "map"] => lint_map_flatten(cx, expr, arg_lists[1]),
938897
["is_some", "find"] => lint_search_is_some(cx, expr, "find", arg_lists[1], arg_lists[0]),
939898
["is_some", "position"] => lint_search_is_some(cx, expr, "position", arg_lists[1], arg_lists[0]),
@@ -1984,14 +1943,25 @@ fn lint_filter_next<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr,
19841943
}
19851944
}
19861945

1987-
/// lint filter_map variants that iterate over Result or Option values
1988-
fn lint_filter_map_variant<'a, 'tcx>(
1946+
/// lint use of `filter().map()` for `Iterators`
1947+
fn lint_filter_map<'a, 'tcx>(
19891948
cx: &LateContext<'a, 'tcx>,
19901949
expr: &'tcx hir::Expr,
19911950
filter_args: &'tcx [hir::Expr],
19921951
map_args: &'tcx [hir::Expr],
1993-
responses: &HashMap<String, &str>,
19941952
) {
1953+
let mut responses: HashMap<String, &str> = HashMap::new();
1954+
responses.insert(
1955+
"is_some".to_string(),
1956+
"called `filter(p).map(q)` on an `Iterator<Option<_>>`. \
1957+
Consider calling `.filter_map(..)` instead."
1958+
);
1959+
responses.insert(
1960+
"is_ok".to_string(),
1961+
"called `filter(p).map(q)` on an `Iterator<Result<_>>`. \
1962+
Consider calling `.filter_map(..)` instead."
1963+
);
1964+
19951965
if_chain! {
19961966
if match_trait_method(cx, expr, &paths::ITERATOR);
19971967
if let hir::ExprKind::Closure(_, _, ref map_body_id, _, _) = map_args[1].node;
@@ -2009,28 +1979,6 @@ fn lint_filter_map_variant<'a, 'tcx>(
20091979
}
20101980
}
20111981

2012-
/// lint use of `filter().map()` for `Iterators`
2013-
fn lint_filter_map<'a, 'tcx>(
2014-
cx: &LateContext<'a, 'tcx>,
2015-
expr: &'tcx hir::Expr,
2016-
filter_args: &'tcx [hir::Expr],
2017-
map_args: &'tcx [hir::Expr],
2018-
) {
2019-
let mut responses: HashMap<String, &str> = HashMap::new();
2020-
responses.insert(
2021-
"is_some".to_string(),
2022-
"called `filter(p).map(q)` on an `Iterator<Option<_>>`. \
2023-
Consider calling `.filter_map(..)` instead."
2024-
);
2025-
responses.insert(
2026-
"is_ok".to_string(),
2027-
"called `filter(p).map(q)` on an `Iterator<Result<_>>`. \
2028-
Consider calling `.filter_map(..)` instead."
2029-
);
2030-
2031-
lint_filter_map_variant(cx, expr, filter_args, map_args, &responses);
2032-
}
2033-
20341982
/// lint use of `filter().map()` for `Iterators`
20351983
fn lint_filter_map_map<'a, 'tcx>(
20361984
cx: &LateContext<'a, 'tcx>,
@@ -2049,27 +1997,10 @@ fn lint_filter_map_map<'a, 'tcx>(
20491997
if match_trait_method(cx, expr, &paths::ITERATOR) {
20501998
let msg = "called `filter_map(p).map(q)` on an `Iterator`. \
20511999
This is more succinctly expressed by only calling `.filter_map(..)` instead.";
2052-
span_lint(cx, FILTER_MAP, span, msg);
2000+
span_lint(cx, FILTER_MAP_MAP, span, msg);
20532001
}
20542002
}
20552003

2056-
/// lint use of `filter().flat_map()` for `Iterators`
2057-
fn lint_filter_flat_map<'a, 'tcx>(
2058-
cx: &LateContext<'a, 'tcx>,
2059-
expr: &'tcx hir::Expr,
2060-
filter_args: &'tcx [hir::Expr],
2061-
map_args: &'tcx [hir::Expr],
2062-
) {
2063-
let mut responses: HashMap<String, &str> = HashMap::new();
2064-
responses.insert(
2065-
"is_some".to_string(),
2066-
"called `filter(p).flat_map(q)` on an `Iterator<Option<_>>`. \
2067-
Consider calling `.filter_map(..)` instead."
2068-
);
2069-
2070-
lint_filter_map_variant(cx, expr, filter_args, map_args, &responses);
2071-
}
2072-
20732004
/// lint searching an Iterator followed by `is_some()`
20742005
fn lint_search_is_some<'a, 'tcx>(
20752006
cx: &LateContext<'a, 'tcx>,

tests/ui/filter_methods.stderr

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,12 @@
1-
error: called `filter_map(p).map(q)` on an `Iterator`. This is more succinctly expressed by only calling `.filter_map(..)` instead.
2-
--> $DIR/filter_methods.rs:25:10
3-
|
4-
25 | .filter_map(|x| x.checked_mul(2))
5-
| __________^
6-
26 | | .map(|x| x.checked_mul(2))
7-
| |__________________________________^
8-
|
9-
= note: `-D clippy::filter-map` implied by `-D warnings`
10-
111
error: called `filter(p).map(q)` on an `Iterator<Option<_>>`. Consider calling `.filter_map(..)` instead.
122
--> $DIR/filter_methods.rs:31:10
133
|
144
31 | .filter(|x| x.is_some())
155
| __________^
166
32 | | .map(|x| x.unwrap())
177
| |____________________________^
18-
19-
error: called `filter(p).flat_map(q)` on an `Iterator<Option<_>>`. Consider calling `.filter_map(..)` instead.
20-
--> $DIR/filter_methods.rs:39:10
218
|
22-
39 | .filter(|x| x.is_some())
23-
| __________^
24-
40 | | .flat_map(|x| x.unwrap())
25-
| |_________________________________^
9+
= note: `-D clippy::filter-map` implied by `-D warnings`
2610

2711
error: called `filter(p).map(q)` on an `Iterator<Result<_>>`. Consider calling `.filter_map(..)` instead.
2812
--> $DIR/filter_methods.rs:46:10
@@ -32,5 +16,5 @@ error: called `filter(p).map(q)` on an `Iterator<Result<_>>`. Consider calling `
3216
47 | | .map(|s| s.unwrap())
3317
| |____________________________^
3418

35-
error: aborting due to 4 previous errors
19+
error: aborting due to 2 previous errors
3620

0 commit comments

Comments
 (0)