1+ use clippy_utils:: attrs:: is_doc_hidden;
12use 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 ;
48use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
59
610declare_clippy_lint ! {
@@ -20,13 +24,15 @@ declare_clippy_lint! {
2024 /// ```
2125 /// Use instead:
2226 /// ```rust
23- /// struct FileHandle_Foo {
27+ /// struct FileHandle {
2428 /// _descriptor: usize,
2529 /// }
26- ///
30+ /// ```
31+ ///
2732 /// // OR
28- ///
29- /// struct FileHandle_Bar {
33+ ///
34+ /// ```
35+ /// struct FileHandle {
3036 /// pub descriptor: usize,
3137 /// }
3238 /// ```
@@ -37,21 +43,36 @@ declare_clippy_lint! {
3743}
3844declare_lint_pass ! ( PubUnderscoreFields => [ PUB_UNDERSCORE_FIELDS ] ) ;
3945
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 {
4450 return ;
4551 } ;
4652
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+ {
5172 span_lint_and_help (
5273 cx,
5374 PUB_UNDERSCORE_FIELDS ,
54- field. vis . span . to ( ident. span ) ,
75+ field. vis_span . to ( field . ident . span ) ,
5576 "field marked as public but also inferred as unused because it's prefixed with `_`" ,
5677 None ,
5778 "consider removing the underscore, or making the field private" ,
0 commit comments