Add manual_filter lint for Option

Share much of its implementation with `manual_map` and should greatly benefit from its previous feedback.
This commit is contained in:
kraktus 2022-10-03 13:57:42 +02:00
parent 18e10ca290
commit 0958f9486b
13 changed files with 951 additions and 67 deletions

View file

@ -0,0 +1,119 @@
// run-rustfix
#![warn(clippy::manual_filter)]
#![allow(unused_variables, dead_code)]
fn main() {
Some(0).filter(|&x| x <= 0);
Some(1).filter(|&x| x <= 0);
Some(2).filter(|&x| x <= 0);
Some(3).filter(|&x| x > 0);
let y = Some(4);
y.filter(|&x| x <= 0);
Some(5).filter(|&x| x > 0);
Some(6).as_ref().filter(|&x| x > &0);
let external_cond = true;
Some(String::new()).filter(|x| external_cond);
Some(7).filter(|&x| external_cond);
Some(8).filter(|&x| x != 0);
Some(9).filter(|&x| x > 10 && x < 100);
const fn f1() {
// Don't lint, `.filter` is not const
match Some(10) {
Some(x) => {
if x > 10 && x < 100 {
Some(x)
} else {
None
}
},
None => None,
};
}
#[allow(clippy::blocks_in_if_conditions)]
Some(11).filter(|&x| {
println!("foo");
x > 10 && x < 100
});
match Some(12) {
// Don't Lint, statement is lost by `.filter`
Some(x) => {
if x > 10 && x < 100 {
println!("foo");
Some(x)
} else {
None
}
},
None => None,
};
match Some(13) {
// Don't Lint, because of `None => Some(1)`
Some(x) => {
if x > 10 && x < 100 {
println!("foo");
Some(x)
} else {
None
}
},
None => Some(1),
};
unsafe fn f(x: u32) -> bool {
true
}
let _ = Some(14).filter(|&x| unsafe { f(x) });
let _ = Some(15).filter(|&x| unsafe { f(x) });
#[allow(clippy::redundant_pattern_matching)]
if let Some(_) = Some(16) {
Some(16)
} else { Some(16).filter(|&x| x % 2 == 0) };
match Some((17, 17)) {
// Not linted for now could be
Some((x, y)) => {
if y != x {
Some((x, y))
} else {
None
}
},
None => None,
};
struct NamedTuple {
pub x: u8,
pub y: (i32, u32),
}
match Some(NamedTuple {
// Not linted for now could be
x: 17,
y: (18, 19),
}) {
Some(NamedTuple { x, y }) => {
if y.1 != x as u32 {
Some(NamedTuple { x, y })
} else {
None
}
},
None => None,
};
}

243
tests/ui/manual_filter.rs Normal file
View file

