Skip to content

Commit 4a3bc6b

Browse files
author
Michael Wright
committed
Add unneeded-wildcard-pattern lint
1 parent 6ca5b20 commit 4a3bc6b

File tree

8 files changed

+254
-3
lines changed

8 files changed

+254
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,7 @@ Released 2018-09-13
11881188
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
11891189
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
11901190
[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
1191+
[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
11911192
[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
11921193
[`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name
11931194
[`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are 313 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 314 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1212

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
825825
misc_early::REDUNDANT_CLOSURE_CALL,
826826
misc_early::REDUNDANT_PATTERN,
827827
misc_early::UNNEEDED_FIELD_PATTERN,
828+
misc_early::UNNEEDED_WILDCARD_PATTERN,
828829
misc_early::ZERO_PREFIXED_LITERAL,
829830
mut_reference::UNNECESSARY_MUT_PASSED,
830831
mutex_atomic::MUTEX_ATOMIC,
@@ -1044,6 +1045,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
10441045
methods::USELESS_ASREF,
10451046
misc::SHORT_CIRCUIT_STATEMENT,
10461047
misc_early::REDUNDANT_CLOSURE_CALL,
1048+
misc_early::UNNEEDED_WILDCARD_PATTERN,
10471049
misc_early::ZERO_PREFIXED_LITERAL,
10481050
needless_bool::BOOL_COMPARISON,
10491051
needless_bool::NEEDLESS_BOOL,

clippy_lints/src/misc_early.rs

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,40 @@ declare_clippy_lint! {
195195
"using `name @ _` in a pattern"
196196
}
197197

198+
declare_clippy_lint! {
199+
/// **What it does:** Checks for tuple patterns with a wildcard
200+
/// pattern (`_`) is next to a rest pattern (`..`) pattern.
201+
///
202+
/// **Why is this bad?** The wildcard pattern is unneeded as the rest pattern
203+
/// can match that element as well.
204+
///
205+
/// **Known problems:** None.
206+
///
207+
/// **Example:**
208+
/// ```rust
209+
/// # struct TupleStruct(u32, u32, u32);
210+
/// # let t = TupleStruct(1, 2, 3);
211+
///
212+
/// match t {
213+
/// TupleStruct(0, .., _) => (),
214+
/// _ => (),
215+
/// }
216+
/// ```
217+
/// can be written as
218+
/// ```rust
219+
/// # struct TupleStruct(u32, u32, u32);
220+
/// # let t = TupleStruct(1, 2, 3);
221+
///
222+
/// match t {
223+
/// TupleStruct(0, ..) => (),
224+
/// _ => (),
225+
/// }
226+
/// ```
227+
pub UNNEEDED_WILDCARD_PATTERN,
228+
complexity,
229+
"tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`) pattern"
230+
}
231+
198232
declare_lint_pass!(MiscEarlyLints => [
199233
UNNEEDED_FIELD_PATTERN,
200234
DUPLICATE_UNDERSCORE_ARGUMENT,
@@ -204,7 +238,8 @@ declare_lint_pass!(MiscEarlyLints => [
204238
UNSEPARATED_LITERAL_SUFFIX,
205239
ZERO_PREFIXED_LITERAL,
206240
BUILTIN_TYPE_SHADOW,
207-
REDUNDANT_PATTERN
241+
REDUNDANT_PATTERN,
242+
UNNEEDED_WILDCARD_PATTERN,
208243
]);
209244

210245
// Used to find `return` statements or equivalents e.g., `?`
@@ -326,6 +361,62 @@ impl EarlyLintPass for MiscEarlyLints {
326361
);
327362
}
328363
}
364+
365+
if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.node {
366+
fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
367+
span_lint_and_sugg(
368+
cx,
369+
UNNEEDED_WILDCARD_PATTERN,
370+
span,
371+
if only_one {
372+
"this pattern is unneeded as the `..` pattern can match that element"
373+
} else {
374+
"these patterns are unneeded as the `..` pattern can match those elements"
375+
},
376+
if only_one { "remove it" } else { "remove them" },
377+
"".to_string(),
378+
Applicability::MachineApplicable,
379+
);
380+
}
381+
382+
fn is_rest<P: std::ops::Deref<Target = Pat>>(pat: &P) -> bool {
383+
if let PatKind::Rest = pat.node {
384+
true
385+
} else {
386+
false
387+
}
388+
}
389+
390+
fn is_wild<P: std::ops::Deref<Target = Pat>>(pat: &&P) -> bool {
391+
if let PatKind::Wild = pat.node {
392+
true
393+
} else {
394+
false
395+
}
396+
}
397+
398+
if let Some(rest_index) = patterns.iter().position(is_rest) {
399+
if let Some((left_index, left_pat)) = patterns[..rest_index]
400+
.iter()
401+
.rev()
402+
.take_while(is_wild)
403+
.enumerate()
404+
.last()
405+
{
406+
span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
407+
}
408+
409+
if let Some((right_index, right_pat)) =
410+
patterns[rest_index + 1..].iter().take_while(is_wild).enumerate().last()
411+
{
412+
span_lint(
413+
cx,
414+
patterns[rest_index].span.shrink_to_hi().to(right_pat.span),
415+
right_index == 0,
416+
);
417+
}
418+
}
419+
}
329420
}
330421

331422
fn check_fn(&mut self, cx: &EarlyContext<'_>, _: FnKind<'_>, decl: &FnDecl, _: Span, _: NodeId) {

src/lintlist/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 313] = [
9+
pub const ALL_LINTS: [Lint; 314] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -1974,6 +1974,13 @@ pub const ALL_LINTS: [Lint; 313] = [
19741974
deprecation: None,
19751975
module: "misc_early",
19761976
},
1977+
Lint {
1978+
name: "unneeded_wildcard_pattern",
1979+
group: "complexity",
1980+
desc: "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`) pattern",
1981+
deprecation: None,
1982+
module: "misc_early",
1983+
},
19771984
Lint {
19781985
name: "unreadable_literal",
19791986
group: "style",
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// run-rustfix
2+
#![feature(stmt_expr_attributes)]
3+
#![deny(clippy::unneeded_wildcard_pattern)]
4+
5+
fn main() {
6+
let t = (0, 1, 2, 3);
7+
8+
if let (0, ..) = t {};
9+
if let (0, ..) = t {};
10+
if let (0, ..) = t {};
11+
if let (0, ..) = t {};
12+
if let (_, 0, ..) = t {};
13+
if let (.., 0, _) = t {};
14+
if let (0, _, _, _) = t {};
15+
if let (0, ..) = t {};
16+
if let (.., 0) = t {};
17+
18+
#[rustfmt::skip]
19+
{
20+
if let (0, ..,) = t {};
21+
}
22+
23+
struct S(usize, usize, usize, usize);
24+
25+
let s = S(0, 1, 2, 3);
26+
27+
if let S(0, ..) = s {};
28+
if let S(0, ..) = s {};
29+
if let S(0, ..) = s {};
30+
if let S(0, ..) = s {};
31+
if let S(_, 0, ..) = s {};
32+
if let S(.., 0, _) = s {};
33+
if let S(0, _, _, _) = s {};
34+
if let S(0, ..) = s {};
35+
if let S(.., 0) = s {};
36+
37+
#[rustfmt::skip]
38+
{
39+
if let S(0, ..,) = s {};
40+
}
41+
}

tests/ui/unneeded_wildcard_pattern.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// run-rustfix
2+
#![feature(stmt_expr_attributes)]
3+
#![deny(clippy::unneeded_wildcard_pattern)]
4+
5+
fn main() {
6+
let t = (0, 1, 2, 3);
7+
8+
if let (0, .., _) = t {};
9+
if let (0, _, ..) = t {};
10+
if let (0, _, _, ..) = t {};
11+
if let (0, .., _, _) = t {};
12+
if let (_, 0, ..) = t {};
13+
if let (.., 0, _) = t {};
14+
if let (0, _, _, _) = t {};
15+
if let (0, ..) = t {};
16+
if let (.., 0) = t {};
17+
18+
#[rustfmt::skip]
19+
{
20+
if let (0, .., _, _,) = t {};
21+
}
22+
23+
struct S(usize, usize, usize, usize);
24+
25+
let s = S(0, 1, 2, 3);
26+
27+
if let S(0, .., _) = s {};
28+
if let S(0, _, ..) = s {};
29+
if let S(0, _, _, ..) = s {};
30+
if let S(0, .., _, _) = s {};
31+
if let S(_, 0, ..) = s {};
32+
if let S(.., 0, _) = s {};
33+
if let S(0, _, _, _) = s {};
34+
if let S(0, ..) = s {};
35+
if let S(.., 0) = s {};
36+
37+
#[rustfmt::skip]
38+
{
39+
if let S(0, .., _, _,) = s {};
40+
}
41+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
error: this pattern is unneeded as the `..` pattern can match that element
2+
--> $DIR/unneeded_wildcard_pattern.rs:8:18
3+
|
4+
LL | if let (0, .., _) = t {};
5+
| ^^^ help: remove it
6+
|
7+
note: lint level defined here
8+
--> $DIR/unneeded_wildcard_pattern.rs:3:9
9+
|
10+
LL | #![deny(clippy::unneeded_wildcard_pattern)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: this pattern is unneeded as the `..` pattern can match that element
14+
--> $DIR/unneeded_wildcard_pattern.rs:9:16
15+
|
16+
LL | if let (0, _, ..) = t {};
17+
| ^^^ help: remove it
18+
19+
error: these patterns are unneeded as the `..` pattern can match those elements
20+
--> $DIR/unneeded_wildcard_pattern.rs:10:16
21+
|
22+
LL | if let (0, _, _, ..) = t {};
23+
| ^^^^^^ help: remove them
24+
25+
error: these patterns are unneeded as the `..` pattern can match those elements
26+
--> $DIR/unneeded_wildcard_pattern.rs:11:18
27+
|
28+
LL | if let (0, .., _, _) = t {};
29+
| ^^^^^^ help: remove them
30+
31+
error: these patterns are unneeded as the `..` pattern can match those elements
32+
--> $DIR/unneeded_wildcard_pattern.rs:20:22
33+
|
34+
LL | if let (0, .., _, _,) = t {};
35+
| ^^^^^^ help: remove them
36+
37+
error: this pattern is unneeded as the `..` pattern can match that element
38+
--> $DIR/unneeded_wildcard_pattern.rs:27:19
39+
|
40+
LL | if let S(0, .., _) = s {};
41+
| ^^^ help: remove it
42+
43+
error: this pattern is unneeded as the `..` pattern can match that element
44+
--> $DIR/unneeded_wildcard_pattern.rs:28:17
45+
|
46+
LL | if let S(0, _, ..) = s {};
47+
| ^^^ help: remove it
48+
49+
error: these patterns are unneeded as the `..` pattern can match those elements
50+
--> $DIR/unneeded_wildcard_pattern.rs:29:17
51+
|
52+
LL | if let S(0, _, _, ..) = s {};
53+
| ^^^^^^ help: remove them
54+
55+
error: these patterns are unneeded as the `..` pattern can match those elements
56+
--> $DIR/unneeded_wildcard_pattern.rs:30:19
57+
|
58+
LL | if let S(0, .., _, _) = s {};
59+
| ^^^^^^ help: remove them
60+
61+
error: these patterns are unneeded as the `..` pattern can match those elements
62+
--> $DIR/unneeded_wildcard_pattern.rs:39:23
63+
|
64+
LL | if let S(0, .., _, _,) = s {};
65+
| ^^^^^^ help: remove them
66+
67+
error: aborting due to 10 previous errors
68+

0 commit comments

Comments
 (0)