Skip to content

Commit 8d57cee

Browse files
committed
Auto merge of #6617 - Manishearth:exhaustive_enums, r=camsteffen
New lint: exhaustive_enums, exhaustive_structs Fixes #6616 changelog: Added restriction lint: `exhaustive_enums`, `exhaustive_structs`
2 parents c56b328 + 3e3dff7 commit 8d57cee

File tree

6 files changed

+332
-0
lines changed

6 files changed

+332
-0
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -1938,6 +1938,8 @@ 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
1942+
[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
19411943
[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
19421944
[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
19431945
[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used

clippy_lints/src/exhaustive_items.rs

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use crate::utils::{indent_of, span_lint_and_then};
2+
use if_chain::if_chain;
3+
use rustc_errors::Applicability;
4+
use rustc_hir::{Item, ItemKind};
5+
use rustc_lint::{LateContext, LateLintPass};
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 exported `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 exported enums may wish to
14+
/// disable them by default.
15+
///
16+
/// **Known problems:** None.
17+
///
18+
/// **Example:**
19+
///
20+
/// ```rust
21+
/// enum Foo {
22+
/// Bar,
23+
/// Baz
24+
/// }
25+
/// ```
26+
/// Use instead:
27+
/// ```rust
28+
/// #[non_exhaustive]
29+
/// enum Foo {
30+
/// Bar,
31+
/// Baz
32+
/// }
33+
/// ```
34+
pub EXHAUSTIVE_ENUMS,
35+
restriction,
36+
"detects exported enums that have not been marked #[non_exhaustive]"
37+
}
38+
39+
declare_clippy_lint! {
40+
/// **What it does:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`
41+
///
42+
/// **Why is this bad?** Exhaustive structs are typically fine, but a project which does
43+
/// not wish to make a stability commitment around exported structs may wish to
44+
/// disable them by default.
45+
///
46+
/// **Known problems:** None.
47+
///
48+
/// **Example:**
49+
///
50+
/// ```rust
51+
/// struct Foo {
52+
/// bar: u8,
53+
/// baz: String,
54+
/// }
55+
/// ```
56+
/// Use instead:
57+
/// ```rust
58+
/// #[non_exhaustive]
59+
/// struct Foo {
60+
/// bar: u8,
61+
/// baz: String,
62+
/// }
63+
/// ```
64+
pub EXHAUSTIVE_STRUCTS,
65+
restriction,
66+
"detects exported structs that have not been marked #[non_exhaustive]"
67+
}
68+
69+
declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]);
70+
71+
impl LateLintPass<'_> for ExhaustiveItems {
72+
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
73+
if_chain! {
74+
if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind;
75+
if cx.access_levels.is_exported(item.hir_id);
76+
if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive));
77+
then {
78+
let (lint, msg) = if let ItemKind::Enum(..) = item.kind {
79+
(EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive")
80+
} else {
81+
(EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive")
82+
};
83+
let suggestion_span = item.span.shrink_to_lo();
84+
let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0));
85+
span_lint_and_then(
86+
cx,
87+
lint,
88+
item.span,
89+
msg,
90+
|diag| {
91+
let sugg = format!("#[non_exhaustive]\n{}", indent);
92+
diag.span_suggestion(suggestion_span,
93+
"try adding #[non_exhaustive]",
94+
sugg,
95+
Applicability::MaybeIncorrect);
96+
}
97+
);
98+
99+
}
100+
}
101+
}
102+
}

clippy_lints/src/lib.rs

+6
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_items;
203204
mod exit;
204205
mod explicit_write;
205206
mod fallible_impl_from;
@@ -615,6 +616,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
615616
&eval_order_dependence::EVAL_ORDER_DEPENDENCE,
616617
&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
617618
&excessive_bools::STRUCT_EXCESSIVE_BOOLS,
619+
&exhaustive_items::EXHAUSTIVE_ENUMS,
620+
&exhaustive_items::EXHAUSTIVE_STRUCTS,
618621
&exit::EXIT,
619622
&explicit_write::EXPLICIT_WRITE,
620623
&fallible_impl_from::FALLIBLE_IMPL_FROM,
@@ -1101,6 +1104,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11011104
store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence);
11021105
store.register_late_pass(|| box missing_doc::MissingDoc::new());
11031106
store.register_late_pass(|| box missing_inline::MissingInline);
1107+
store.register_late_pass(move || box exhaustive_items::ExhaustiveItems);
11041108
store.register_late_pass(|| box if_let_some_result::OkIfLet);
11051109
store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl);
11061110
store.register_late_pass(|| box unused_io_amount::UnusedIoAmount);
@@ -1251,6 +1255,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
12511255
LintId::of(&create_dir::CREATE_DIR),
12521256
LintId::of(&dbg_macro::DBG_MACRO),
12531257
LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
1258+
LintId::of(&exhaustive_items::EXHAUSTIVE_ENUMS),
1259+
LintId::of(&exhaustive_items::EXHAUSTIVE_STRUCTS),
12541260
LintId::of(&exit::EXIT),
12551261
LintId::of(&float_literal::LOSSY_FLOAT_LITERAL),
12561262
LintId::of(&implicit_return::IMPLICIT_RETURN),

tests/ui/exhaustive_items.fixed

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// run-rustfix
2+
3+
#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
4+
#![allow(unused)]
5+
6+
fn main() {
7+
// nop
8+
}
9+
10+
pub mod enums {
11+
#[non_exhaustive]
12+
pub enum Exhaustive {
13+
Foo,
14+
Bar,
15+
Baz,
16+
Quux(String),
17+
}
18+
19+
/// Some docs
20+
#[repr(C)]
21+
#[non_exhaustive]
22+
pub enum ExhaustiveWithAttrs {
23+
Foo,
24+
Bar,
25+
Baz,
26+
Quux(String),
27+
}
28+
29+
// no warning, already non_exhaustive
30+
#[non_exhaustive]
31+
pub enum NonExhaustive {
32+
Foo,
33+
Bar,
34+
Baz,
35+
Quux(String),
36+
}
37+
38+
// no warning, private
39+
enum ExhaustivePrivate {
40+
Foo,
41+
Bar,
42+
Baz,
43+
Quux(String),
44+
}
45+
46+
// no warning, private
47+
#[non_exhaustive]
48+
enum NonExhaustivePrivate {
49+
Foo,
50+
Bar,
51+
Baz,
52+
Quux(String),
53+
}
54+
}
55+
56+
pub mod structs {
57+
#[non_exhaustive]
58+
pub struct Exhaustive {
59+
foo: u8,
60+
bar: String,
61+
}
62+
63+
// no warning, already non_exhaustive
64+
#[non_exhaustive]
65+
pub struct NonExhaustive {
66+
foo: u8,
67+
bar: String,
68+
}
69+
70+
// no warning, private
71+
struct ExhaustivePrivate {
72+
foo: u8,
73+
bar: String,
74+
}
75+
76+
// no warning, private
77+
#[non_exhaustive]
78+
struct NonExhaustivePrivate {
79+
foo: u8,
80+
bar: String,
81+
}
82+
}

tests/ui/exhaustive_items.rs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// run-rustfix
2+
3+
#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
4+
#![allow(unused)]
5+
6+
fn main() {
7+
// nop
8+
}
9+
10+
pub mod enums {
11+
pub enum Exhaustive {
12+
Foo,
13+
Bar,
14+
Baz,
15+
Quux(String),
16+
}
17+
18+
/// Some docs
19+
#[repr(C)]
20+
pub enum ExhaustiveWithAttrs {
21+
Foo,
22+
Bar,
23+
Baz,
24+
Quux(String),
25+
}
26+
27+
// no warning, already non_exhaustive
28+
#[non_exhaustive]
29+
pub enum NonExhaustive {
30+
Foo,
31+
Bar,
32+
Baz,
33+
Quux(String),
34+
}
35+
36+
// no warning, private
37+
enum ExhaustivePrivate {
38+
Foo,
39+
Bar,
40+
Baz,
41+
Quux(String),
42+
}
43+
44+
// no warning, private
45+
#[non_exhaustive]
46+
enum NonExhaustivePrivate {
47+
Foo,
48+
Bar,
49+
Baz,
50+
Quux(String),
51+
}
52+
}
53+
54+
pub mod structs {
55+
pub struct Exhaustive {
56+
foo: u8,
57+
bar: String,
58+
}
59+
60+
// no warning, already non_exhaustive
61+
#[non_exhaustive]
62+
pub struct NonExhaustive {
63+
foo: u8,
64+
bar: String,
65+
}
66+
67+
// no warning, private
68+
struct ExhaustivePrivate {
69+
foo: u8,
70+
bar: String,
71+
}
72+
73+
// no warning, private
74+
#[non_exhaustive]
75+
struct NonExhaustivePrivate {
76+
foo: u8,
77+
bar: String,
78+
}
79+
}

tests/ui/exhaustive_items.stderr

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
error: exported enums should not be exhaustive
2+
--> $DIR/exhaustive_items.rs:11:5
3+
|
4+
LL | / pub 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_items.rs:3:9
14+
|
15+
LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^
17+
help: try adding #[non_exhaustive]
18+
|
19+
LL | #[non_exhaustive]
20+
LL | pub enum Exhaustive {
21+
|
22+
23+
error: exported enums should not be exhaustive
24+
--> $DIR/exhaustive_items.rs:20:5
25+
|
26+
LL | / pub enum ExhaustiveWithAttrs {
27+
LL | | Foo,
28+
LL | | Bar,
29+
LL | | Baz,
30+
LL | | Quux(String),
31+
LL | | }
32+
| |_____^
33+
|
34+
help: try adding #[non_exhaustive]
35+
|
36+
LL | #[non_exhaustive]
37+
LL | pub enum ExhaustiveWithAttrs {
38+
|
39+
40+
error: exported structs should not be exhaustive
41+
--> $DIR/exhaustive_items.rs:55:5
42+
|
43+
LL | / pub struct Exhaustive {
44+
LL | | foo: u8,
45+
LL | | bar: String,
46+
LL | | }
47+
| |_____^
48+
|
49+
note: the lint level is defined here
50+
--> $DIR/exhaustive_items.rs:3:35
51+
|
52+
LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
53+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
54+
help: try adding #[non_exhaustive]
55+
|
56+
LL | #[non_exhaustive]
57+
LL | pub struct Exhaustive {
58+
|
59+
60+
error: aborting due to 3 previous errors
61+

0 commit comments

Comments
 (0)