about summary refs log tree commit diff
path: root/clippy_lints/src/partial_pub_fields.rs
diff options
context:
space:
mode:
Diffstat (limited to 'clippy_lints/src/partial_pub_fields.rs')
-rw-r--r--clippy_lints/src/partial_pub_fields.rs81
1 files changed, 81 insertions, 0 deletions
diff --git a/clippy_lints/src/partial_pub_fields.rs b/clippy_lints/src/partial_pub_fields.rs
new file mode 100644
index 00000000000..f60d9d65b12
--- /dev/null
+++ b/clippy_lints/src/partial_pub_fields.rs
@@ -0,0 +1,81 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_ast::ast::{Item, ItemKind};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks whether partial fields of a struct are public.
+    ///
+    /// Either make all fields of a type public, or make none of them public
+    ///
+    /// ### Why is this bad?
+    /// Most types should either be:
+    /// * Abstract data types: complex objects with opaque implementation which guard
+    /// interior invariants and expose intentionally limited API to the outside world.
+    /// * Data: relatively simple objects which group a bunch of related attributes together.
+    ///
+    /// ### Example
+    /// ```rust
+    /// pub struct Color {
+    ///     pub r: u8,
+    ///     pub g: u8,
+    ///     b: u8,
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// pub struct Color {
+    ///     pub r: u8,
+    ///     pub g: u8,
+    ///     pub b: u8,
+    /// }
+    /// ```
+    #[clippy::version = "1.66.0"]
+    pub PARTIAL_PUB_FIELDS,
+    restriction,
+    "partial fields of a struct are public"
+}
+declare_lint_pass!(PartialPubFields => [PARTIAL_PUB_FIELDS]);
+
+impl EarlyLintPass for PartialPubFields {
+    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+        let ItemKind::Struct(ref st, _) = item.kind else {
+            return;
+        };
+
+        let mut fields = st.fields().iter();
+        let Some(first_field) = fields.next() else {
+            // Empty struct.
+            return;
+        };
+        let all_pub = first_field.vis.kind.is_pub();
+        let all_priv = !all_pub;
+
+        let msg = "mixed usage of pub and non-pub fields";
+
+        for field in fields {
+            if all_priv && field.vis.kind.is_pub() {
+                span_lint_and_help(
+                    cx,
+                    PARTIAL_PUB_FIELDS,
+                    field.vis.span,
+                    msg,
+                    None,
+                    "consider using private field here",
+                );
+                return;
+            } else if all_pub && !field.vis.kind.is_pub() {
+                span_lint_and_help(
+                    cx,
+                    PARTIAL_PUB_FIELDS,
+                    field.vis.span,
+                    msg,
+                    None,
+                    "consider using public field here",
+                );
+                return;
+            }
+        }
+    }
+}