@ -0,0 +1,243 @@
// run-rustfix
#![warn(clippy::manual_filter)]
#![allow(unused_variables, dead_code)]
fn main() {
match Some(0) {
None => None,
Some(x) => {
if x > 0 {
None
} else {
Some(x)
}
},
};
match Some(1) {
Some(x) => {
if x > 0 {
None
} else {
Some(x)
}
},
None => None,
};
match Some(2) {
Some(x) => {
if x > 0 {
None
} else {
Some(x)
}
},
_ => None,
};
match Some(3) {
Some(x) => {
if x > 0 {
Some(x)
} else {
None
}
},
None => None,
};
let y = Some(4);
match y {
// Some(4)
None => None,
Some(x) => {
if x > 0 {
None
} else {
Some(x)
}
},
};
match Some(5) {
Some(x) => {
if x > 0 {
Some(x)
} else {
None
}
},
_ => None,
};
match Some(6) {
Some(ref x) => {
if x > &0 {
Some(x)
} else {
None
}
},
_ => None,
};
let external_cond = true;
match Some(String::new()) {
Some(x) => {
if external_cond {
Some(x)
} else {
None
}
},
_ => None,
};
if let Some(x) = Some(7) {
if external_cond { Some(x) } else { None }
} else {
None
};
match &Some(8) {
&Some(x) => {
if x != 0 {
Some(x)
} else {
None
}
},
_ => None,
};
match Some(9) {
Some(x) => {
if x > 10 && x < 100 {
Some(x)
} else {
None
}
},
None => None,
};
const fn f1() {
// Don't lint, `.filter` is not const
match Some(10) {
Some(x) => {
if x > 10 && x < 100 {
Some(x)
} else {
None
}
},
None => None,
};
}
#[allow(clippy::blocks_in_if_conditions)]
match Some(11) {
// Lint, statement is preserved by `.filter`
Some(x) => {
if {
println!("foo");
x > 10 && x < 100
} {
Some(x)
} else {
None
}
},
None => None,
};
match Some(12) {
// Don't Lint, statement is lost by `.filter`
Some(x) => {
if x > 10 && x < 100 {
println!("foo");
Some(x)
} else {
None
}
},
None => None,
};
match Some(13) {
// Don't Lint, because of `None => Some(1)`
Some(x) => {
if x > 10 && x < 100 {
println!("foo");
Some(x)
} else {
None
}
},
None => Some(1),
};
unsafe fn f(x: u32) -> bool {
true
}
let _ = match Some(14) {
Some(x) => {
if unsafe { f(x) } {
Some(x)
} else {
None
}
},
None => None,
};
let _ = match Some(15) {
Some(x) => unsafe {
if f(x) { Some(x) } else { None }
},
None => None,
};
#[allow(clippy::redundant_pattern_matching)]
if let Some(_) = Some(16) {
Some(16)
} else if let Some(x) = Some(16) {
// Lint starting from here
if x % 2 == 0 { Some(x) } else { None }
} else {
None
};
match Some((17, 17)) {
// Not linted for now could be
Some((x, y)) => {
if y != x {
Some((x, y))
} else {
None
}
},
None => None,
};
struct NamedTuple {
pub x: u8,
pub y: (i32, u32),
}
match Some(NamedTuple {
// Not linted for now could be
x: 17,
y: (18, 19),
}) {
Some(NamedTuple { x, y }) => {
if y.1 != x as u32 {
Some(NamedTuple { x, y })
} else {
None
}
},
None => None,
};
}

View file

