Skip to content

Commit f1ab302

Browse files
committed
New lint: exhaustive_enums
1 parent 043cf97 commit f1ab302

File tree

6 files changed

+148
-0
lines changed

6 files changed

+148
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,6 +1938,7 @@ Released 2018-09-13
19381938
[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
19391939
[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
19401940
[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
1941+
[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
19411942
[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
19421943
[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
19431944
[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used

clippy_lints/src/exhaustive_enums.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg};
2+
use if_chain::if_chain;
3+
use rustc_ast::ast::{Item, ItemKind};
4+
use rustc_errors::Applicability;
5+
use rustc_lint::{EarlyContext, EarlyLintPass};
6+
use rustc_session::{declare_lint_pass, declare_tool_lint};
7+
use rustc_span::sym;
8+
9+
declare_clippy_lint! {
10+
/// **What it does:** Warns on any `enum`s that are not tagged `#[non_exhaustive]`
11+
///
12+
/// **Why is this bad?** Exhaustive enums are typically fine, but a project which does
13+
/// not wish to make a stability commitment around enums may wish to disable them by default.
14+
///
15+
/// **Known problems:** None.
16+
///
17+
/// **Example:**
18+
///
19+
/// ```rust
20+
/// enum Foo {
21+
/// Bar,
22+
/// Baz
23+
/// }
24+
/// ```
25+
/// Use instead:
26+
/// ```rust
27+
/// #[non_exhaustive]
28+
/// enum Foo {
29+
/// Bar,
30+
/// Baz
31+
/// } /// ```
32+
pub EXHAUSTIVE_ENUMS,
33+
restriction,
34+
"default lint description"
35+
}
36+
37+
declare_lint_pass!(ExhaustiveEnums => [EXHAUSTIVE_ENUMS]);
38+
39+
impl EarlyLintPass for ExhaustiveEnums {
40+
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
41+
if_chain! {
42+
if let ItemKind::Enum(..) = item.kind;
43+
if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive));
44+
then {
45+
if let Some(snippet) = snippet_opt(cx, item.span) {
46+
span_lint_and_sugg(
47+
cx,
48+
EXHAUSTIVE_ENUMS,
49+
item.span,
50+
"enums should not be exhaustive",
51+
"try adding #[non_exhaustive]",
52+
format!("#[non_exhaustive]\n{}", snippet),
53+
Applicability::MaybeIncorrect,
54+
);
55+
} else {
56+
span_lint_and_help(
57+
cx,
58+
EXHAUSTIVE_ENUMS,
59+
item.span,
60+
"enums should not be exhaustive",
61+
None,
62+
"try adding #[non_exhaustive]",
63+
);
64+
}
65+
}
66+
}
67+
}
68+
}

clippy_lints/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ mod escape;
200200
mod eta_reduction;
201201
mod eval_order_dependence;
202202
mod excessive_bools;
203+
mod exhaustive_enums;
203204
mod exit;
204205
mod explicit_write;
205206
mod fallible_impl_from;
@@ -611,6 +612,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
611612
&eval_order_dependence::EVAL_ORDER_DEPENDENCE,
612613
&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
613614
&excessive_bools::STRUCT_EXCESSIVE_BOOLS,
615+
&exhaustive_enums::EXHAUSTIVE_ENUMS,
614616
&exit::EXIT,
615617
&explicit_write::EXPLICIT_WRITE,
616618
&fallible_impl_from::FALLIBLE_IMPL_FROM,
@@ -1096,6 +1098,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10961098
store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence);
10971099
store.register_late_pass(|| box missing_doc::MissingDoc::new());
10981100
store.register_late_pass(|| box missing_inline::MissingInline);
1101+
store.register_early_pass(move || box exhaustive_enums::ExhaustiveEnums);
10991102
store.register_late_pass(|| box if_let_some_result::OkIfLet);
11001103
store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl);
11011104
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:
12461249
LintId::of(&create_dir::CREATE_DIR),
12471250
LintId::of(&dbg_macro::DBG_MACRO),
12481251
LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
1252+
LintId::of(&exhaustive_enums::EXHAUSTIVE_ENUMS),
12491253
LintId::of(&exit::EXIT),
12501254
LintId::of(&float_literal::LOSSY_FLOAT_LITERAL),
12511255
LintId::of(&implicit_return::IMPLICIT_RETURN),

tests/ui/exhaustive_enums.fixed

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// run-rustfix
2+
3+
#![deny(clippy::exhaustive_enums)]
4+
#![allow(unused)]
5+
6+
fn main() {
7+
// nop
8+
}
9+
10+
#[non_exhaustive]
11+
enum Exhaustive {
12+
Foo,
13+
Bar,
14+
Baz,
15+
Quux(String),
16+
}
17+
18+
#[non_exhaustive]
19+
enum NonExhaustive {
20+
Foo,
21+
Bar,
22+
Baz,
23+
Quux(String),
24+
}

tests/ui/exhaustive_enums.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// run-rustfix
2+
3+
#![deny(clippy::exhaustive_enums)]
4+
#![allow(unused)]
5+
6+
fn main() {
7+
// nop
8+
}
9+
10+
enum Exhaustive {
11+
Foo,
12+
Bar,
13+
Baz,
14+
Quux(String),
15+
}
16+
17+
#[non_exhaustive]
18+
enum NonExhaustive {
19+
Foo,
20+
Bar,
21+
Baz,
22+
Quux(String),
23+
}

tests/ui/exhaustive_enums.stderr

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error: enums should not be exhaustive
2+
--> $DIR/exhaustive_enums.rs:10:1
3+
|
4+
LL | / enum Exhaustive {
5+
LL | | Foo,
6+
LL | | Bar,
7+
LL | | Baz,
8+
LL | | Quux(String),
9+
LL | | }
10+
| |_^
11+
|
12+
note: the lint level is defined here
13+
--> $DIR/exhaustive_enums.rs:3:9
14+
|
15+
LL | #![deny(clippy::exhaustive_enums)]
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^
17+
help: try adding #[non_exhaustive]
18+
|
19+
LL | #[non_exhaustive]
20+
LL | enum Exhaustive {
21+
LL | Foo,
22+
LL | Bar,
23+
LL | Baz,
24+
LL | Quux(String),
25+
...
26+
27+
error: aborting due to previous error
28+

0 commit comments

Comments
 (0)