about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-06-16 20:14:20 +0000
committerbors <bors@rust-lang.org>2024-06-16 20:14:20 +0000
commit9f5d60f160afe9d88fd244610fcc1376a8f7d5e3 (patch)
treec481fc2c8f8ba84df734925f1a8a0e4976c31687
parent8065e0f61c26d9d8649fb063fabcf5d40a479fcb (diff)
parent3405ce3bcaeb445bc780a5ad863524dea151a385 (diff)
downloadrust-9f5d60f160afe9d88fd244610fcc1376a8f7d5e3.tar.gz
rust-9f5d60f160afe9d88fd244610fcc1376a8f7d5e3.zip
Auto merge of #12893 - kyleoneill:field_scoped_visibility_modifiers, r=blyxyas
Add field_scoped_visibility_modifiers lint

changelog: [`field_scoped_visibility_modifiers`]: Add a lint which checks for struct fields using Restricted (not inherited, not public) visibility modifiers.
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/declared_lints.rs1
-rw-r--r--clippy_lints/src/field_scoped_visibility_modifiers.rs75
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--tests/ui/field_scoped_visibility_modifiers.rs21
-rw-r--r--tests/ui/field_scoped_visibility_modifiers.stderr28
6 files changed, 128 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d7bcd7a1968..4de3124bd3d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5362,6 +5362,7 @@ Released 2018-09-13
 [`extra_unused_type_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_type_parameters
 [`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
 [`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
+[`field_scoped_visibility_modifiers`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_scoped_visibility_modifiers
 [`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
 [`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
 [`filter_map_bool_then`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_bool_then
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index 7e43a99e9f2..88986f9ad72 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -178,6 +178,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::explicit_write::EXPLICIT_WRITE_INFO,
     crate::extra_unused_type_parameters::EXTRA_UNUSED_TYPE_PARAMETERS_INFO,
     crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO,
+    crate::field_scoped_visibility_modifiers::FIELD_SCOPED_VISIBILITY_MODIFIERS_INFO,
     crate::float_literal::EXCESSIVE_PRECISION_INFO,
     crate::float_literal::LOSSY_FLOAT_LITERAL_INFO,
     crate::floating_point_arithmetic::IMPRECISE_FLOPS_INFO,
diff --git a/clippy_lints/src/field_scoped_visibility_modifiers.rs b/clippy_lints/src/field_scoped_visibility_modifiers.rs
new file mode 100644
index 00000000000..bb74e345703
--- /dev/null
+++ b/clippy_lints/src/field_scoped_visibility_modifiers.rs
@@ -0,0 +1,75 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_ast::ast::{Item, ItemKind, VisibilityKind};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::declare_lint_pass;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usage of scoped visibility modifiers, like `pub(crate)`, on fields. These
+    /// make a field visible within a scope between public and private.
+    ///
+    /// ### Why restrict this?
+    /// Scoped visibility modifiers cause a field to be accessible within some scope between
+    /// public and private, potentially within an entire crate. This allows for fields to be
+    /// non-private while upholding internal invariants, but can be a code smell. Scoped visibility
+    /// requires checking a greater area, potentially an entire crate, to verify that an invariant
+    /// is upheld, and global analysis requires a lot of effort.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// pub mod public_module {
+    ///     struct MyStruct {
+    ///         pub(crate) first_field: bool,
+    ///         pub(super) second_field: bool
+    ///     }
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// pub mod public_module {
+    ///     struct MyStruct {
+    ///         first_field: bool,
+    ///         second_field: bool
+    ///     }
+    ///     impl MyStruct {
+    ///         pub(crate) fn get_first_field(&self) -> bool {
+    ///             self.first_field
+    ///         }
+    ///         pub(super) fn get_second_field(&self) -> bool {
+    ///             self.second_field
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    #[clippy::version = "1.78.0"]
+    pub FIELD_SCOPED_VISIBILITY_MODIFIERS,
+    restriction,
+    "checks for usage of a scoped visibility modifier, like `pub(crate)`, on fields"
+}
+
+declare_lint_pass!(FieldScopedVisibilityModifiers => [FIELD_SCOPED_VISIBILITY_MODIFIERS]);
+
+impl EarlyLintPass for FieldScopedVisibilityModifiers {
+    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+        let ItemKind::Struct(ref st, _) = item.kind else {
+            return;
+        };
+        for field in st.fields() {
+            let VisibilityKind::Restricted { path, .. } = &field.vis.kind else {
+                continue;
+            };
+            if !path.segments.is_empty() && path.segments[0].ident.name == rustc_span::symbol::kw::SelfLower {
+                // pub(self) is equivalent to not using pub at all, so we ignore it
+                continue;
+            }
+            span_lint_and_help(
+                cx,
+                FIELD_SCOPED_VISIBILITY_MODIFIERS,
+                field.vis.span,
+                "scoped visibility modifier on a field",
+                None,
+                "consider making the field private and adding a scoped visibility method for it",
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 50274a715ec..1bd6dfe2461 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -136,6 +136,7 @@ mod exit;
 mod explicit_write;
 mod extra_unused_type_parameters;
 mod fallible_impl_from;
+mod field_scoped_visibility_modifiers;
 mod float_literal;
 mod floating_point_arithmetic;
 mod format;
@@ -1169,6 +1170,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
         })
     });
     store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(msrv())));
+    store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/tests/ui/field_scoped_visibility_modifiers.rs b/tests/ui/field_scoped_visibility_modifiers.rs
new file mode 100644
index 00000000000..5789dbf9b1d
--- /dev/null
+++ b/tests/ui/field_scoped_visibility_modifiers.rs
@@ -0,0 +1,21 @@
+#![warn(clippy::field_scoped_visibility_modifiers)]
+
+pub mod pub_module {
+    pub(in crate::pub_module) mod pub_in_path_module {}
+    pub(super) mod pub_super_module {}
+    struct MyStruct {
+        private_field: bool,
+        pub pub_field: bool,
+        pub(crate) pub_crate_field: bool,
+        pub(in crate::pub_module) pub_in_path_field: bool,
+        pub(super) pub_super_field: bool,
+        #[allow(clippy::needless_pub_self)]
+        pub(self) pub_self_field: bool,
+    }
+}
+pub(crate) mod pub_crate_module {}
+
+#[allow(clippy::needless_pub_self)]
+pub(self) mod pub_self_module {}
+
+fn main() {}
diff --git a/tests/ui/field_scoped_visibility_modifiers.stderr b/tests/ui/field_scoped_visibility_modifiers.stderr
new file mode 100644
index 00000000000..beea6c92107
--- /dev/null
+++ b/tests/ui/field_scoped_visibility_modifiers.stderr
@@ -0,0 +1,28 @@
+error: scoped visibility modifier on a field
+  --> tests/ui/field_scoped_visibility_modifiers.rs:9:9
+   |
+LL |         pub(crate) pub_crate_field: bool,
+   |         ^^^^^^^^^^
+   |
+   = help: consider making the field private and adding a scoped visibility method for it
+   = note: `-D clippy::field-scoped-visibility-modifiers` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::field_scoped_visibility_modifiers)]`
+
+error: scoped visibility modifier on a field
+  --> tests/ui/field_scoped_visibility_modifiers.rs:10:9
+   |
+LL |         pub(in crate::pub_module) pub_in_path_field: bool,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider making the field private and adding a scoped visibility method for it
+
+error: scoped visibility modifier on a field
+  --> tests/ui/field_scoped_visibility_modifiers.rs:11:9
+   |
+LL |         pub(super) pub_super_field: bool,
+   |         ^^^^^^^^^^
+   |
+   = help: consider making the field private and adding a scoped visibility method for it
+
+error: aborting due to 3 previous errors
+