@ -0,0 +1,191 @@
error: manual implementation of `Option::filter`
--> $DIR/manual_filter.rs:7:5
|
LL | / match Some(0) {
LL | | None => None,
LL | | Some(x) => {
LL | | if x > 0 {
... |
LL | | },
LL | | };
| |_____^ help: try this: `Some(0).filter(|&x| x <= 0)`
|
= note: `-D clippy::manual-filter` implied by `-D warnings`
error: manual implementation of `Option::filter`
--> $DIR/manual_filter.rs:18:5
|
LL | / match Some(1) {
LL | | Some(x) => {
LL | | if x > 0 {
LL | | None
... |
LL | | None => None,
LL | | };
| |_____^ help: try this: `Some(1).filter(|&x| x <= 0)`
error: manual implementation of `Option::filter`
--> $DIR/manual_filter.rs:29:5
|
LL | / match Some(2) {
LL | | Some(x) => {
LL | | if x > 0 {
LL | | None
... |
LL | | _ => None,
LL | | };
| |_____^ help: try this: `Some(2).filter(|&x| x <= 0)`
error: manual implementation of `Option::filter`
--> $DIR/manual_filter.rs:40:5
|
LL | / match Some(3) {
LL | | Some(x) => {
LL | | if x > 0 {
LL | | Some(x)
... |
LL | | None => None,
LL | | };
| |_____^ help: try this: `Some(3).filter(|&x| x > 0)`
error: manual implementation of `Option::filter`
--> $DIR/manual_filter.rs:52:5
|
LL | / match y {
LL | | // Some(4)
LL | | None => None,
LL | | Some(x) => {
... |
LL | | },
LL | | };
| |_____^ help: try this: `y.filter(|&x| x <= 0)`
error: manual implementation of `Option::filter`
--> $DIR/manual_filter.rs:64:5
|
LL | / match Some(5) {
LL | | Some(x) => {
LL | | if x > 0 {
LL | | Some(x)
... |
LL | | _ => None,
LL | | };
| |_____^ help: try this: `Some(5).filter(|&x| x > 0)`
error: manual implementation of `Option::filter`
--> $DIR/manual_filter.rs:75:5
|
LL | / match Some(6) {
LL | | Some(ref x) => {
LL | | if x > &0 {
LL | | Some(x)
... |
LL | | _ => None,
LL | | };
| |_____^ help: try this: `Some(6).as_ref().filter(|&x| x > &0)`
error: manual implementation of `Option::filter`
--> $DIR/manual_filter.rs:87:5
|
LL | / match Some(String::new()) {
LL | | Some(x) => {
LL | | if external_cond {
LL | | Some(x)
... |
LL | | _ => None,
LL | | };
| |_____^ help: try this: `Some(String::new()).filter(|x| external_cond)`
error: manual implementation of `Option::filter`
--> $DIR/manual_filter.rs:98:5
|
LL | / if let Some(x) = Some(7) {
LL | | if external_cond { Some(x) } else { None }
LL | | } else {
LL | | None
LL | | };
| |_____^ help: try this: `Some(7).filter(|&x| external_cond)`
error: manual implementation of `Option::filter`
--> $DIR/manual_filter.rs:104:5
|
LL | / match &Some(8) {
LL | | &Some(x) => {
LL | | if x != 0 {
LL | | Some(x)
... |
LL | | _ => None,
LL | | };
| |_____^ help: try this: `Some(8).filter(|&x| x != 0)`
error: manual implementation of `Option::filter`
--> $DIR/manual_filter.rs:115:5
|
LL | / match Some(9) {
LL | | Some(x) => {
LL | | if x > 10 && x < 100 {
LL | | Some(x)
... |
LL | | None => None,
LL | | };
| |_____^ help: try this: `Some(9).filter(|&x| x > 10 && x < 100)`
error: manual implementation of `Option::filter`
--> $DIR/manual_filter.rs:141:5
|
LL | / match Some(11) {
LL | | // Lint, statement is preserved by `.filter`
LL | | Some(x) => {
LL | | if {
... |
LL | | None => None,
LL | | };
| |_____^
|
help: try this
|
LL ~ Some(11).filter(|&x| {
LL + println!("foo");
LL + x > 10 && x < 100
LL ~ });
|
error: manual implementation of `Option::filter`
--> $DIR/manual_filter.rs:185:13
|
LL | let _ = match Some(14) {
| _____________^
LL | | Some(x) => {
LL | | if unsafe { f(x) } {
LL | | Some(x)
... |
LL | | None => None,
LL | | };
| |_____^ help: try this: `Some(14).filter(|&x| unsafe { f(x) })`
error: manual implementation of `Option::filter`
--> $DIR/manual_filter.rs:195:13
|
LL | let _ = match Some(15) {
| _____________^
LL | | Some(x) => unsafe {
LL | | if f(x) { Some(x) } else { None }
LL | | },
LL | | None => None,
LL | | };
| |_____^ help: try this: `Some(15).filter(|&x| unsafe { f(x) })`
error: manual implementation of `Option::filter`
--> $DIR/manual_filter.rs:205:12
|
LL | } else if let Some(x) = Some(16) {
| ____________^
LL | | // Lint starting from here
LL | | if x % 2 == 0 { Some(x) } else { None }
LL | | } else {
LL | | None
LL | | };
| |_____^ help: try this: `{ Some(16).filter(|&x| x % 2 == 0) }`
error: aborting due to 15 previous errors