-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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`
- Loading branch information
Showing
6 changed files
with
332 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
); | ||
|
||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|