Skip to content

Commit 9f5d60f

Browse files
committed
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.
2 parents 8065e0f + 3405ce3 commit 9f5d60f

6 files changed

+128
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5362,6 +5362,7 @@ Released 2018-09-13
53625362
[`extra_unused_type_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_type_parameters
53635363
[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
53645364
[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
5365+
[`field_scoped_visibility_modifiers`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_scoped_visibility_modifiers
53655366
[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
53665367
[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
53675368
[`filter_map_bool_then`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_bool_then

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
178178
crate::explicit_write::EXPLICIT_WRITE_INFO,
179179
crate::extra_unused_type_parameters::EXTRA_UNUSED_TYPE_PARAMETERS_INFO,
180180
crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO,
181+
crate::field_scoped_visibility_modifiers::FIELD_SCOPED_VISIBILITY_MODIFIERS_INFO,
181182
crate::float_literal::EXCESSIVE_PRECISION_INFO,
182183
crate::float_literal::LOSSY_FLOAT_LITERAL_INFO,
183184
crate::floating_point_arithmetic::IMPRECISE_FLOPS_INFO,
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use clippy_utils::diagnostics::span_lint_and_help;
2+
use rustc_ast::ast::{Item, ItemKind, VisibilityKind};
3+
use rustc_lint::{EarlyContext, EarlyLintPass};
4+
use rustc_session::declare_lint_pass;
5+
6+
declare_clippy_lint! {
7+
/// ### What it does
8+
/// Checks for usage of scoped visibility modifiers, like `pub(crate)`, on fields. These
9+
/// make a field visible within a scope between public and private.
10+
///
11+
/// ### Why restrict this?
12+
/// Scoped visibility modifiers cause a field to be accessible within some scope between
13+
/// public and private, potentially within an entire crate. This allows for fields to be
14+
/// non-private while upholding internal invariants, but can be a code smell. Scoped visibility
15+
/// requires checking a greater area, potentially an entire crate, to verify that an invariant
16+
/// is upheld, and global analysis requires a lot of effort.
17+
///
18+
/// ### Example
19+
/// ```no_run
20+
/// pub mod public_module {
21+
/// struct MyStruct {
22+
/// pub(crate) first_field: bool,
23+
/// pub(super) second_field: bool
24+
/// }
25+
/// }
26+
/// ```
27+
/// Use instead:
28+
/// ```no_run
29+
/// pub mod public_module {
30+
/// struct MyStruct {
31+
/// first_field: bool,
32+
/// second_field: bool
33+
/// }
34+
/// impl MyStruct {
35+
/// pub(crate) fn get_first_field(&self) -> bool {
36+
/// self.first_field
37+
/// }
38+
/// pub(super) fn get_second_field(&self) -> bool {
39+
/// self.second_field
40+
/// }
41+
/// }
42+
/// }
43+
/// ```
44+
#[clippy::version = "1.78.0"]
45+
pub FIELD_SCOPED_VISIBILITY_MODIFIERS,
46+
restriction,
47+
"checks for usage of a scoped visibility modifier, like `pub(crate)`, on fields"
48+
}
49+
50+
declare_lint_pass!(FieldScopedVisibilityModifiers => [FIELD_SCOPED_VISIBILITY_MODIFIERS]);
51+
52+
impl EarlyLintPass for FieldScopedVisibilityModifiers {
53+
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
54+
let ItemKind::Struct(ref st, _) = item.kind else {
55+
return;
56+
};
57+
for field in st.fields() {
58+
let VisibilityKind::Restricted { path, .. } = &field.vis.kind else {
59+
continue;
60+
};
61+
if !path.segments.is_empty() && path.segments[0].ident.name == rustc_span::symbol::kw::SelfLower {
62+
// pub(self) is equivalent to not using pub at all, so we ignore it
63+
continue;
64+
}
65+
span_lint_and_help(
66+
cx,
67+
FIELD_SCOPED_VISIBILITY_MODIFIERS,
68+
field.vis.span,
69+
"scoped visibility modifier on a field",
70+
None,
71+
"consider making the field private and adding a scoped visibility method for it",
72+
);
73+
}
74+
}
75+
}

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ mod exit;
136136
mod explicit_write;
137137
mod extra_unused_type_parameters;
138138
mod fallible_impl_from;
139+
mod field_scoped_visibility_modifiers;
139140
mod float_literal;
140141
mod floating_point_arithmetic;
141142
mod format;
@@ -1169,6 +1170,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
11691170
})
11701171
});
11711172
store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(msrv())));
1173+
store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers));
11721174
// add lints here, do not remove this comment, it's used in `new_lint`
11731175
}
11741176

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![warn(clippy::field_scoped_visibility_modifiers)]
2+
3+
pub mod pub_module {
4+
pub(in crate::pub_module) mod pub_in_path_module {}
5+
pub(super) mod pub_super_module {}
6+
struct MyStruct {
7+
private_field: bool,
8+
pub pub_field: bool,
9+
pub(crate) pub_crate_field: bool,
10+
pub(in crate::pub_module) pub_in_path_field: bool,
11+
pub(super) pub_super_field: bool,
12+
#[allow(clippy::needless_pub_self)]
13+
pub(self) pub_self_field: bool,
14+
}
15+
}
16+
pub(crate) mod pub_crate_module {}
17+
18+
#[allow(clippy::needless_pub_self)]
19+
pub(self) mod pub_self_module {}
20+
21+
fn main() {}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error: scoped visibility modifier on a field
2+
--> tests/ui/field_scoped_visibility_modifiers.rs:9:9
3+
|
4+
LL | pub(crate) pub_crate_field: bool,
5+
| ^^^^^^^^^^
6+
|
7+
= help: consider making the field private and adding a scoped visibility method for it
8+
= note: `-D clippy::field-scoped-visibility-modifiers` implied by `-D warnings`
9+
= help: to override `-D warnings` add `#[allow(clippy::field_scoped_visibility_modifiers)]`
10+
11+
error: scoped visibility modifier on a field
12+
--> tests/ui/field_scoped_visibility_modifiers.rs:10:9
13+
|
14+
LL | pub(in crate::pub_module) pub_in_path_field: bool,
15+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
16+
|
17+
= help: consider making the field private and adding a scoped visibility method for it
18+
19+
error: scoped visibility modifier on a field
20+
--> tests/ui/field_scoped_visibility_modifiers.rs:11:9
21+
|
22+
LL | pub(super) pub_super_field: bool,
23+
| ^^^^^^^^^^
24+
|
25+
= help: consider making the field private and adding a scoped visibility method for it
26+
27+
error: aborting due to 3 previous errors
28+

0 commit comments

Comments
 (0)