Skip to content

Commit 69d4390

Browse files
committed
Handle Result on map_flatten lint
Adds a check on `.map(..).flatten()` on `Result` type that follows the behaviour on `Option` type.
1 parent 05fa78f commit 69d4390

File tree

4 files changed

+49
-21
lines changed

4 files changed

+49
-21
lines changed

clippy_lints/src/methods/map_flatten.rs

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,32 @@ pub(super) fn check<'tcx>(
5252
);
5353
}
5454

55-
// lint if caller of `.map().flatten()` is an Option
56-
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type) {
57-
let func_snippet = snippet(cx, map_arg.span, "..");
58-
let hint = format!(".and_then({})", func_snippet);
59-
span_lint_and_sugg(
60-
cx,
61-
MAP_FLATTEN,
62-
expr.span.with_lo(recv.span.hi()),
63-
"called `map(..).flatten()` on an `Option`",
64-
"try using `and_then` instead",
65-
hint,
66-
Applicability::MachineApplicable,
67-
);
68-
}
55+
// lint if caller of `.map().flatten()` is an Option or Result
56+
let caller_type = match cx.typeck_results().expr_ty(recv).kind() {
57+
ty::Adt(adt, _) => {
58+
if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) {
59+
"Option"
60+
} else if cx.tcx.is_diagnostic_item(sym::result_type, adt.did) {
61+
"Result"
62+
} else {
63+
return;
64+
}
65+
},
66+
_ => {
67+
return;
68+
},
69+
};
70+
71+
let func_snippet = snippet(cx, map_arg.span, "..");
72+
let hint = format!(".and_then({})", func_snippet);
73+
let lint_info = format!("called `map(..).flatten()` on an `{}`", caller_type);
74+
span_lint_and_sugg(
75+
cx,
76+
MAP_FLATTEN,
77+
expr.span.with_lo(recv.span.hi()),
78+
&lint_info,
79+
"try using `and_then` instead",
80+
hint,
81+
Applicability::MachineApplicable,
82+
);
6983
}

tests/ui/map_flatten.fixed

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#![allow(clippy::missing_docs_in_private_items)]
66
#![allow(clippy::map_identity)]
77
#![allow(clippy::unnecessary_wraps)]
8+
#![feature(result_flattening)]
89

910
fn main() {
1011
// mapping to Option on Iterator
@@ -23,4 +24,7 @@ fn main() {
2324

2425
// mapping to Option on Option
2526
let _: Option<_> = (Some(Some(1))).and_then(|x| x);
27+
28+
// mapping to Result on Result
29+
let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x);
2630
}

tests/ui/map_flatten.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#![allow(clippy::missing_docs_in_private_items)]
66
#![allow(clippy::map_identity)]
77
#![allow(clippy::unnecessary_wraps)]
8+
#![feature(result_flattening)]
89

910
fn main() {
1011
// mapping to Option on Iterator
@@ -23,4 +24,7 @@ fn main() {
2324

2425
// mapping to Option on Option
2526
let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
27+
28+
// mapping to Result on Result
29+
let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
2630
}

tests/ui/map_flatten.stderr

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,46 @@
11
error: called `map(..).flatten()` on an `Iterator`
2-
--> $DIR/map_flatten.rs:16:46
2+
--> $DIR/map_flatten.rs:17:46
33
|
44
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)`
66
|
77
= note: `-D clippy::map-flatten` implied by `-D warnings`
88

99
error: called `map(..).flatten()` on an `Iterator`
10-
--> $DIR/map_flatten.rs:17:46
10+
--> $DIR/map_flatten.rs:18:46
1111
|
1212
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
1313
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)`
1414

1515
error: called `map(..).flatten()` on an `Iterator`
16-
--> $DIR/map_flatten.rs:18:46
16+
--> $DIR/map_flatten.rs:19:46
1717
|
1818
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
1919
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)`
2020

2121
error: called `map(..).flatten()` on an `Iterator`
22-
--> $DIR/map_flatten.rs:19:46
22+
--> $DIR/map_flatten.rs:20:46
2323
|
2424
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
2525
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))`
2626

2727
error: called `map(..).flatten()` on an `Iterator`
28-
--> $DIR/map_flatten.rs:22:46
28+
--> $DIR/map_flatten.rs:23:46
2929
|
3030
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
3131
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)`
3232

3333
error: called `map(..).flatten()` on an `Option`
34-
--> $DIR/map_flatten.rs:25:39
34+
--> $DIR/map_flatten.rs:26:39
3535
|
3636
LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
3737
| ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)`
3838

39-
error: aborting due to 6 previous errors
39+
error: called `map(..).flatten()` on an `Result`
40+
--> $DIR/map_flatten.rs:29:41
41+
|
42+
LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
43+
| ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)`
44+
45+
error: aborting due to 7 previous errors
4046

0 commit comments

Comments
 (0)