Skip to content

Commit c9a2555

Browse files
committed
add a new lint, pub_underscore_fields
- add a new late pass lint - add ui tests - update CHANGELOG.md
1 parent 7624045 commit c9a2555

File tree

6 files changed

+196
-0
lines changed

6 files changed

+196
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5357,6 +5357,7 @@ Released 2018-09-13
53575357
[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
53585358
[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
53595359
[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
5360+
[`pub_underscore_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields
53605361
[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
53615362
[`pub_with_shorthand`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_with_shorthand
53625363
[`pub_without_shorthand`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_without_shorthand

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
561561
crate::ptr::MUT_FROM_REF_INFO,
562562
crate::ptr::PTR_ARG_INFO,
563563
crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO,
564+
crate::pub_underscore_fields::PUB_UNDERSCORE_FIELDS_INFO,
564565
crate::pub_use::PUB_USE_INFO,
565566
crate::question_mark::QUESTION_MARK_INFO,
566567
crate::question_mark_used::QUESTION_MARK_USED_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ mod permissions_set_readonly_false;
271271
mod precedence;
272272
mod ptr;
273273
mod ptr_offset_with_cast;
274+
mod pub_underscore_fields;
274275
mod pub_use;
275276
mod question_mark;
276277
mod question_mark_used;
@@ -1123,6 +1124,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11231124
});
11241125
store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(msrv())));
11251126
store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter));
1127+
store.register_late_pass(|_| Box::new(pub_underscore_fields::PubUnderscoreFields));
11261128
// add lints here, do not remove this comment, it's used in `new_lint`
11271129
}
11281130

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use clippy_utils::attrs::is_doc_hidden;
2+
use clippy_utils::diagnostics::span_lint_and_help;
3+
use clippy_utils::is_path_lang_item;
4+
use rustc_hir::{FieldDef, Item, ItemKind, LangItem};
5+
use rustc_lint::{LateContext, LateLintPass};
6+
use rustc_middle::ty::Visibility;
7+
use rustc_session::{declare_lint_pass, declare_tool_lint};
8+
9+
declare_clippy_lint! {
10+
/// ### What it does
11+
/// Checks whether any field of the struct is prefixed with an `_` (underscore) and also marked
12+
/// `pub` (public)
13+
///
14+
/// ### Why is this bad?
15+
/// Fields prefixed with an `_` are inferred as unused, which suggests it should not be marked
16+
/// as `pub`, because marking it as `pub` infers it will be used.
17+
///
18+
/// ### Example
19+
/// ```rust
20+
/// struct FileHandle {
21+
/// pub _descriptor: usize,
22+
/// }
23+
/// ```
24+
/// Use instead:
25+
/// ```rust
26+
/// struct FileHandle {
27+
/// _descriptor: usize,
28+
/// }
29+
/// ```
30+
///
31+
/// // OR
32+
///
33+
/// ```rust
34+
/// struct FileHandle {
35+
/// pub descriptor: usize,
36+
/// }
37+
/// ```
38+
#[clippy::version = "1.75.0"]
39+
pub PUB_UNDERSCORE_FIELDS,
40+
pedantic,
41+
"struct field prefixed with underscore and marked public"
42+
}
43+
declare_lint_pass!(PubUnderscoreFields => [PUB_UNDERSCORE_FIELDS]);
44+
45+
impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields {
46+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
47+
// This lint only pertains to structs.
48+
let ItemKind::Struct(variant_data, _) = &item.kind else {
49+
return;
50+
};
51+
52+
let is_visible = |field: &FieldDef<'_>| {
53+
let parent = cx.tcx.parent_module_from_def_id(field.def_id);
54+
let grandparent = cx.tcx.parent_module_from_def_id(parent.into());
55+
let visibility = cx.tcx.visibility(field.def_id);
56+
57+
let case_1 = parent == grandparent && !field.vis_span.is_empty();
58+
let case_2 = visibility != Visibility::Restricted(parent.to_def_id());
59+
60+
case_1 || case_2
61+
};
62+
63+
for field in variant_data.fields() {
64+
// Only pertains to fields that start with an underscore, and are public.
65+
if field.ident.as_str().starts_with('_') && is_visible(field)
66+
// We ignore fields that have `#[doc(hidden)]`.
67+
&& !is_doc_hidden(cx.tcx.hir().attrs(field.hir_id))
68+
// We ignore fields that are `PhantomData`.
69+
&& !is_path_lang_item(cx, field.ty, LangItem::PhantomData)
70+
{
71+
span_lint_and_help(
72+
cx,
73+
PUB_UNDERSCORE_FIELDS,
74+
field.vis_span.to(field.ident.span),
75+
"field marked as public but also inferred as unused because it's prefixed with `_`",
76+
None,
77+
"consider removing the underscore, or making the field private",
78+
);
79+
}
80+
}
81+
}
82+
}

tests/ui/pub_underscore_fields.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#![allow(unused)]
2+
#![warn(clippy::pub_underscore_fields)]
3+
4+
use std::marker::PhantomData;
5+
6+
mod inner {
7+
use std::marker;
8+
9+
pub struct PubSuper {
10+
pub(super) a: usize,
11+
pub(super) _b: u8,
12+
_c: i32,
13+
pub _mark: marker::PhantomData<u8>,
14+
}
15+
16+
mod inner2 {
17+
pub struct PubModVisibility {
18+
pub(in crate::inner) e: bool,
19+
pub(in crate::inner) _f: Option<()>,
20+
}
21+
}
22+
}
23+
24+
fn main() {
25+
pub struct StructWithOneViolation {
26+
pub _a: usize,
27+
}
28+
29+
// should handle structs with multiple violations
30+
pub struct StructWithMultipleViolations {
31+
a: u8,
32+
_b: usize,
33+
pub _c: i64,
34+
#[doc(hidden)]
35+
pub d: String,
36+
pub _e: Option<u8>,
37+
}
38+
39+
// shouldn't warn on anonymous fields
40+
pub struct AnonymousFields(pub usize, i32);
41+
42+
// don't warn on empty structs
43+
pub struct Empty1;
44+
pub struct Empty2();
45+
pub struct Empty3 {};
46+
47+
pub struct PubCrate {
48+
pub(crate) a: String,
49+
pub(crate) _b: Option<String>,
50+
}
51+
52+
// shouldn't warn on fields named pub
53+
pub struct NamedPub {
54+
r#pub: bool,
55+
_pub: String,
56+
pub(crate) _mark: PhantomData<u8>,
57+
}
58+
}

tests/ui/pub_underscore_fields.stderr

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
error: field marked as public but also inferred as unused because it's prefixed with `_`
2+
--> $DIR/pub_underscore_fields.rs:11:9
3+
|
4+
LL | pub(super) _b: u8,
5+
| ^^^^^^^^^^^^^
6+
|
7+
= help: consider removing the underscore, or making the field private
8+
= note: `-D clippy::pub-underscore-fields` implied by `-D warnings`
9+
= help: to override `-D warnings` add `#[allow(clippy::pub_underscore_fields)]`
10+
11+
error: field marked as public but also inferred as unused because it's prefixed with `_`
12+
--> $DIR/pub_underscore_fields.rs:19:13
13+
|
14+
LL | pub(in crate::inner) _f: Option<()>,
15+
| ^^^^^^^^^^^^^^^^^^^^^^^
16+
|
17+
= help: consider removing the underscore, or making the field private
18+
19+
error: field marked as public but also inferred as unused because it's prefixed with `_`
20+
--> $DIR/pub_underscore_fields.rs:26:9
21+
|
22+
LL | pub _a: usize,
23+
| ^^^^^^
24+
|
25+
= help: consider removing the underscore, or making the field private
26+
27+
error: field marked as public but also inferred as unused because it's prefixed with `_`
28+
--> $DIR/pub_underscore_fields.rs:33:9
29+
|
30+
LL | pub _c: i64,
31+
| ^^^^^^
32+
|
33+
= help: consider removing the underscore, or making the field private
34+
35+
error: field marked as public but also inferred as unused because it's prefixed with `_`
36+
--> $DIR/pub_underscore_fields.rs:36:9
37+
|
38+
LL | pub _e: Option<u8>,
39+
| ^^^^^^
40+
|
41+
= help: consider removing the underscore, or making the field private
42+
43+
error: field marked as public but also inferred as unused because it's prefixed with `_`
44+
--> $DIR/pub_underscore_fields.rs:49:9
45+
|
46+
LL | pub(crate) _b: Option<String>,
47+
| ^^^^^^^^^^^^^
48+
|
49+
= help: consider removing the underscore, or making the field private
50+
51+
error: aborting due to 6 previous errors
52+

0 commit comments

Comments
 (0)