1- use  clippy_utils:: diagnostics :: span_lint_and_then ; 
2- use  clippy_utils:: source :: snippet_opt ; 
3- use  rustc_ast :: ast :: { Item ,   ItemKind ,   Variant ,   VariantData } ; 
1+ use  clippy_utils:: attrs :: span_contains_cfg ; 
2+ use  clippy_utils:: diagnostics :: { span_lint_and_then ,  span_lint_hir_and_then } ; 
3+ use  rustc_data_structures :: fx :: FxIndexMap ; 
44use  rustc_errors:: Applicability ; 
5- use  rustc_lexer:: TokenKind ; 
6- use  rustc_lint:: { EarlyContext ,  EarlyLintPass } ; 
7- use  rustc_session:: declare_lint_pass; 
5+ use  rustc_hir:: def:: CtorOf ; 
6+ use  rustc_hir:: def:: DefKind :: Ctor ; 
7+ use  rustc_hir:: def:: Res :: Def ; 
8+ use  rustc_hir:: def_id:: LocalDefId ; 
9+ use  rustc_hir:: { Expr ,  ExprKind ,  Item ,  ItemKind ,  Node ,  Path ,  QPath ,  Variant ,  VariantData } ; 
10+ use  rustc_lint:: { LateContext ,  LateLintPass } ; 
11+ use  rustc_middle:: ty:: TyCtxt ; 
12+ use  rustc_session:: impl_lint_pass; 
813use  rustc_span:: Span ; 
914
1015declare_clippy_lint !  { 
@@ -70,15 +75,28 @@ declare_clippy_lint! {
7075    "finds enum variants with empty brackets" 
7176} 
7277
73- declare_lint_pass ! ( EmptyWithBrackets  => [ EMPTY_STRUCTS_WITH_BRACKETS ,  EMPTY_ENUM_VARIANTS_WITH_BRACKETS ] ) ; 
78+ #[ derive( Debug ) ]  
79+ enum  Usage  { 
80+     Unused  {  redundant_use_sites :  Vec < Span >  } , 
81+     Used , 
82+     NoDefinition  {  redundant_use_sites :  Vec < Span >  } , 
83+ } 
84+ 
85+ #[ derive( Default ) ]  
86+ pub  struct  EmptyWithBrackets  { 
87+     // Value holds `Usage::Used` if the empty tuple variant was used as a function 
88+     empty_tuple_enum_variants :  FxIndexMap < LocalDefId ,  Usage > , 
89+ } 
90+ 
91+ impl_lint_pass ! ( EmptyWithBrackets  => [ EMPTY_STRUCTS_WITH_BRACKETS ,  EMPTY_ENUM_VARIANTS_WITH_BRACKETS ] ) ; 
7492
75- impl  EarlyLintPass  for  EmptyWithBrackets  { 
76-     fn  check_item ( & mut  self ,  cx :  & EarlyContext < ' _ > ,  item :  & Item )  { 
93+ impl  LateLintPass < ' _ >  for  EmptyWithBrackets  { 
94+     fn  check_item ( & mut  self ,  cx :  & LateContext < ' _ > ,  item :  & Item < ' _ > )  { 
7795        let  span_after_ident = item. span . with_lo ( item. ident . span . hi ( ) ) ; 
7896
7997        if  let  ItemKind :: Struct ( var_data,  _)  = & item. kind 
8098            && has_brackets ( var_data) 
81-             && has_no_fields ( cx,  var_data,  span_after_ident) 
99+             && has_no_fields ( cx,  Some ( var_data) ,  span_after_ident) 
82100        { 
83101            span_lint_and_then ( 
84102                cx, 
@@ -97,70 +115,166 @@ impl EarlyLintPass for EmptyWithBrackets {
97115        } 
98116    } 
99117
100-     fn  check_variant ( & mut  self ,  cx :  & EarlyContext < ' _ > ,  variant :  & Variant )  { 
118+     fn  check_variant ( & mut  self ,  cx :  & LateContext < ' _ > ,  variant :  & Variant < ' _ > )  { 
101119        let  span_after_ident = variant. span . with_lo ( variant. ident . span . hi ( ) ) ; 
102120
103-         if  has_brackets ( & variant. data )  && has_no_fields ( cx,  & variant. data ,  span_after_ident)  { 
104-             span_lint_and_then ( 
121+         if  has_no_fields ( cx,  Some ( & variant. data ) ,  span_after_ident)  { 
122+             match  variant. data  { 
123+                 VariantData :: Struct  {  .. }  => { 
124+                     span_lint_and_then ( 
125+                         cx, 
126+                         EMPTY_ENUM_VARIANTS_WITH_BRACKETS , 
127+                         span_after_ident, 
128+                         "enum variant has empty brackets" , 
129+                         |diagnostic| { 
130+                             diagnostic. span_suggestion_hidden ( 
131+                                 span_after_ident, 
132+                                 "remove the brackets" , 
133+                                 "" , 
134+                                 Applicability :: MaybeIncorrect , 
135+                             ) ; 
136+                         } , 
137+                     ) ; 
138+                 } , 
139+                 VariantData :: Tuple ( ..,  local_def_id)  => { 
140+                     // Don't lint reachable tuple enums 
141+                     if  cx. effective_visibilities . is_reachable ( variant. def_id )  { 
142+                         return ; 
143+                     } 
144+                     if  let  Some ( entry)  = self . empty_tuple_enum_variants . get_mut ( & local_def_id)  { 
145+                         if  let  Usage :: NoDefinition  {  redundant_use_sites }  = entry { 
146+                             * entry = Usage :: Unused  { 
147+                                 redundant_use_sites :  redundant_use_sites. clone ( ) , 
148+                             } ; 
149+                         } 
150+                     }  else  { 
151+                         self . empty_tuple_enum_variants . insert ( 
152+                             local_def_id, 
153+                             Usage :: Unused  { 
154+                                 redundant_use_sites :  vec ! [ ] , 
155+                             } , 
156+                         ) ; 
157+                     } 
158+                 } , 
159+                 VariantData :: Unit ( ..)  => { } , 
160+             } 
161+         } 
162+     } 
163+ 
164+     fn  check_expr ( & mut  self ,  cx :  & LateContext < ' _ > ,  expr :  & Expr < ' _ > )  { 
165+         if  let  Some ( def_id)  = check_expr_for_enum_as_function ( expr)  { 
166+             if  let  Some ( parentheses_span)  = call_parentheses_span ( cx. tcx ,  expr)  { 
167+                 // Do not count expressions from macro expansion as a redundant use site. 
168+                 if  expr. span . from_expansion ( )  { 
169+                     return ; 
170+                 } 
171+                 match  self . empty_tuple_enum_variants . get_mut ( & def_id)  { 
172+                     Some ( 
173+                         Usage :: Unused  { 
174+                             ref  mut  redundant_use_sites, 
175+                         } 
176+                         | Usage :: NoDefinition  { 
177+                             ref  mut  redundant_use_sites, 
178+                         } , 
179+                     )  => { 
180+                         redundant_use_sites. push ( parentheses_span) ; 
181+                     } , 
182+                     None  => { 
183+                         self . empty_tuple_enum_variants . insert ( 
184+                             def_id, 
185+                             Usage :: NoDefinition  { 
186+                                 redundant_use_sites :  vec ! [ parentheses_span] , 
187+                             } , 
188+                         ) ; 
189+                     } , 
190+                     _ => { } , 
191+                 } 
192+             }  else  { 
193+                 self . empty_tuple_enum_variants . insert ( def_id,  Usage :: Used ) ; 
194+             } 
195+         } 
196+     } 
197+ 
198+     fn  check_crate_post ( & mut  self ,  cx :  & LateContext < ' _ > )  { 
199+         for  ( local_def_id,  usage)  in  & self . empty_tuple_enum_variants  { 
200+             let  Usage :: Unused  {  redundant_use_sites }  = usage else  { 
201+                 continue ; 
202+             } ; 
203+             let  Node :: Variant ( variant)  = cx. tcx . hir_node ( 
204+                 cx. tcx 
205+                     . local_def_id_to_hir_id ( cx. tcx . parent ( local_def_id. to_def_id ( ) ) . expect_local ( ) ) , 
206+             )  else  { 
207+                 continue ; 
208+             } ; 
209+             let  span = variant. span . with_lo ( variant. ident . span . hi ( ) ) ; 
210+             span_lint_hir_and_then ( 
105211                cx, 
106212                EMPTY_ENUM_VARIANTS_WITH_BRACKETS , 
107-                 span_after_ident, 
213+                 variant. hir_id , 
214+                 span, 
108215                "enum variant has empty brackets" , 
109216                |diagnostic| { 
110-                     diagnostic. span_suggestion_hidden ( 
111-                         span_after_ident, 
112-                         "remove the brackets" , 
113-                         "" , 
114-                         Applicability :: MaybeIncorrect , 
115-                     ) ; 
217+                     if  redundant_use_sites. is_empty ( )  { 
218+                         diagnostic. span_suggestion_hidden ( 
219+                             span, 
220+                             "remove the brackets" , 
221+                             "" , 
222+                             Applicability :: MaybeIncorrect , 
223+                         ) ; 
224+                     }  else  { 
225+                         let  mut  parentheses_spans:  Vec < _ >  =
226+                             redundant_use_sites. iter ( ) . map ( |span| ( * span,  String :: new ( ) ) ) . collect ( ) ; 
227+                         parentheses_spans. push ( ( span,  String :: new ( ) ) ) ; 
228+                         diagnostic. multipart_suggestion ( 
229+                             "remove the brackets" , 
230+                             parentheses_spans, 
231+                             Applicability :: MaybeIncorrect , 
232+                         ) ; 
233+                     } 
116234                } , 
117235            ) ; 
118236        } 
119237    } 
120238} 
121239
122- fn  has_no_ident_token ( braces_span_str :  & str )  -> bool  { 
123-     !rustc_lexer :: tokenize ( braces_span_str ) . any ( |t| t . kind  ==  TokenKind :: Ident ) 
240+ fn  has_brackets ( var_data :  & VariantData < ' _ > )  -> bool  { 
241+     !matches ! ( var_data ,   VariantData :: Unit ( .. ) ) 
124242} 
125243
126- fn  has_brackets ( var_data :  & VariantData )  -> bool  { 
127-     !matches ! ( var_data,  VariantData :: Unit ( _) ) 
128- } 
129- 
130- fn  has_no_fields ( cx :  & EarlyContext < ' _ > ,  var_data :  & VariantData ,  braces_span :  Span )  -> bool  { 
131-     if  !var_data. fields ( ) . is_empty ( )  { 
244+ fn  has_no_fields ( cx :  & LateContext < ' _ > ,  var_data_opt :  Option < & VariantData < ' _ > > ,  braces_span :  Span )  -> bool  { 
245+     if  let  Some ( var_data)  = var_data_opt
246+         && !var_data. fields ( ) . is_empty ( ) 
247+     { 
132248        return  false ; 
133249    } 
134250
135251    // there might still be field declarations hidden from the AST 
136252    // (conditionally compiled code using #[cfg(..)]) 
137- 
138-     let  Some ( braces_span_str)  = snippet_opt ( cx,  braces_span)  else  { 
139-         return  false ; 
140-     } ; 
141- 
142-     has_no_ident_token ( braces_span_str. as_ref ( ) ) 
253+     !span_contains_cfg ( cx,  braces_span) 
143254} 
144255
145- #[ cfg( test) ]  
146- mod  unit_test { 
147-     use  super :: * ; 
148- 
149-     #[ test]  
150-     fn  test_has_no_ident_token ( )  { 
151-         let  input = "{ field: u8 }" ; 
152-         assert ! ( !has_no_ident_token( input) ) ; 
153- 
154-         let  input = "(u8, String);" ; 
155-         assert ! ( !has_no_ident_token( input) ) ; 
156- 
157-         let  input = " { 
158-                 // test = 5 
159-         } 
160-         " ; 
161-         assert ! ( has_no_ident_token( input) ) ; 
256+ fn  call_parentheses_span ( tcx :  TyCtxt < ' _ > ,  expr :  & Expr < ' _ > )  -> Option < Span >  { 
257+     if  let  Node :: Expr ( parent)  = tcx. parent_hir_node ( expr. hir_id ) 
258+         && let  ExprKind :: Call ( callee,  ..)  = parent. kind 
259+         && callee. hir_id  == expr. hir_id 
260+     { 
261+         Some ( parent. span . with_lo ( expr. span . hi ( ) ) ) 
262+     }  else  { 
263+         None 
264+     } 
265+ } 
162266
163-         let  input = " ();" ; 
164-         assert ! ( has_no_ident_token( input) ) ; 
267+ fn  check_expr_for_enum_as_function ( expr :  & Expr < ' _ > )  -> Option < LocalDefId >  { 
268+     if  let  ExprKind :: Path ( QPath :: Resolved ( 
269+         _, 
270+         Path  { 
271+             res :  Def ( Ctor ( CtorOf :: Variant ,  _) ,  def_id) , 
272+             ..
273+         } , 
274+     ) )  = expr. kind 
275+     { 
276+         def_id. as_local ( ) 
277+     }  else  { 
278+         None 
165279    } 
166280} 
0 commit comments