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:
bors 2021-01-25 23:06:39 +00:00
commit 8d57cee9ca
6 changed files with 332 additions and 0 deletions

View File

@ -1938,6 +1938,8 @@ Released 2018-09-13
[`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
[`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

View 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);
}
);
}
}
}
}

View File

@ -200,6 +200,7 @@ macro_rules! declare_clippy_lint {
mod eta_reduction;
mod eval_order_dependence;
mod excessive_bools;
mod exhaustive_items;
mod exit;
mod explicit_write;
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,
&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,
@ -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 missing_doc::MissingDoc::new());
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 partialeq_ne_impl::PartialEqNeImpl);
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(&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),

View 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,
}
}

View 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,
}
}

View 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