Navigation Menu

Skip to content

Commit

Permalink
Add exhaustive_structs lint
Browse files Browse the repository at this point in the history
  • Loading branch information
Manishearth committed Jan 22, 2021
1 parent 09d4d49 commit 8cb7e85
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 81 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -1939,6 +1939,7 @@ Released 2018-09-13
[`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
[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
[`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
Expand Down
54 changes: 46 additions & 8 deletions clippy_lints/src/exhaustive_items.rs
@@ -1,7 +1,7 @@
use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_hir::{Item, ItemKind};
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
Expand All @@ -10,7 +10,8 @@ declare_clippy_lint! {
/// **What it does:** Warns on any exported `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.
/// not wish to make a stability commitment around exported enums may wish to
/// disable them by default.
///
/// **Known problems:** None.
///
Expand All @@ -28,25 +29,62 @@ declare_clippy_lint! {
/// enum Foo {
/// Bar,
/// Baz
/// } /// ```
/// }
/// ```
pub EXHAUSTIVE_ENUMS,
restriction,
"default lint description"
"detects exported enums that have not been marked #[non_exhaustive]"
}

declare_clippy_lint! {
/// **What it does:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`
///
/// **Why is this bad?** Exhaustive structs are typically fine, but a project which does
/// not wish to make a stability commitment around exported structs may wish to
/// disable them by default.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// struct Foo {
/// bar: u8,
/// baz: String,
/// }
/// ```
/// Use instead:
/// ```rust
/// #[non_exhaustive]
/// struct Foo {
/// bar: u8,
/// baz: String,
/// }
/// ```
pub EXHAUSTIVE_STRUCTS,
restriction,
"detects exported structs that have not been marked #[non_exhaustive]"
}

declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS]);
declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]);

