about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVardan Margaryan <v.t.margaryan@gmail.com>2020-05-10 00:08:41 +0300
committerVardan Margaryan <v.t.margaryan@gmail.com>2020-05-14 22:36:46 +0300
commit94e4b5ec316993200d75276b4e7c16a059bf3a57 (patch)
treed20f2dff8cd920f7cab5f27faa7b0d2c0d410c6b
parente1842b0cace2a73b55e6d85dc145a102603c7e5a (diff)
downloadrust-94e4b5ec316993200d75276b4e7c16a059bf3a57.tar.gz
rust-94e4b5ec316993200d75276b4e7c16a059bf3a57.zip
Add the redundant_wildcard_enum_match lint
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/matches.rs48
-rw-r--r--src/lintlist/mod.rs7
-rw-r--r--tests/ui/match_wildcard_for_single_variants.fixed18
-rw-r--r--tests/ui/match_wildcard_for_single_variants.rs18
-rw-r--r--tests/ui/match_wildcard_for_single_variants.stderr10
7 files changed, 104 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b25ef049356..d6298fec65a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1439,6 +1439,7 @@ Released 2018-09-13
 [`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
 [`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
 [`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
+[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
 [`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
 [`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
 [`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 0c4daeb731f..41046c18ed2 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -641,6 +641,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &matches::MATCH_OVERLAPPING_ARM,
         &matches::MATCH_REF_PATS,
         &matches::MATCH_SINGLE_BINDING,
+        &matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
         &matches::MATCH_WILD_ERR_ARM,
         &matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
         &matches::SINGLE_MATCH,
@@ -1147,6 +1148,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&macro_use::MACRO_USE_IMPORTS),
         LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
         LintId::of(&matches::MATCH_BOOL),
+        LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
         LintId::of(&matches::SINGLE_MATCH_ELSE),
         LintId::of(&methods::FILTER_MAP),
         LintId::of(&methods::FILTER_MAP_NEXT),
diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs
index 8f86535ef1e..42a6c416619 100644
--- a/clippy_lints/src/matches.rs
+++ b/clippy_lints/src/matches.rs
@@ -230,6 +230,40 @@ declare_clippy_lint! {
 }
 
 declare_clippy_lint! {
+    /// **What it does:** Checks for wildcard enum matches for a single variant.
+    ///
+    /// **Why is this bad?** New enum variants added by library updates can be missed.
+    ///
+    /// **Known problems:** Suggested replacements may not use correct path to enum
+    /// if it's not present in the current scope.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// # enum Foo { A, B, C }
+    /// # let x = Foo::B;
+    /// match x {
+    ///     Foo::A => {},
+    ///     Foo::B => {},
+    ///     _ => {},
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # enum Foo { A, B, C }
+    /// # let x = Foo::B;
+    /// match x {
+    ///     Foo::A => {},
+    ///     Foo::B => {},
+    ///     Foo::C => {},
+    /// }
+    /// ```
+    pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+    pedantic,
+    "a wildcard enum match for a single variant"
+}
+
+declare_clippy_lint! {
     /// **What it does:** Checks for wildcard pattern used with others patterns in same match arm.
     ///
     /// **Why is this bad?** Wildcard pattern already covers any other pattern as it will match anyway.
@@ -356,6 +390,7 @@ impl_lint_pass!(Matches => [
     MATCH_WILD_ERR_ARM,
     MATCH_AS_REF,
     WILDCARD_ENUM_MATCH_ARM,
+    MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
     WILDCARD_IN_OR_PATTERNS,
     MATCH_SINGLE_BINDING,
     INFALLIBLE_DESTRUCTURING_MATCH,
@@ -766,6 +801,19 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_
             }
         }
 
+        if suggestion.len() == 1 {
+            // No need to check for non-exhaustive enum as in that case len would be greater than 1
+            span_lint_and_sugg(
+                cx,
+                MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+                wildcard_span,
+                message,
+                "try this",
+                suggestion[0].clone(),
+                Applicability::MachineApplicable,
+            )
+        };
+
         span_lint_and_sugg(
             cx,
             WILDCARD_ENUM_MATCH_ARM,
diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs
index e1a6d4bdd31..250a7c09f78 100644
--- a/src/lintlist/mod.rs
+++ b/src/lintlist/mod.rs
@@ -1201,6 +1201,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
         module: "matches",
     },
     Lint {
+        name: "match_wildcard_for_single_variants",
+        group: "pedantic",
+        desc: "a wildcard enum match for a single variant",
+        deprecation: None,
+        module: "matches",
+    },
+    Lint {
         name: "maybe_infinite_iter",
         group: "pedantic",
         desc: "possible infinite iteration",
diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed
new file mode 100644
index 00000000000..5f1a559f591
--- /dev/null
+++ b/tests/ui/match_wildcard_for_single_variants.fixed
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::match_wildcard_for_single_variants)]
+#![allow(dead_code)]
+
+enum Foo {
+    A,
+    B,
+    C,
+}
+
+fn main() {
+    match Foo::A {
+        Foo::A => {},
+        Foo::B => {},
+        Foo::C => {},
+    }
+}
diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs
new file mode 100644
index 00000000000..1159f9e722d
--- /dev/null
+++ b/tests/ui/match_wildcard_for_single_variants.rs
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::match_wildcard_for_single_variants)]
+#![allow(dead_code)]
+
+enum Foo {
+    A,
+    B,
+    C,
+}
+
+fn main() {
+    match Foo::A {
+        Foo::A => {},
+        Foo::B => {},
+        _ => {},
+    }
+}
diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr
new file mode 100644
index 00000000000..128dd4808bf
--- /dev/null
+++ b/tests/ui/match_wildcard_for_single_variants.stderr
@@ -0,0 +1,10 @@
+error: wildcard match will miss any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:16:9
+   |
+LL |         _ => {},
+   |         ^ help: try this: `Foo::C`
+   |
+   = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings`
+
+error: aborting due to previous error
+