From 558cdcbb2d2fe01a25d041f7191e548ec7d454c4 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 21 Jan 2021 12:48:30 -0800 Subject: [PATCH] New lint: exhaustive_enums --- CHANGELOG.md | 1 + clippy_lints/src/exhaustive_enums.rs | 68 ++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 ++ tests/ui/exhaustive_enums.fixed | 24 ++++++++++ tests/ui/exhaustive_enums.rs | 23 ++++++++++ tests/ui/exhaustive_enums.stderr | 28 ++++++++++++ 6 files changed, 148 insertions(+) create mode 100644 clippy_lints/src/exhaustive_enums.rs create mode 100644 tests/ui/exhaustive_enums.fixed create mode 100644 tests/ui/exhaustive_enums.rs create mode 100644 tests/ui/exhaustive_enums.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 04f042b2debf..4cf2125ea2fc 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 000000000000..099171962d3c --- /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 53764bb73903..465ad3846cec 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 000000000000..bd98a7e9e6b0 --- /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 000000000000..5c88454ae612 --- /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 000000000000..81c592a9ca5f --- /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 +