about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--README.md2
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/misc_early.rs93
-rw-r--r--src/lintlist/mod.rs9
-rw-r--r--tests/ui/unneeded_wildcard_pattern.fixed41
-rw-r--r--tests/ui/unneeded_wildcard_pattern.rs41
-rw-r--r--tests/ui/unneeded_wildcard_pattern.stderr68
8 files changed, 254 insertions, 3 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dbdf3df4ddc..eb710654caf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1188,6 +1188,7 @@ Released 2018-09-13
 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
 [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
 [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
+[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
 [`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
 [`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name
 [`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization
diff --git a/README.md b/README.md
index dd315fd397b..4541af9c844 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
 
 A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
 
-[There are 313 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
+[There are 314 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
 
 We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
 
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 9d0a91c5318..de4d262bddc 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -825,6 +825,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
         misc_early::REDUNDANT_CLOSURE_CALL,
         misc_early::REDUNDANT_PATTERN,
         misc_early::UNNEEDED_FIELD_PATTERN,
+        misc_early::UNNEEDED_WILDCARD_PATTERN,
         misc_early::ZERO_PREFIXED_LITERAL,
         mut_reference::UNNECESSARY_MUT_PASSED,
         mutex_atomic::MUTEX_ATOMIC,
@@ -1044,6 +1045,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
         methods::USELESS_ASREF,
         misc::SHORT_CIRCUIT_STATEMENT,
         misc_early::REDUNDANT_CLOSURE_CALL,
+        misc_early::UNNEEDED_WILDCARD_PATTERN,
         misc_early::ZERO_PREFIXED_LITERAL,
         needless_bool::BOOL_COMPARISON,
         needless_bool::NEEDLESS_BOOL,
diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs
index a9907ed132d..2e1be755d09 100644
--- a/clippy_lints/src/misc_early.rs
+++ b/clippy_lints/src/misc_early.rs
@@ -195,6 +195,40 @@ declare_clippy_lint! {
     "using `name @ _` in a pattern"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for tuple patterns with a wildcard
+    /// pattern (`_`) is next to a rest pattern (`..`) pattern.
+    ///
+    /// **Why is this bad?** The wildcard pattern is unneeded as the rest pattern
+    /// can match that element as well.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// # struct TupleStruct(u32, u32, u32);
+    /// # let t = TupleStruct(1, 2, 3);
+    ///
+    /// match t {
+    ///     TupleStruct(0, .., _) => (),
+    ///     _ => (),
+    /// }
+    /// ```
+    /// can be written as
+    /// ```rust
+    /// # struct TupleStruct(u32, u32, u32);
+    /// # let t = TupleStruct(1, 2, 3);
+    ///
+    /// match t {
+    ///     TupleStruct(0, ..) => (),
+    ///     _ => (),
+    /// }
+    /// ```
+    pub UNNEEDED_WILDCARD_PATTERN,
+    complexity,
+    "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`) pattern"
+}
+
 declare_lint_pass!(MiscEarlyLints => [
     UNNEEDED_FIELD_PATTERN,
     DUPLICATE_UNDERSCORE_ARGUMENT,
@@ -204,7 +238,8 @@ declare_lint_pass!(MiscEarlyLints => [
     UNSEPARATED_LITERAL_SUFFIX,
     ZERO_PREFIXED_LITERAL,
     BUILTIN_TYPE_SHADOW,
-    REDUNDANT_PATTERN
+    REDUNDANT_PATTERN,
+    UNNEEDED_WILDCARD_PATTERN,
 ]);
 
 // Used to find `return` statements or equivalents e.g., `?`
@@ -326,6 +361,62 @@ impl EarlyLintPass for MiscEarlyLints {
                 );
             }
         }
+
+        if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.node {
+            fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
+                span_lint_and_sugg(
+                    cx,
+                    UNNEEDED_WILDCARD_PATTERN,
+                    span,
+                    if only_one {
+                        "this pattern is unneeded as the `..` pattern can match that element"
+                    } else {
+                        "these patterns are unneeded as the `..` pattern can match those elements"
+                    },
+                    if only_one { "remove it" } else { "remove them" },
+                    "".to_string(),
+                    Applicability::MachineApplicable,
+                );
+            }
+
+            fn is_rest<P: std::ops::Deref<Target = Pat>>(pat: &P) -> bool {
+                if let PatKind::Rest = pat.node {
+                    true
+                } else {
+                    false
+                }
+            }
+
+            fn is_wild<P: std::ops::Deref<Target = Pat>>(pat: &&P) -> bool {
+                if let PatKind::Wild = pat.node {
+                    true
+                } else {
+                    false
+                }
+            }
+
+            if let Some(rest_index) = patterns.iter().position(is_rest) {
+                if let Some((left_index, left_pat)) = patterns[..rest_index]
+                    .iter()
+                    .rev()
+                    .take_while(is_wild)
+                    .enumerate()
+                    .last()
+                {
+                    span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
+                }
+
+                if let Some((right_index, right_pat)) =
+                    patterns[rest_index + 1..].iter().take_while(is_wild).enumerate().last()
+                {
+                    span_lint(
+                        cx,
+                        patterns[rest_index].span.shrink_to_hi().to(right_pat.span),
+                        right_index == 0,
+                    );
+                }
+            }
+        }
     }
 
     fn check_fn(&mut self, cx: &EarlyContext<'_>, _: FnKind<'_>, decl: &FnDecl, _: Span, _: NodeId) {
diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs
index 223e6aa9acd..589ae6b3d35 100644
--- a/src/lintlist/mod.rs
+++ b/src/lintlist/mod.rs
@@ -6,7 +6,7 @@ pub use lint::Lint;
 pub use lint::LINT_LEVELS;
 
 // begin lint list, do not remove this comment, it’s used in `update_lints`
-pub const ALL_LINTS: [Lint; 313] = [
+pub const ALL_LINTS: [Lint; 314] = [
     Lint {
         name: "absurd_extreme_comparisons",
         group: "correctness",
@@ -1975,6 +1975,13 @@ pub const ALL_LINTS: [Lint; 313] = [
         module: "misc_early",
     },
     Lint {
+        name: "unneeded_wildcard_pattern",
+        group: "complexity",
+        desc: "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`) pattern",
+        deprecation: None,
+        module: "misc_early",
+    },
+    Lint {
         name: "unreadable_literal",
         group: "style",
         desc: "long integer literal without underscores",
diff --git a/tests/ui/unneeded_wildcard_pattern.fixed b/tests/ui/unneeded_wildcard_pattern.fixed
new file mode 100644
index 00000000000..d0b62fa6f43
--- /dev/null
+++ b/tests/ui/unneeded_wildcard_pattern.fixed
@@ -0,0 +1,41 @@
+// run-rustfix
+#![feature(stmt_expr_attributes)]
+#![deny(clippy::unneeded_wildcard_pattern)]
+
+fn main() {
+    let t = (0, 1, 2, 3);
+
+    if let (0, ..) = t {};
+    if let (0, ..) = t {};
+    if let (0, ..) = t {};
+    if let (0, ..) = t {};
+    if let (_, 0, ..) = t {};
+    if let (.., 0, _) = t {};
+    if let (0, _, _, _) = t {};
+    if let (0, ..) = t {};
+    if let (.., 0) = t {};
+
+    #[rustfmt::skip]
+    {
+        if let (0, ..,) = t {};
+    }
+
+    struct S(usize, usize, usize, usize);
+
+    let s = S(0, 1, 2, 3);
+
+    if let S(0, ..) = s {};
+    if let S(0, ..) = s {};
+    if let S(0, ..) = s {};
+    if let S(0, ..) = s {};
+    if let S(_, 0, ..) = s {};
+    if let S(.., 0, _) = s {};
+    if let S(0, _, _, _) = s {};
+    if let S(0, ..) = s {};
+    if let S(.., 0) = s {};
+
+    #[rustfmt::skip]
+    {
+        if let S(0, ..,) = s {};
+    }
+}
diff --git a/tests/ui/unneeded_wildcard_pattern.rs b/tests/ui/unneeded_wildcard_pattern.rs
new file mode 100644
index 00000000000..bad158907ba
--- /dev/null
+++ b/tests/ui/unneeded_wildcard_pattern.rs
@@ -0,0 +1,41 @@
+// run-rustfix
+#![feature(stmt_expr_attributes)]
+#![deny(clippy::unneeded_wildcard_pattern)]
+
+fn main() {
+    let t = (0, 1, 2, 3);
+
+    if let (0, .., _) = t {};
+    if let (0, _, ..) = t {};
+    if let (0, _, _, ..) = t {};
+    if let (0, .., _, _) = t {};
+    if let (_, 0, ..) = t {};
+    if let (.., 0, _) = t {};
+    if let (0, _, _, _) = t {};
+    if let (0, ..) = t {};
+    if let (.., 0) = t {};
+
+    #[rustfmt::skip]
+    {
+        if let (0, .., _, _,) = t {};
+    }
+
+    struct S(usize, usize, usize, usize);
+
+    let s = S(0, 1, 2, 3);
+
+    if let S(0, .., _) = s {};
+    if let S(0, _, ..) = s {};
+    if let S(0, _, _, ..) = s {};
+    if let S(0, .., _, _) = s {};
+    if let S(_, 0, ..) = s {};
+    if let S(.., 0, _) = s {};
+    if let S(0, _, _, _) = s {};
+    if let S(0, ..) = s {};
+    if let S(.., 0) = s {};
+
+    #[rustfmt::skip]
+    {
+        if let S(0, .., _, _,) = s {};
+    }
+}
diff --git a/tests/ui/unneeded_wildcard_pattern.stderr b/tests/ui/unneeded_wildcard_pattern.stderr
new file mode 100644
index 00000000000..8cc2516959a
--- /dev/null
+++ b/tests/ui/unneeded_wildcard_pattern.stderr
@@ -0,0 +1,68 @@
+error: this pattern is unneeded as the `..` pattern can match that element
+  --> $DIR/unneeded_wildcard_pattern.rs:8:18
+   |
+LL |     if let (0, .., _) = t {};
+   |                  ^^^ help: remove it
+   |
+note: lint level defined here
+  --> $DIR/unneeded_wildcard_pattern.rs:3:9
+   |
+LL | #![deny(clippy::unneeded_wildcard_pattern)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this pattern is unneeded as the `..` pattern can match that element
+  --> $DIR/unneeded_wildcard_pattern.rs:9:16
+   |
+LL |     if let (0, _, ..) = t {};
+   |                ^^^ help: remove it
+
+error: these patterns are unneeded as the `..` pattern can match those elements
+  --> $DIR/unneeded_wildcard_pattern.rs:10:16
+   |
+LL |     if let (0, _, _, ..) = t {};
+   |                ^^^^^^ help: remove them
+
+error: these patterns are unneeded as the `..` pattern can match those elements
+  --> $DIR/unneeded_wildcard_pattern.rs:11:18
+   |
+LL |     if let (0, .., _, _) = t {};
+   |                  ^^^^^^ help: remove them
+
+error: these patterns are unneeded as the `..` pattern can match those elements
+  --> $DIR/unneeded_wildcard_pattern.rs:20:22
+   |
+LL |         if let (0, .., _, _,) = t {};
+   |                      ^^^^^^ help: remove them
+
+error: this pattern is unneeded as the `..` pattern can match that element
+  --> $DIR/unneeded_wildcard_pattern.rs:27:19
+   |
+LL |     if let S(0, .., _) = s {};
+   |                   ^^^ help: remove it
+
+error: this pattern is unneeded as the `..` pattern can match that element
+  --> $DIR/unneeded_wildcard_pattern.rs:28:17
+   |
+LL |     if let S(0, _, ..) = s {};
+   |                 ^^^ help: remove it
+
+error: these patterns are unneeded as the `..` pattern can match those elements
+  --> $DIR/unneeded_wildcard_pattern.rs:29:17
+   |
+LL |     if let S(0, _, _, ..) = s {};
+   |                 ^^^^^^ help: remove them
+
+error: these patterns are unneeded as the `..` pattern can match those elements
+  --> $DIR/unneeded_wildcard_pattern.rs:30:19
+   |
+LL |     if let S(0, .., _, _) = s {};
+   |                   ^^^^^^ help: remove them
+
+error: these patterns are unneeded as the `..` pattern can match those elements
+  --> $DIR/unneeded_wildcard_pattern.rs:39:23
+   |
+LL |         if let S(0, .., _, _,) = s {};
+   |                       ^^^^^^ help: remove them
+
+error: aborting due to 10 previous errors
+