Skip to content

Commit a427c99

Browse files
committed
Handle mapping to Option in map_flatten lint
1 parent 2e0f8b6 commit a427c99

File tree

4 files changed

+35
-9
lines changed

4 files changed

+35
-9
lines changed

clippy_lints/src/methods/mod.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2569,17 +2569,35 @@ fn lint_ok_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Ex
25692569
fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) {
25702570
// lint if caller of `.map().flatten()` is an Iterator
25712571
if match_trait_method(cx, expr, &paths::ITERATOR) {
2572-
let msg = "called `map(..).flatten()` on an `Iterator`. \
2573-
This is more succinctly expressed by calling `.flat_map(..)`";
2572+
let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]);
2573+
let is_map_to_option = if let ty::Closure(_def_id, substs) = map_closure_ty.kind {
2574+
let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&substs.as_closure().sig().output());
2575+
is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type))
2576+
} else {
2577+
false
2578+
};
2579+
2580+
let method_to_use = if is_map_to_option {
2581+
// `(...).map(...)` has type `impl Iterator<Item=Option<...>>
2582+
"filter_map"
2583+
} else {
2584+
// `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>>
2585+
"flat_map"
2586+
};
2587+
let msg = &format!(
2588+
"called `map(..).flatten()` on an `Iterator`. \
2589+
This is more succinctly expressed by calling `.{}(..)`",
2590+
method_to_use
2591+
);
25742592
let self_snippet = snippet(cx, map_args[0].span, "..");
25752593
let func_snippet = snippet(cx, map_args[1].span, "..");
2576-
let hint = format!("{0}.flat_map({1})", self_snippet, func_snippet);
2594+
let hint = format!("{0}.{1}({2})", self_snippet, method_to_use, func_snippet);
25772595
span_lint_and_sugg(
25782596
cx,
25792597
MAP_FLATTEN,
25802598
expr.span,
25812599
msg,
2582-
"try using `flat_map` instead",
2600+
&format!("try using `{}` instead", method_to_use),
25832601
hint,
25842602
Applicability::MachineApplicable,
25852603
);

tests/ui/map_flatten.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#![allow(clippy::map_identity)]
66

77
fn main() {
8+
let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect();
89
let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect();
910
let _: Option<_> = (Some(Some(1))).and_then(|x| x);
1011
}

tests/ui/map_flatten.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#![allow(clippy::map_identity)]
66

77
fn main() {
8+
let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
89
let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
910
let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
1011
}

tests/ui/map_flatten.stderr

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
1-
error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)`
1+
error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.filter_map(..)`
22
--> $DIR/map_flatten.rs:8:21
33
|
4-
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)`
4+
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1))`
66
|
77
= note: `-D clippy::map-flatten` implied by `-D warnings`
88

9+
error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)`
10+
--> $DIR/map_flatten.rs:9:21
11+
|
12+
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)`
14+
915
error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)`
10-
--> $DIR/map_flatten.rs:9:24
16+
--> $DIR/map_flatten.rs:10:24
1117
|
1218
LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
1319
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)`
1420

15-
error: aborting due to 2 previous errors
21+
error: aborting due to 3 previous errors
1622

0 commit comments

Comments
 (0)