impl LateLintPass<'_> for ExhaustiveItems {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if_chain! {
if let ItemKind::Enum(..) = item.kind;
if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind;
if cx.access_levels.is_exported(item.hir_id);
if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive));
then {
let lint = if let ItemKind::Enum(..) = item.kind {
EXHAUSTIVE_ENUMS
} else {
EXHAUSTIVE_STRUCTS
};

if let Some(snippet) = snippet_opt(cx, item.span) {
span_lint_and_sugg(
cx,
EXHAUSTIVE_ENUMS,
lint,
item.span,
"enums should not be exhaustive",
"try adding #[non_exhaustive]",
Expand All @@ -56,7 +94,7 @@ impl LateLintPass<'_> for ExhaustiveItems {
} else {
span_lint_and_help(
cx,
EXHAUSTIVE_ENUMS,
lint,
item.span,
"enums should not be exhaustive",
None,
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Expand Up @@ -613,6 +613,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
&excessive_bools::STRUCT_EXCESSIVE_BOOLS,
&exhaustive_items::EXHAUSTIVE_ENUMS,
&exhaustive_items::EXHAUSTIVE_STRUCTS,
&exit::EXIT,
&explicit_write::EXPLICIT_WRITE,
&fallible_impl_from::FALLIBLE_IMPL_FROM,
Expand Down Expand Up @@ -1250,6 +1251,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&dbg_macro::DBG_MACRO),
LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
LintId::of(&exhaustive_items::EXHAUSTIVE_ENUMS),
LintId::of(&exhaustive_items::EXHAUSTIVE_STRUCTS),
LintId::of(&exit::EXIT),
LintId::of(&float_literal::LOSSY_FLOAT_LITERAL),
LintId::of(&implicit_return::IMPLICIT_RETURN),
Expand Down
86 changes: 58 additions & 28 deletions tests/ui/exhaustive_items.fixed
@@ -1,42 +1,72 @@
// run-rustfix

#![deny(clippy::exhaustive_enums)]
#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
#![allow(unused)]

fn main() {
// nop
}

#[non_exhaustive]
pub mod enums {
#[non_exhaustive]
pub enum Exhaustive {
Foo,
Bar,
Baz,
Quux(String),
}
Foo,
Bar,
Baz,
Quux(String),
}

// no warning, already non_exhaustive
#[non_exhaustive]
pub enum NonExhaustive {
Foo,
Bar,
Baz,
Quux(String),
}
// no warning, already non_exhaustive
#[non_exhaustive]
pub enum NonExhaustive {
Foo,
Bar,
Baz,
Quux(String),
}

// no warning, private
enum ExhaustivePrivate {
Foo,
Bar,
Baz,
Quux(String),
}

// no warning, private
enum ExhaustivePrivate {
Foo,
Bar,
Baz,
Quux(String),
// no warning, private
#[non_exhaustive]
enum NonExhaustivePrivate {
Foo,
Bar,
Baz,
Quux(String),
}
}

// no warning, private
#[non_exhaustive]
enum NonExhaustivePrivate {
Foo,
Bar,
Baz,
Quux(String),
pub mod structs {
#[non_exhaustive]
pub struct Exhaustive {
foo: u8,
bar: String,
}

// no warning, already non_exhaustive
#[non_exhaustive]
pub struct NonExhaustive {
foo: u8,
bar: String,
}

// no warning, private
struct ExhaustivePrivate {
foo: u8,
bar: String,
}

// no warning, private
#[non_exhaustive]
struct NonExhaustivePrivate {
foo: u8,
bar: String,
}
}
85 changes: 57 additions & 28 deletions tests/ui/exhaustive_items.rs
@@ -1,41 +1,70 @@
// run-rustfix

#![deny(clippy::exhaustive_enums)]
#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
#![allow(unused)]

fn main() {
// nop
}

pub enum Exhaustive {
Foo,
Bar,
Baz,
Quux(String),
}
pub mod enums {
pub enum Exhaustive {
Foo,
Bar,
Baz,
Quux(String),
}

// no warning, already non_exhaustive
#[non_exhaustive]
pub enum NonExhaustive {
Foo,
Bar,
Baz,
Quux(String),
}
// no warning, already non_exhaustive
#[non_exhaustive]
pub enum NonExhaustive {
Foo,
Bar,
Baz,
Quux(String),
}

// no warning, private
enum ExhaustivePrivate {
Foo,
Bar,
Baz,
Quux(String),
}

// no warning, private
enum ExhaustivePrivate {
Foo,
Bar,
Baz,
Quux(String),
// no warning, private
#[non_exhaustive]
enum NonExhaustivePrivate {
Foo,
Bar,
Baz,
Quux(String),
}
}

// no warning, private
#[non_exhaustive]
enum NonExhaustivePrivate {
Foo,
Bar,
Baz,
Quux(String),
pub mod structs {
pub struct Exhaustive {
foo: u8,
bar: String,
}

// no warning, already non_exhaustive
#[non_exhaustive]
pub struct NonExhaustive {
foo: u8,
bar: String,
}

// no warning, private
struct ExhaustivePrivate {
foo: u8,
bar: String,
}

// no warning, private
#[non_exhaustive]
struct NonExhaustivePrivate {
foo: u8,
bar: String,
}
}
52 changes: 35 additions & 17 deletions tests/ui/exhaustive_items.stderr
@@ -1,28 +1,46 @@
error: enums should not be exhaustive
--> $DIR/exhaustive_items.rs:10:1
--> $DIR/exhaustive_items.rs:11:5
|
LL | / pub enum Exhaustive {
LL | | Foo,
LL | | Bar,
LL | | Baz,
LL | | Quux(String),
LL | | }
| |_^
LL | / pub enum Exhaustive {
LL | | Foo,
LL | | Bar,
LL | | Baz,
LL | | Quux(String),
LL | | }
| |_____^
|
note: the lint level is defined here
--> $DIR/exhaustive_items.rs:3:9
--> $DIR/exhaustive_items.rs:3:35
|
LL | #![deny(clippy::exhaustive_enums)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
help: try adding #[non_exhaustive]
|
LL | #[non_exhaustive]
LL | #[non_exhaustive]
LL | pub enum Exhaustive {
LL | Foo,
LL | Bar,
LL | Baz,
LL | Quux(String),
LL | Foo,
LL | Bar,
LL | Baz,
LL | Quux(String),
...

error: aborting due to previous error
error: enums should not be exhaustive
--> $DIR/exhaustive_items.rs:46:5
|
LL | / pub struct Exhaustive {
LL | | foo: u8,
LL | | bar: String,
LL | | }
| |_____^
|
help: try adding #[non_exhaustive]
|
LL | #[non_exhaustive]
LL | pub struct Exhaustive {
LL | foo: u8,
LL | bar: String,
LL | }
|

error: aborting due to 2 previous errors

0 comments on commit 8cb7e85

Please sign in to comment.