about summary refs log tree commit diff
diff options
context:
space:
mode:
authorManish Goregaokar <manishsmail@gmail.com>2021-01-21 12:48:30 -0800
committerManish Goregaokar <manishsmail@gmail.com>2021-01-21 13:31:06 -0800
commitf1ab3024b27cc7c02a80fd54382a10a1b4ef3bcd (patch)
tree9558720e5d3ec4380d97de2a4c613f71256c9574
parent043cf97abc51bdb6df728d57957f1d194d988475 (diff)
downloadrust-f1ab3024b27cc7c02a80fd54382a10a1b4ef3bcd.tar.gz
rust-f1ab3024b27cc7c02a80fd54382a10a1b4ef3bcd.zip
New lint: exhaustive_enums
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/exhaustive_enums.rs68
-rw-r--r--clippy_lints/src/lib.rs4
-rw-r--r--tests/ui/exhaustive_enums.fixed24
-rw-r--r--tests/ui/exhaustive_enums.rs23
-rw-r--r--tests/ui/exhaustive_enums.stderr28
6 files changed, 148 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 04f042b2deb..4cf2125ea2f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1938,6 +1938,7 @@ Released 2018-09-13
 [`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
 [`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
 [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
+[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
 [`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
 [`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
 [`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
diff --git a/clippy_lints/src/exhaustive_enums.rs b/clippy_lints/src/exhaustive_enums.rs
new file mode 100644
index 00000000000..099171962d3
--- /dev/null
+++ b/clippy_lints/src/exhaustive_enums.rs
@@ -0,0 +1,68 @@
+use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg};
+use if_chain::if_chain;
+use rustc_ast::ast::{Item, ItemKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// **What it does:** Warns on any `enum`s that are not tagged `#[non_exhaustive]`
+    ///
+    /// **Why is this bad?** Exhaustive enums are typically fine, but a project which does
+    /// not wish to make a stability commitment around enums may wish to disable them by default.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// enum Foo {
+    ///     Bar,
+    ///     Baz
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// #[non_exhaustive]
+    /// enum Foo {
+    ///     Bar,
+    ///     Baz
+    /// }    /// ```
+    pub EXHAUSTIVE_ENUMS,
+    restriction,
+    "default lint description"
+}
+
+declare_lint_pass!(ExhaustiveEnums => [EXHAUSTIVE_ENUMS]);
+
+impl EarlyLintPass for ExhaustiveEnums {
+    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+        if_chain! {
+            if let ItemKind::Enum(..) = item.kind;
+            if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive));
+            then {
+                if let Some(snippet) = snippet_opt(cx, item.span) {
+                    span_lint_and_sugg(
+                        cx,
+                        EXHAUSTIVE_ENUMS,
+                        item.span,
+                        "enums should not be exhaustive",
+                        "try adding #[non_exhaustive]",
+                        format!("#[non_exhaustive]\n{}", snippet),
+                        Applicability::MaybeIncorrect,
+                    );
+                } else {
+                    span_lint_and_help(
+                        cx,
+                        EXHAUSTIVE_ENUMS,
+                        item.span,
+                        "enums should not be exhaustive",
+                        None,
+                        "try adding #[non_exhaustive]",
+                    );
+                }
+            }
+        }
+    }
+}
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 53764bb7390..465ad3846ce 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -200,6 +200,7 @@ mod escape;
 mod eta_reduction;
 mod eval_order_dependence;
 mod excessive_bools;
+mod exhaustive_enums;
 mod exit;
 mod explicit_write;
 mod fallible_impl_from;
@@ -611,6 +612,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &eval_order_dependence::EVAL_ORDER_DEPENDENCE,
         &excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
         &excessive_bools::STRUCT_EXCESSIVE_BOOLS,
+        &exhaustive_enums::EXHAUSTIVE_ENUMS,
         &exit::EXIT,
         &explicit_write::EXPLICIT_WRITE,
         &fallible_impl_from::FALLIBLE_IMPL_FROM,
@@ -1096,6 +1098,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence);
     store.register_late_pass(|| box missing_doc::MissingDoc::new());
     store.register_late_pass(|| box missing_inline::MissingInline);
+    store.register_early_pass(move || box exhaustive_enums::ExhaustiveEnums);
     store.register_late_pass(|| box if_let_some_result::OkIfLet);
     store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl);
     store.register_late_pass(|| box unused_io_amount::UnusedIoAmount);
@@ -1246,6 +1249,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&create_dir::CREATE_DIR),
         LintId::of(&dbg_macro::DBG_MACRO),
         LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
+        LintId::of(&exhaustive_enums::EXHAUSTIVE_ENUMS),
         LintId::of(&exit::EXIT),
         LintId::of(&float_literal::LOSSY_FLOAT_LITERAL),
         LintId::of(&implicit_return::IMPLICIT_RETURN),
diff --git a/tests/ui/exhaustive_enums.fixed b/tests/ui/exhaustive_enums.fixed
new file mode 100644
index 00000000000..2d5f0474bc0
--- /dev/null
+++ b/tests/ui/exhaustive_enums.fixed
@@ -0,0 +1,24 @@
+// run-rustfix
+
+#![deny(clippy::exhaustive_enums)]
+#![allow(unused)]
+
+fn main() {
+    // nop
+}
+
+#[non_exhaustive]
+enum Exhaustive {
+    Foo,
+    Bar,
+    Baz,
+    Quux(String),
+}
+
+#[non_exhaustive]
+enum NonExhaustive {
+    Foo,
+    Bar,
+    Baz,
+    Quux(String),
+}
diff --git a/tests/ui/exhaustive_enums.rs b/tests/ui/exhaustive_enums.rs
new file mode 100644
index 00000000000..5c88454ae61
--- /dev/null
+++ b/tests/ui/exhaustive_enums.rs
@@ -0,0 +1,23 @@
+// run-rustfix
+
+#![deny(clippy::exhaustive_enums)]
+#![allow(unused)]
+
+fn main() {
+    // nop
+}
+
+enum Exhaustive {
+    Foo,
+    Bar,
+    Baz,
+    Quux(String),
+}
+
+#[non_exhaustive]
+enum NonExhaustive {
+    Foo,
+    Bar,
+    Baz,
+    Quux(String),
+}
diff --git a/tests/ui/exhaustive_enums.stderr b/tests/ui/exhaustive_enums.stderr
new file mode 100644
index 00000000000..ee5a1836267
--- /dev/null
+++ b/tests/ui/exhaustive_enums.stderr
@@ -0,0 +1,28 @@
+error: enums should not be exhaustive
+  --> $DIR/exhaustive_enums.rs:10:1
+   |
+LL | / enum Exhaustive {
+LL | |     Foo,
+LL | |     Bar,
+LL | |     Baz,
+LL | |     Quux(String),
+LL | | }
+   | |_^
+   |
+note: the lint level is defined here
+  --> $DIR/exhaustive_enums.rs:3:9
+   |
+LL | #![deny(clippy::exhaustive_enums)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^
+help: try adding #[non_exhaustive]
+   |
+LL | #[non_exhaustive]
+LL | enum Exhaustive {
+LL |     Foo,
+LL |     Bar,
+LL |     Baz,
+LL |     Quux(String),
+ ...
+
+error: aborting due to previous error
+