Skip to content

Commit c23b375

Browse files
committed
Auto merge of #4537 - mikerite:unneeded_wildcard_pattern, r=phansch
Add `unneeded-wildcard-pattern` lint changelog: Add `unneeded-wildcard-pattern` lint
2 parents 157edad + ca6d36b commit c23b375

11 files changed

+290
-6
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,7 @@ Released 2018-09-13
11901190
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
11911191
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
11921192
[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
1193+
[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
11931194
[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
11941195
[`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name
11951196
[`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization

README.md

+1-1
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 315 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 316 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/eval_order_dependence.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ struct DivergenceVisitor<'a, 'tcx> {
101101
impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
102102
fn maybe_walk_expr(&mut self, e: &'tcx Expr) {
103103
match e.node {
104-
ExprKind::Closure(.., _) => {},
104+
ExprKind::Closure(..) => {},
105105
ExprKind::Match(ref e, ref arms, _) => {
106106
self.visit_expr(e);
107107
for arm in arms {

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
828828
misc_early::REDUNDANT_CLOSURE_CALL,
829829
misc_early::REDUNDANT_PATTERN,
830830
misc_early::UNNEEDED_FIELD_PATTERN,
831+
misc_early::UNNEEDED_WILDCARD_PATTERN,
831832
misc_early::ZERO_PREFIXED_LITERAL,
832833
mut_reference::UNNECESSARY_MUT_PASSED,
833834
mutex_atomic::MUTEX_ATOMIC,
@@ -1047,6 +1048,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
10471048
methods::USELESS_ASREF,
10481049
misc::SHORT_CIRCUIT_STATEMENT,
10491050
misc_early::REDUNDANT_CLOSURE_CALL,
1051+
misc_early::UNNEEDED_WILDCARD_PATTERN,
10501052
misc_early::ZERO_PREFIXED_LITERAL,
10511053
needless_bool::BOOL_COMPARISON,
10521054
needless_bool::NEEDLESS_BOOL,

clippy_lints/src/misc_early.rs

+93-1
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,44 @@ 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 (`..`).
201+
///
202+
/// _NOTE_: While `_, ..` means there is at least one element left, `..`
203+
/// means there are 0 or more elements left. This can make a difference
204+
/// when refactoring, but shouldn't result in errors in the refactored code,
205+
/// since the wildcard pattern isn't used anyway.
206+
/// **Why is this bad?** The wildcard pattern is unneeded as the rest pattern
207+
/// can match that element as well.
208+
///
209+
/// **Known problems:** None.
210+
///
211+
/// **Example:**
212+
/// ```rust
213+
/// # struct TupleStruct(u32, u32, u32);
214+
/// # let t = TupleStruct(1, 2, 3);
215+
///
216+
/// match t {
217+
/// TupleStruct(0, .., _) => (),
218+
/// _ => (),
219+
/// }
220+
/// ```
221+
/// can be written as
222+
/// ```rust
223+
/// # struct TupleStruct(u32, u32, u32);
224+
/// # let t = TupleStruct(1, 2, 3);
225+
///
226+
/// match t {
227+
/// TupleStruct(0, ..) => (),
228+
/// _ => (),
229+
/// }
230+
/// ```
231+
pub UNNEEDED_WILDCARD_PATTERN,
232+
complexity,
233+
"tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
234+
}
235+
198236
declare_lint_pass!(MiscEarlyLints => [
199237
UNNEEDED_FIELD_PATTERN,
200238
DUPLICATE_UNDERSCORE_ARGUMENT,
@@ -204,7 +242,8 @@ declare_lint_pass!(MiscEarlyLints => [
204242
UNSEPARATED_LITERAL_SUFFIX,
205243
ZERO_PREFIXED_LITERAL,
206244
BUILTIN_TYPE_SHADOW,
207-
REDUNDANT_PATTERN
245+
REDUNDANT_PATTERN,
246+
UNNEEDED_WILDCARD_PATTERN,
208247
]);
209248

210249
// Used to find `return` statements or equivalents e.g., `?`
@@ -326,6 +365,8 @@ impl EarlyLintPass for MiscEarlyLints {
326365
);
327366
}
328367
}
368+
369+
check_unneeded_wildcard_pattern(cx, pat);
329370
}
330371

331372
fn check_fn(&mut self, cx: &EarlyContext<'_>, _: FnKind<'_>, decl: &FnDecl, _: Span, _: NodeId) {
@@ -520,3 +561,54 @@ impl MiscEarlyLints {
520561
}
521562
}
522563
}
564+
565+
fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) {
566+
if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.node {
567+
fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
568+
span_lint_and_sugg(
569+
cx,
570+
UNNEEDED_WILDCARD_PATTERN,
571+
span,
572+
if only_one {
573+
"this pattern is unneeded as the `..` pattern can match that element"
574+
} else {
575+
"these patterns are unneeded as the `..` pattern can match those elements"
576+
},
577+
if only_one { "remove it" } else { "remove them" },
578+
"".to_string(),
579+
Applicability::MachineApplicable,
580+
);
581+
}
582+
583+
#[allow(clippy::trivially_copy_pass_by_ref)]
584+
fn is_wild<P: std::ops::Deref<Target = Pat>>(pat: &&P) -> bool {
585+
if let PatKind::Wild = pat.node {
586+
true
587+
} else {
588+
false
589+
}
590+
}
591+
592+
if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) {
593+
if let Some((left_index, left_pat)) = patterns[..rest_index]
594+
.iter()
595+
.rev()
596+
.take_while(is_wild)
597+
.enumerate()
598+
.last()
599+
{
600+
span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
601+
}
602+
603+
if let Some((right_index, right_pat)) =
604+
patterns[rest_index + 1..].iter().take_while(is_wild).enumerate().last()
605+
{
606+
span_lint(
607+
cx,
608+
patterns[rest_index].span.shrink_to_hi().to(right_pat.span),
609+
right_index == 0,
610+
);
611+
}
612+
}
613+
}
614+
}

clippy_lints/src/no_effect.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ fn has_no_effect(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
4747
return false;
4848
}
4949
match expr.node {
50-
ExprKind::Lit(..) | ExprKind::Closure(.., _) => true,
50+
ExprKind::Lit(..) | ExprKind::Closure(..) => true,
5151
ExprKind::Path(..) => !has_drop(cx, cx.tables.expr_ty(expr)),
5252
ExprKind::Index(ref a, ref b) | ExprKind::Binary(_, ref a, ref b) => {
5353
has_no_effect(cx, a) && has_no_effect(cx, b)

clippy_lints/src/utils/sugg.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ impl<'a> Sugg<'a> {
9393
match expr.node {
9494
hir::ExprKind::AddrOf(..)
9595
| hir::ExprKind::Box(..)
96-
| hir::ExprKind::Closure(.., _)
96+
| hir::ExprKind::Closure(..)
9797
| hir::ExprKind::Unary(..)
9898
| hir::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
9999
hir::ExprKind::Continue(..)

src/lintlist/mod.rs

+8-1
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; 315] = [
9+
pub const ALL_LINTS: [Lint; 316] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -1988,6 +1988,13 @@ pub const ALL_LINTS: [Lint; 315] = [
19881988
deprecation: None,
19891989
module: "misc_early",
19901990
},
1991+
Lint {
1992+
name: "unneeded_wildcard_pattern",
1993+
group: "complexity",
1994+
desc: "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)",
1995+
deprecation: None,
1996+
module: "misc_early",
1997+
},
19911998
Lint {
19921999
name: "unreadable_literal",
19932000
group: "style",
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
if let (0, ..) = t {};
18+
if let (.., 0) = t {};
19+
20+
#[rustfmt::skip]
21+
{
22+
if let (0, ..,) = t {};
23+
}
24+
25+
struct S(usize, usize, usize, usize);
26+
27+
let s = S(0, 1, 2, 3);
28+
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+
if let S(.., 0, _) = s {};
37+
if let S(0, _, _, _) = s {};
38+
if let S(0, ..) = s {};
39+
if let S(.., 0) = s {};
40+
41+
#[rustfmt::skip]
42+
{
43+
if let S(0, ..,) = s {};
44+
}
45+
}

tests/ui/unneeded_wildcard_pattern.rs

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
if let (0, ..) = t {};
18+
if let (.., 0) = t {};
19+
20+
#[rustfmt::skip]
21+
{
22+
if let (0, .., _, _,) = t {};
23+
}
24+
25+
struct S(usize, usize, usize, usize);
26+
27+
let s = S(0, 1, 2, 3);
28+
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+
if let S(.., 0, _) = s {};
37+
if let S(0, _, _, _) = s {};
38+
if let S(0, ..) = s {};
39+
if let S(.., 0) = s {};
40+
41+
#[rustfmt::skip]
42+
{
43+
if let S(0, .., _, _,) = s {};
44+
}
45+
}
+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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: this pattern is unneeded as the `..` pattern can match that element
20+
--> $DIR/unneeded_wildcard_pattern.rs:10:13
21+
|
22+
LL | if let (_, .., 0) = t {};
23+
| ^^^ help: remove it
24+
25+
error: this pattern is unneeded as the `..` pattern can match that element
26+
--> $DIR/unneeded_wildcard_pattern.rs:11:15
27+
|
28+
LL | if let (.., _, 0) = t {};
29+
| ^^^ help: remove it
30+
31+
error: these patterns are unneeded as the `..` pattern can match those elements
32+
--> $DIR/unneeded_wildcard_pattern.rs:12:16
33+
|
34+
LL | if let (0, _, _, ..) = t {};
35+
| ^^^^^^ help: remove them
36+
37+
error: these patterns are unneeded as the `..` pattern can match those elements
38+
--> $DIR/unneeded_wildcard_pattern.rs:13:18
39+
|
40+
LL | if let (0, .., _, _) = t {};
41+
| ^^^^^^ help: remove them
42+
43+
error: these patterns are unneeded as the `..` pattern can match those elements
44+
--> $DIR/unneeded_wildcard_pattern.rs:22:22
45+
|
46+
LL | if let (0, .., _, _,) = t {};
47+
| ^^^^^^ help: remove them
48+
49+
error: this pattern is unneeded as the `..` pattern can match that element
50+
--> $DIR/unneeded_wildcard_pattern.rs:29:19
51+
|
52+
LL | if let S(0, .., _) = s {};
53+
| ^^^ help: remove it
54+
55+
error: this pattern is unneeded as the `..` pattern can match that element
56+
--> $DIR/unneeded_wildcard_pattern.rs:30:17
57+
|
58+
LL | if let S(0, _, ..) = s {};
59+
| ^^^ help: remove it
60+
61+
error: this pattern is unneeded as the `..` pattern can match that element
62+
--> $DIR/unneeded_wildcard_pattern.rs:31:14
63+
|
64+
LL | if let S(_, .., 0) = s {};
65+
| ^^^ help: remove it
66+
67+
error: this pattern is unneeded as the `..` pattern can match that element
68+
--> $DIR/unneeded_wildcard_pattern.rs:32:16
69+
|
70+
LL | if let S(.., _, 0) = s {};
71+
| ^^^ help: remove it
72+
73+
error: these patterns are unneeded as the `..` pattern can match those elements
74+
--> $DIR/unneeded_wildcard_pattern.rs:33:17
75+
|
76+
LL | if let S(0, _, _, ..) = s {};
77+
| ^^^^^^ help: remove them
78+
79+
error: these patterns are unneeded as the `..` pattern can match those elements
80+
--> $DIR/unneeded_wildcard_pattern.rs:34:19
81+
|
82+
LL | if let S(0, .., _, _) = s {};
83+
| ^^^^^^ help: remove them
84+
85+
error: these patterns are unneeded as the `..` pattern can match those elements
86+
--> $DIR/unneeded_wildcard_pattern.rs:43:23
87+
|
88+
LL | if let S(0, .., _, _,) = s {};
89+
| ^^^^^^ help: remove them
90+
91+
error: aborting due to 14 previous errors
92+

0 commit comments

Comments
 (0)