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`
This commit is contained in:
@@ -1938,6 +1938,8 @@ Released 2018-09-13
|
|||||||
[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
|
[`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
|
[`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
|
[`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
|
[`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_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
|
[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
|
||||||
|
|||||||
102
clippy_lints/src/exhaustive_items.rs
Normal file
102
clippy_lints/src/exhaustive_items.rs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
use crate::utils::{indent_of, span_lint_and_then};
|
||||||
|
use if_chain::if_chain;
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 exported 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,
|
||||||
|
"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, EXHAUSTIVE_STRUCTS]);
|
||||||
|
|
||||||
|
impl LateLintPass<'_> for ExhaustiveItems {
|
||||||
|
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||||
|
if_chain! {
|
||||||
|
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, msg) = if let ItemKind::Enum(..) = item.kind {
|
||||||
|
(EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive")
|
||||||
|
} else {
|
||||||
|
(EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive")
|
||||||
|
};
|
||||||
|
let suggestion_span = item.span.shrink_to_lo();
|
||||||
|
let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0));
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
lint,
|
||||||
|
item.span,
|
||||||
|
msg,
|
||||||
|
|diag| {
|
||||||
|
let sugg = format!("#[non_exhaustive]\n{}", indent);
|
||||||
|
diag.span_suggestion(suggestion_span,
|
||||||
|
"try adding #[non_exhaustive]",
|
||||||
|
sugg,
|
||||||
|
Applicability::MaybeIncorrect);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -200,6 +200,7 @@ mod escape;
|
|||||||
mod eta_reduction;
|
mod eta_reduction;
|
||||||
mod eval_order_dependence;
|
mod eval_order_dependence;
|
||||||
mod excessive_bools;
|
mod excessive_bools;
|
||||||
|
mod exhaustive_items;
|
||||||
mod exit;
|
mod exit;
|
||||||
mod explicit_write;
|
mod explicit_write;
|
||||||
mod fallible_impl_from;
|
mod fallible_impl_from;
|
||||||
@@ -615,6 +616,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
&eval_order_dependence::EVAL_ORDER_DEPENDENCE,
|
&eval_order_dependence::EVAL_ORDER_DEPENDENCE,
|
||||||
&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
|
&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
|
||||||
&excessive_bools::STRUCT_EXCESSIVE_BOOLS,
|
&excessive_bools::STRUCT_EXCESSIVE_BOOLS,
|
||||||
|
&exhaustive_items::EXHAUSTIVE_ENUMS,
|
||||||
|
&exhaustive_items::EXHAUSTIVE_STRUCTS,
|
||||||
&exit::EXIT,
|
&exit::EXIT,
|
||||||
&explicit_write::EXPLICIT_WRITE,
|
&explicit_write::EXPLICIT_WRITE,
|
||||||
&fallible_impl_from::FALLIBLE_IMPL_FROM,
|
&fallible_impl_from::FALLIBLE_IMPL_FROM,
|
||||||
@@ -1101,6 +1104,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 eval_order_dependence::EvalOrderDependence);
|
||||||
store.register_late_pass(|| box missing_doc::MissingDoc::new());
|
store.register_late_pass(|| box missing_doc::MissingDoc::new());
|
||||||
store.register_late_pass(|| box missing_inline::MissingInline);
|
store.register_late_pass(|| box missing_inline::MissingInline);
|
||||||
|
store.register_late_pass(move || box exhaustive_items::ExhaustiveItems);
|
||||||
store.register_late_pass(|| box if_let_some_result::OkIfLet);
|
store.register_late_pass(|| box if_let_some_result::OkIfLet);
|
||||||
store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl);
|
store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl);
|
||||||
store.register_late_pass(|| box unused_io_amount::UnusedIoAmount);
|
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:
|
|||||||
LintId::of(&create_dir::CREATE_DIR),
|
LintId::of(&create_dir::CREATE_DIR),
|
||||||
LintId::of(&dbg_macro::DBG_MACRO),
|
LintId::of(&dbg_macro::DBG_MACRO),
|
||||||
LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
|
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(&exit::EXIT),
|
||||||
LintId::of(&float_literal::LOSSY_FLOAT_LITERAL),
|
LintId::of(&float_literal::LOSSY_FLOAT_LITERAL),
|
||||||
LintId::of(&implicit_return::IMPLICIT_RETURN),
|
LintId::of(&implicit_return::IMPLICIT_RETURN),
|
||||||
|
|||||||
82
tests/ui/exhaustive_items.fixed
Normal file
82
tests/ui/exhaustive_items.fixed
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// nop
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod enums {
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum Exhaustive {
|
||||||
|
Foo,
|
||||||
|
Bar,
|
||||||
|
Baz,
|
||||||
|
Quux(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Some docs
|
||||||
|
#[repr(C)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum ExhaustiveWithAttrs {
|
||||||
|
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
|
||||||
|
#[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,
|
||||||
|
}
|
||||||
|
}
|
||||||
79
tests/ui/exhaustive_items.rs
Normal file
79
tests/ui/exhaustive_items.rs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// nop
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod enums {
|
||||||
|
pub enum Exhaustive {
|
||||||
|
Foo,
|
||||||
|
Bar,
|
||||||
|
Baz,
|
||||||
|
Quux(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Some docs
|
||||||
|
#[repr(C)]
|
||||||
|
pub enum ExhaustiveWithAttrs {
|
||||||
|
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
|
||||||
|
#[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,
|
||||||
|
}
|
||||||
|
}
|
||||||
61
tests/ui/exhaustive_items.stderr
Normal file
61
tests/ui/exhaustive_items.stderr
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
error: exported enums should not be exhaustive
|
||||||
|
--> $DIR/exhaustive_items.rs:11:5
|
||||||
|
|
|
||||||
|
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
|
||||||
|
|
|
||||||
|
LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
help: try adding #[non_exhaustive]
|
||||||
|
|
|
||||||
|
LL | #[non_exhaustive]
|
||||||
|
LL | pub enum Exhaustive {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: exported enums should not be exhaustive
|
||||||
|
--> $DIR/exhaustive_items.rs:20:5
|
||||||
|
|
|
||||||
|
LL | / pub enum ExhaustiveWithAttrs {
|
||||||
|
LL | | Foo,
|
||||||
|
LL | | Bar,
|
||||||
|
LL | | Baz,
|
||||||
|
LL | | Quux(String),
|
||||||
|
LL | | }
|
||||||
|
| |_____^
|
||||||
|
|
|
||||||
|
help: try adding #[non_exhaustive]
|
||||||
|
|
|
||||||
|
LL | #[non_exhaustive]
|
||||||
|
LL | pub enum ExhaustiveWithAttrs {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: exported structs should not be exhaustive
|
||||||
|
--> $DIR/exhaustive_items.rs:55:5
|
||||||
|
|
|
||||||
|
LL | / pub struct Exhaustive {
|
||||||
|
LL | | foo: u8,
|
||||||
|
LL | | bar: String,
|
||||||
|
LL | | }
|
||||||
|
| |_____^
|
||||||
|
|
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/exhaustive_items.rs:3:35
|
||||||
|
|
|
||||||
|
LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
help: try adding #[non_exhaustive]
|
||||||
|
|
|
||||||
|
LL | #[non_exhaustive]
|
||||||
|
LL | pub struct Exhaustive {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
Reference in New Issue
Block a user