about summary refs log tree commit diff
diff options
context:
space:
mode:
authorManish Goregaokar <manishsmail@gmail.com>2021-01-21 13:41:57 -0800
committerManish Goregaokar <manishsmail@gmail.com>2021-01-22 11:59:36 -0800
commit8cb7e85006853e99e6ba2bf378c801fb53eee8fa (patch)
tree5388c115b64ba03d5d712a5f55a228cdddc28037
parent09d4d49299c6614a0ae956980e709a234d21a9ef (diff)
downloadrust-8cb7e85006853e99e6ba2bf378c801fb53eee8fa.tar.gz
rust-8cb7e85006853e99e6ba2bf378c801fb53eee8fa.zip
Add exhaustive_structs lint
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/exhaustive_items.rs54
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--tests/ui/exhaustive_items.fixed86
-rw-r--r--tests/ui/exhaustive_items.rs85
-rw-r--r--tests/ui/exhaustive_items.stderr52
6 files changed, 199 insertions, 81 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4cf2125ea2f..ccc7eb7e881 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs
index 0fa6c4b589f..d604ad0004f 100644
--- a/clippy_lints/src/exhaustive_items.rs
+++ b/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;
@@ -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.
     ///
@@ -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]",
@@ -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,
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index a5deebf7d5f..91c74026f64 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -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,
@@ -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),
diff --git a/tests/ui/exhaustive_items.fixed b/tests/ui/exhaustive_items.fixed
index 71c4a251e3b..bc146c6afc5 100644
--- a/tests/ui/exhaustive_items.fixed
+++ b/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,
+    }
 }
diff --git a/tests/ui/exhaustive_items.rs b/tests/ui/exhaustive_items.rs
index 45af6851dd1..ed86b50be30 100644
--- a/tests/ui/exhaustive_items.rs
+++ b/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,
+    }
 }
diff --git a/tests/ui/exhaustive_items.stderr b/tests/ui/exhaustive_items.stderr
index d00d950efc7..7e286e65949 100644
--- a/tests/ui/exhaustive_items.stderr
+++ b/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