Auto merge of #10949 - y21:issue8010, r=Alexendoo
[`manual_filter_map`]: lint on `matches` and pattern matching Fixes #8010 Previously this lint only worked specifically for a very limited set of methods on the filter call (`.filter(|opt| opt.is_some())` and `.filter(|res| res.is_ok())`). This PR extends it to also recognize `matches!` in the `filter` and pattern matching with `if let` or `match` in the `map`. Example: ```rs enum Enum { A(i32), B, } let _ = [Enum::A(123), Enum::B].into_iter() .filter(|x| matches!(x, Enum::A(_))) .map(|x| if let Enum::A(s) = x { s } else { unreachable!() }); ``` Now suggests: ```diff - .filter(|x| matches!(x, Enum::A(_))).map(if let Enum::A(s) = x { s } else { unreachable!() }) + .filter_map(|x| match x { Enum::A(s) => Some(s), _ => None }) ``` Adding this required a somewhat large change in code because it originally seemed to be specifically written with only method calls in the filter in mind, and `matches!` has different behavior in the map, so this new setup should make it possible to support more "generic" cases that need different handling for the filter and map calls. changelog: [`manual_filter_map`]: lint on `matches` and pattern matching (and some internal refactoring)
This commit is contained in:
commit
0b63e95dce
6 changed files with 441 additions and 60 deletions
|
|
@ -120,3 +120,27 @@ fn issue_8920() {
|
|||
.iter()
|
||||
.filter_map(|f| f.result_field.to_owned().ok());
|
||||
}
|
||||
|
||||
fn issue8010() {
|
||||
#[derive(Clone)]
|
||||
enum Enum {
|
||||
A(i32),
|
||||
B,
|
||||
}
|
||||
|
||||
let iter = [Enum::A(123), Enum::B].into_iter();
|
||||
|
||||
let _x = iter.clone().filter_map(|x| match x { Enum::A(s) => Some(s), _ => None });
|
||||
let _x = iter.clone().filter(|x| matches!(x, Enum::B)).map(|x| match x {
|
||||
Enum::A(s) => s,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
let _x = iter
|
||||
.clone()
|
||||
.filter_map(|x| match x { Enum::A(s) => Some(s), _ => None });
|
||||
#[allow(clippy::unused_unit)]
|
||||
let _x = iter
|
||||
.clone()
|
||||
.filter(|x| matches!(x, Enum::B))
|
||||
.map(|x| if let Enum::B = x { () } else { unreachable!() });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,3 +133,31 @@ fn issue_8920() {
|
|||
.filter(|f| f.result_field.is_ok())
|
||||
.map(|f| f.result_field.to_owned().unwrap());
|
||||
}
|
||||
|
||||
fn issue8010() {
|
||||
#[derive(Clone)]
|
||||
enum Enum {
|
||||
A(i32),
|
||||
B,
|
||||
}
|
||||
|
||||
let iter = [Enum::A(123), Enum::B].into_iter();
|
||||
|
||||
let _x = iter.clone().filter(|x| matches!(x, Enum::A(_))).map(|x| match x {
|
||||
Enum::A(s) => s,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
let _x = iter.clone().filter(|x| matches!(x, Enum::B)).map(|x| match x {
|
||||
Enum::A(s) => s,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
let _x = iter
|
||||
.clone()
|
||||
.filter(|x| matches!(x, Enum::A(_)))
|
||||
.map(|x| if let Enum::A(s) = x { s } else { unreachable!() });
|
||||
#[allow(clippy::unused_unit)]
|
||||
let _x = iter
|
||||
.clone()
|
||||
.filter(|x| matches!(x, Enum::B))
|
||||
.map(|x| if let Enum::B = x { () } else { unreachable!() });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
|||
LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:9:30
|
||||
|
|
||||
LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
|
||||
| ^^^^^^^^^^
|
||||
= note: `-D clippy::manual-filter-map` implied by `-D warnings`
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
|
|
@ -11,12 +16,24 @@ error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
|||
|
|
||||
LL | let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:12:31
|
||||
|
|
||||
LL | let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:15:19
|
||||
|
|
||||
LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_res(a).ok())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:15:31
|
||||
|
|
||||
LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:18:10
|
||||
|
|
@ -25,6 +42,12 @@ LL | .filter(|&x| to_ref(to_opt(x)).is_some())
|
|||
| __________^
|
||||
LL | | .map(|y| to_ref(to_opt(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:18:22
|
||||
|
|
||||
LL | .filter(|&x| to_ref(to_opt(x)).is_some())
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:21:10
|
||||
|
|
@ -33,6 +56,12 @@ LL | .filter(|x| to_ref(to_opt(*x)).is_some())
|
|||
| __________^
|
||||
LL | | .map(|y| to_ref(to_opt(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:21:21
|
||||
|
|
||||
LL | .filter(|x| to_ref(to_opt(*x)).is_some())
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:25:10
|
||||
|
|
@ -41,6 +70,12 @@ LL | .filter(|&x| to_ref(to_res(x)).is_ok())
|
|||
| __________^
|
||||
LL | | .map(|y| to_ref(to_res(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:25:22
|
||||
|
|
||||
LL | .filter(|&x| to_ref(to_res(x)).is_ok())
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:28:10
|
||||
|
|
@ -49,6 +84,12 @@ LL | .filter(|x| to_ref(to_res(*x)).is_ok())
|
|||
| __________^
|
||||
LL | | .map(|y| to_ref(to_res(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:28:21
|
||||
|
|
||||
LL | .filter(|x| to_ref(to_res(*x)).is_ok())
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:34:27
|
||||
|
|
@ -75,6 +116,12 @@ error: `find(..).map(..)` can be simplified as `find_map(..)`
|
|||
|
|
||||
LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:37:41
|
||||
|
|
||||
LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:39:30
|
||||
|
|
@ -117,6 +164,12 @@ error: `find(..).map(..)` can be simplified as `find_map(..)`
|
|||
|
|
||||
LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:45:45
|
||||
|
|
||||
LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:93:10
|
||||
|
|
@ -190,5 +243,23 @@ LL | .filter(|f| f.result_field.is_ok())
|
|||
LL | | .map(|f| f.result_field.to_owned().unwrap());
|
||||
| |____________________________________________________^ help: try: `filter_map(|f| f.result_field.to_owned().ok())`
|
||||
|
||||
error: aborting due to 27 previous errors
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:146:27
|
||||
|
|
||||
LL | let _x = iter.clone().filter(|x| matches!(x, Enum::A(_))).map(|x| match x {
|
||||
| ___________________________^
|
||||
LL | | Enum::A(s) => s,
|
||||
LL | | _ => unreachable!(),
|
||||
LL | | });
|
||||
| |______^ help: try: `filter_map(|x| match x { Enum::A(s) => Some(s), _ => None })`
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:156:10
|
||||
|
|
||||
LL | .filter(|x| matches!(x, Enum::A(_)))
|
||||
| __________^
|
||||
LL | | .map(|x| if let Enum::A(s) = x { s } else { unreachable!() });
|
||||
| |_____________________________________________________________________^ help: try: `filter_map(|x| match x { Enum::A(s) => Some(s), _ => None })`
|
||||
|
||||
error: aborting due to 29 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ error: `find(..).map(..)` can be simplified as `find_map(..)`
|
|||
LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:9:28
|
||||
|
|
||||
LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
|
||||
| ^^^^^^^^^^
|
||||
= note: `-D clippy::manual-find-map` implied by `-D warnings`
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
|
|
@ -11,12 +16,24 @@ error: `find(..).map(..)` can be simplified as `find_map(..)`
|
|||
|
|
||||
LL | let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:12:29
|
||||
|
|
||||
LL | let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:15:19
|
||||
|
|
||||
LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_res(a).ok())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:15:29
|
||||
|
|
||||
LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:18:10
|
||||
|
|
@ -25,6 +42,12 @@ LL | .find(|&x| to_ref(to_opt(x)).is_some())
|
|||
| __________^
|
||||
LL | | .map(|y| to_ref(to_opt(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:18:20
|
||||
|
|
||||
LL | .find(|&x| to_ref(to_opt(x)).is_some())
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:21:10
|
||||
|
|
@ -33,6 +56,12 @@ LL | .find(|x| to_ref(to_opt(*x)).is_some())
|
|||
| __________^
|
||||
LL | | .map(|y| to_ref(to_opt(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:21:19
|
||||
|
|
||||
LL | .find(|x| to_ref(to_opt(*x)).is_some())
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:25:10
|
||||
|
|
@ -41,6 +70,12 @@ LL | .find(|&x| to_ref(to_res(x)).is_ok())
|
|||
| __________^
|
||||
LL | | .map(|y| to_ref(to_res(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:25:20
|
||||
|
|
||||
LL | .find(|&x| to_ref(to_res(x)).is_ok())
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:28:10
|
||||
|
|
@ -49,6 +84,12 @@ LL | .find(|x| to_ref(to_res(*x)).is_ok())
|
|||
| __________^
|
||||
LL | | .map(|y| to_ref(to_res(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:28:19
|
||||
|
|
||||
LL | .find(|x| to_ref(to_res(*x)).is_ok())
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:34:26
|
||||
|
|
@ -91,6 +132,12 @@ error: `find(..).map(..)` can be simplified as `find_map(..)`
|
|||
|
|
||||
LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:40:41
|
||||
|
|
||||
LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:42:30
|
||||
|
|
@ -133,6 +180,12 @@ error: `find(..).map(..)` can be simplified as `find_map(..)`
|
|||
|
|
||||
LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:48:45
|
||||
|
|
||||
LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:96:10
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue