Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 8cb7e85

Browse files
committed
Add exhaustive_structs lint
1 parent 09d4d49 commit 8cb7e85

File tree

6 files changed

+199
-81
lines changed

6 files changed

+199
-81
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1939,6 +1939,7 @@ Released 2018-09-13
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
19411941
[`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
19421943
[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
19431944
[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
19441945
[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used

clippy_lints/src/exhaustive_items.rs

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg};
22
use if_chain::if_chain;
3-
use rustc_hir::{Item, ItemKind};
43
use rustc_errors::Applicability;
4+
use rustc_hir::{Item, ItemKind};
55
use rustc_lint::{LateContext, LateLintPass};
66
use rustc_session::{declare_lint_pass, declare_tool_lint};
77
use rustc_span::sym;
@@ -10,7 +10,8 @@ declare_clippy_lint! {
1010
/// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`
1111
///
1212
/// **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.
13+
/// not wish to make a stability commitment around exported enums may wish to
14+
/// disable them by default.
1415
///
1516
/// **Known problems:** None.
1617
///
@@ -28,25 +29,62 @@ declare_clippy_lint! {
2829
/// enum Foo {
2930
/// Bar,
3031
/// Baz
31-
/// } /// ```
32+
/// }
33+
/// ```
3234
pub EXHAUSTIVE_ENUMS,
3335
restriction,
34-
"default lint description"
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]"
3567
}
3668

37-
declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS]);
69+
declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]);
3870

3971
impl LateLintPass<'_> for ExhaustiveItems {
4072
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
4173
if_chain! {
42-
if let ItemKind::Enum(..) = item.kind;
74+
if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind;
4375
if cx.access_levels.is_exported(item.hir_id);
4476
if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive));
4577
then {
78+
let lint = if let ItemKind::Enum(..) = item.kind {
79+
EXHAUSTIVE_ENUMS
80+
} else {
81+
EXHAUSTIVE_STRUCTS
82+
};
83+
4684
if let Some(snippet) = snippet_opt(cx, item.span) {
4785
span_lint_and_sugg(
4886
cx,
49-
EXHAUSTIVE_ENUMS,
87+
lint,
5088
item.span,
5189
"enums should not be exhaustive",
5290
"try adding #[non_exhaustive]",
@@ -56,7 +94,7 @@ impl LateLintPass<'_> for ExhaustiveItems {
5694
} else {
5795
span_lint_and_help(
5896
cx,
59-
EXHAUSTIVE_ENUMS,
97+
lint,
6098
item.span,
6199
"enums should not be exhaustive",
62100
None,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
613613
&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
614614
&excessive_bools::STRUCT_EXCESSIVE_BOOLS,
615615
&exhaustive_items::EXHAUSTIVE_ENUMS,
616+
&exhaustive_items::EXHAUSTIVE_STRUCTS,
616617
&exit::EXIT,
617618
&explicit_write::EXPLICIT_WRITE,
618619
&fallible_impl_from::FALLIBLE_IMPL_FROM,
@@ -1250,6 +1251,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
12501251
LintId::of(&dbg_macro::DBG_MACRO),
12511252
LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
12521253
LintId::of(&exhaustive_items::EXHAUSTIVE_ENUMS),
1254+
LintId::of(&exhaustive_items::EXHAUSTIVE_STRUCTS),
12531255
LintId::of(&exit::EXIT),
12541256
LintId::of(&float_literal::LOSSY_FLOAT_LITERAL),
12551257
LintId::of(&implicit_return::IMPLICIT_RETURN),

tests/ui/exhaustive_items.fixed

Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,72 @@
11
// run-rustfix
22

3-
#![deny(clippy::exhaustive_enums)]
3+
#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
44
#![allow(unused)]
55

66
fn main() {
77
// nop
88
}
99

10-
#[non_exhaustive]
10+
pub mod enums {
11+
#[non_exhaustive]
1112
pub enum Exhaustive {
12-
Foo,
13-
Bar,
14-
Baz,
15-
Quux(String),
16-
}
13+
Foo,
14+
Bar,
15+
Baz,
16+
Quux(String),
17+
}
1718

18-
// no warning, already non_exhaustive
19-
#[non_exhaustive]
20-
pub enum NonExhaustive {
21-
Foo,
22-
Bar,
23-
Baz,
24-
Quux(String),
25-
}
19+
// no warning, already non_exhaustive
20+
#[non_exhaustive]
21+
pub enum NonExhaustive {
22+
Foo,
23+
Bar,
24+
Baz,
25+
Quux(String),
26+
}
27+
28+
// no warning, private
29+
enum ExhaustivePrivate {
30+
Foo,
31+
Bar,
32+
Baz,
33+
Quux(String),
34+
}
2635

27-
// no warning, private
28-
enum ExhaustivePrivate {
29-
Foo,
30-
Bar,
31-
Baz,
32-
Quux(String),
36+
// no warning, private
37+
#[non_exhaustive]
38+
enum NonExhaustivePrivate {
39+
Foo,
40+
Bar,
41+
Baz,
42+
Quux(String),
43+
}
3344
}
3445

35-
// no warning, private
36-
#[non_exhaustive]
37-
enum NonExhaustivePrivate {
38-
Foo,
39-
Bar,
40-
Baz,
41-
Quux(String),
46+
pub mod structs {
47+
#[non_exhaustive]
48+
pub struct Exhaustive {
49+
foo: u8,
50+
bar: String,
51+
}
52+
53+
// no warning, already non_exhaustive
54+
#[non_exhaustive]
55+
pub struct NonExhaustive {
56+
foo: u8,
57+
bar: String,
58+
}
59+
60+
// no warning, private
61+
struct ExhaustivePrivate {
62+
foo: u8,
63+
bar: String,
64+
}
65+
66+
// no warning, private
67+
#[non_exhaustive]
68+
struct NonExhaustivePrivate {
69+
foo: u8,
70+
bar: String,
71+
}
4272
}

tests/ui/exhaustive_items.rs

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,70 @@
11
// run-rustfix
22

3-
#![deny(clippy::exhaustive_enums)]
3+
#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
44
#![allow(unused)]
55

66
fn main() {
77
// nop
88
}
99

10-
pub enum Exhaustive {
11-
Foo,
12-
Bar,
13-
Baz,
14-
Quux(String),
15-
}
10+
pub mod enums {
11+
pub enum Exhaustive {
12+
Foo,
13+
Bar,
14+
Baz,
15+
Quux(String),
16+
}
1617

17-
// no warning, already non_exhaustive
18-
#[non_exhaustive]
19-
pub enum NonExhaustive {
20-
Foo,
21-
Bar,
22-
Baz,
23-
Quux(String),
24-
}
18+
// no warning, already non_exhaustive
19+
#[non_exhaustive]
20+
pub enum NonExhaustive {
21+
Foo,
22+
Bar,
23+
Baz,
24+
Quux(String),
25+
}
26+
27+
// no warning, private
28+
enum ExhaustivePrivate {
29+
Foo,
30+
Bar,
31+
Baz,
32+
Quux(String),
33+
}
2534

26-
// no warning, private
27-
enum ExhaustivePrivate {
28-
Foo,
29-
Bar,
30-
Baz,
31-
Quux(String),
35+
// no warning, private
36+
#[non_exhaustive]
37+
enum NonExhaustivePrivate {
38+
Foo,
39+
Bar,
40+
Baz,
41+
Quux(String),
42+
}
3243
}
3344

34-
// no warning, private
35-
#[non_exhaustive]
36-
enum NonExhaustivePrivate {
37-
Foo,
38-
Bar,
39-
Baz,
40-
Quux(String),
45+
pub mod structs {
46+
pub struct Exhaustive {
47+
foo: u8,
48+
bar: String,
49+
}
50+
51+
// no warning, already non_exhaustive
52+
#[non_exhaustive]
53+
pub struct NonExhaustive {
54+
foo: u8,
55+
bar: String,
56+
}
57+
58+
// no warning, private
59+
struct ExhaustivePrivate {
60+
foo: u8,
61+
bar: String,
62+
}
63+
64+
// no warning, private
65+
#[non_exhaustive]
66+
struct NonExhaustivePrivate {
67+
foo: u8,
68+
bar: String,
69+
}
4170
}

tests/ui/exhaustive_items.stderr

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,46 @@
11
error: enums should not be exhaustive
2-
--> $DIR/exhaustive_items.rs:10:1
2+
--> $DIR/exhaustive_items.rs:11:5
33
|
4-
LL | / pub enum Exhaustive {
5-
LL | | Foo,
6-
LL | | Bar,
7-
LL | | Baz,
8-
LL | | Quux(String),
9-
LL | | }
10-
| |_^
4+
LL | / pub enum Exhaustive {
5+
LL | | Foo,
6+
LL | | Bar,
7+
LL | | Baz,
8+
LL | | Quux(String),
9+
LL | | }
10+
| |_____^
1111
|
1212
note: the lint level is defined here
13-
--> $DIR/exhaustive_items.rs:3:9
13+
--> $DIR/exhaustive_items.rs:3:35
1414
|
15-
LL | #![deny(clippy::exhaustive_enums)]
16-
| ^^^^^^^^^^^^^^^^^^^^^^^^
15+
LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
1717
help: try adding #[non_exhaustive]
1818
|
19-
LL | #[non_exhaustive]
19+
LL | #[non_exhaustive]
2020
LL | pub enum Exhaustive {
21-
LL | Foo,
22-
LL | Bar,
23-
LL | Baz,
24-
LL | Quux(String),
21+
LL | Foo,
22+
LL | Bar,
23+
LL | Baz,
24+
LL | Quux(String),
2525
...
2626

27-
error: aborting due to previous error
27+
error: enums should not be exhaustive
28+
--> $DIR/exhaustive_items.rs:46:5
29+
|
30+
LL | / pub struct Exhaustive {
31+
LL | | foo: u8,
32+
LL | | bar: String,
33+
LL | | }
34+
| |_____^
35+
|
36+
help: try adding #[non_exhaustive]
37+
|
38+
LL | #[non_exhaustive]
39+
LL | pub struct Exhaustive {
40+
LL | foo: u8,
41+
LL | bar: String,
42+
LL | }
43+
|
44+
45+
error: aborting due to 2 previous errors
2846

0 commit comments

Comments
 (0)