1
+ use clippy_utils:: attrs:: is_doc_hidden;
1
2
use clippy_utils:: diagnostics:: span_lint_and_help;
2
- use rustc_ast:: ast:: { Item , ItemKind , VisibilityKind } ;
3
- use rustc_lint:: { EarlyContext , EarlyLintPass } ;
3
+ use clippy_utils:: is_path_lang_item;
4
+ // use rustc_ast::ast::{Item, ItemKind, VisibilityKind};
5
+ use rustc_hir:: { FieldDef , Item , ItemKind , LangItem } ;
6
+ use rustc_lint:: { LateContext , LateLintPass } ;
7
+ use rustc_middle:: ty:: Visibility ;
4
8
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
5
9
6
10
declare_clippy_lint ! {
@@ -20,13 +24,15 @@ declare_clippy_lint! {
20
24
/// ```
21
25
/// Use instead:
22
26
/// ```rust
23
- /// struct FileHandle_Foo {
27
+ /// struct FileHandle {
24
28
/// _descriptor: usize,
25
29
/// }
26
- ///
30
+ /// ```
31
+ ///
27
32
/// // OR
28
- ///
29
- /// struct FileHandle_Bar {
33
+ ///
34
+ /// ```
35
+ /// struct FileHandle {
30
36
/// pub descriptor: usize,
31
37
/// }
32
38
/// ```
@@ -37,21 +43,36 @@ declare_clippy_lint! {
37
43
}
38
44
declare_lint_pass ! ( PubUnderscoreFields => [ PUB_UNDERSCORE_FIELDS ] ) ;
39
45
40
- impl EarlyLintPass for PubUnderscoreFields {
41
- fn check_item ( & mut self , cx : & EarlyContext < ' _ > , item : & Item ) {
42
- // this only pertains to structs
43
- let ItemKind :: Struct ( ref st , _) = item. kind else {
46
+ impl < ' tcx > LateLintPass < ' tcx > for PubUnderscoreFields {
47
+ fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > ) {
48
+ // This lint only pertains to structs.
49
+ let ItemKind :: Struct ( var , _) = & item. kind else {
44
50
return ;
45
51
} ;
46
52
47
- for field in st. fields ( ) {
48
- if let Some ( ident) = field. ident . as_ref ( )
49
- && ident. as_str ( ) . starts_with ( '_' )
50
- && !matches ! ( field. vis. kind, VisibilityKind :: Inherited ) {
53
+ let is_visible = |field : & FieldDef < ' _ > | {
54
+ let parent = cx. tcx . parent_module_from_def_id ( field. def_id ) ;
55
+ let grandparent = cx. tcx . parent_module_from_def_id ( parent. into ( ) ) ;
56
+ let visibility = cx. tcx . visibility ( field. def_id ) ;
57
+
58
+ let case_1 = parent == grandparent && !field. vis_span . is_empty ( ) ;
59
+ let case_2 = visibility != Visibility :: Restricted ( parent. to_def_id ( ) ) ;
60
+
61
+ case_1 || case_2
62
+ } ;
63
+
64
+ for field in var. fields ( ) {
65
+ // Only pertains to fields that start with an underscore, and are visible publically.
66
+ if field. ident . as_str ( ) . starts_with ( '_' ) && is_visible ( & field)
67
+ // We ignore fields that have `#[doc(hidden)]`.
68
+ && !is_doc_hidden ( cx. tcx . hir ( ) . attrs ( field. hir_id ) )
69
+ // We ignore fields that are `PhantomData`.
70
+ && !is_path_lang_item ( cx, field. ty , LangItem :: PhantomData )
71
+ {
51
72
span_lint_and_help (
52
73
cx,
53
74
PUB_UNDERSCORE_FIELDS ,
54
- field. vis . span . to ( ident. span ) ,
75
+ field. vis_span . to ( field . ident . span ) ,
55
76
"field marked as public but also inferred as unused because it's prefixed with `_`" ,
56
77
None ,
57
78
"consider removing the underscore, or making the field private" ,
0 commit comments