11use clippy_utils:: diagnostics:: span_lint_and_then;
22use clippy_utils:: source:: snippet_opt;
3- use rustc_ast :: ast :: { Item , ItemKind , Variant , VariantData } ;
3+ use rustc_data_structures :: fx :: FxHashSet ;
44use rustc_errors:: Applicability ;
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:: DefId ;
9+ use rustc_hir:: { Expr , ExprKind , Item , ItemKind , Path , QPath , Variant , VariantData } ;
510use rustc_lexer:: TokenKind ;
6- use rustc_lint:: { EarlyContext , EarlyLintPass } ;
7- use rustc_session:: declare_lint_pass ;
11+ use rustc_lint:: { LateContext , LateLintPass } ;
12+ use rustc_session:: impl_lint_pass ;
813use rustc_span:: Span ;
914
1015declare_clippy_lint ! {
@@ -70,10 +75,16 @@ 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( Default ) ]
79+ pub struct EmptyWithBrackets {
80+ empty_tuple_enum_variants : FxHashSet < ( DefId , Span ) > ,
81+ enum_variants_used_as_functions : FxHashSet < DefId > ,
82+ }
83+
84+ impl_lint_pass ! ( EmptyWithBrackets => [ EMPTY_STRUCTS_WITH_BRACKETS , EMPTY_ENUM_VARIANTS_WITH_BRACKETS ] ) ;
7485
75- impl EarlyLintPass for EmptyWithBrackets {
76- fn check_item ( & mut self , cx : & EarlyContext < ' _ > , item : & Item ) {
86+ impl LateLintPass < ' _ > for EmptyWithBrackets {
87+ fn check_item ( & mut self , cx : & LateContext < ' _ > , item : & Item < ' _ > ) {
7788 let span_after_ident = item. span . with_lo ( item. ident . span . hi ( ) ) ;
7889
7990 if let ItemKind :: Struct ( var_data, _) = & item. kind
@@ -97,22 +108,62 @@ impl EarlyLintPass for EmptyWithBrackets {
97108 }
98109 }
99110
100- fn check_variant ( & mut self , cx : & EarlyContext < ' _ > , variant : & Variant ) {
111+ fn check_variant ( & mut self , cx : & LateContext < ' _ > , variant : & Variant < ' _ > ) {
112+ // Don't lint pub enums
113+ if cx. effective_visibilities . is_reachable ( variant. def_id ) {
114+ return ;
115+ }
116+
101117 let span_after_ident = variant. span . with_lo ( variant. ident . span . hi ( ) ) ;
102118
103- if has_brackets ( & variant. data ) && has_no_fields ( cx, & variant. data , span_after_ident) {
119+ if has_no_fields ( cx, & variant. data , span_after_ident) {
120+ match variant. data {
121+ VariantData :: Struct { .. } => {
122+ span_lint_and_then (
123+ cx,
124+ EMPTY_ENUM_VARIANTS_WITH_BRACKETS ,
125+ span_after_ident,
126+ "enum variant has empty brackets" ,
127+ |diagnostic| {
128+ diagnostic. span_suggestion_hidden (
129+ span_after_ident,
130+ "remove the brackets" ,
131+ "" ,
132+ Applicability :: MaybeIncorrect ,
133+ ) ;
134+ } ,
135+ ) ;
136+ } ,
137+ VariantData :: Tuple ( ..) => {
138+ if let Some ( local_def_id) = variant. data . ctor_def_id ( ) {
139+ self . empty_tuple_enum_variants
140+ . insert ( ( local_def_id. to_def_id ( ) , span_after_ident) ) ;
141+ }
142+ } ,
143+ VariantData :: Unit ( ..) => { } ,
144+ }
145+ }
146+ }
147+
148+ fn check_expr ( & mut self , _cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
149+ if let Some ( def_id) = check_expr_for_enum_as_function ( expr) {
150+ self . enum_variants_used_as_functions . insert ( def_id) ;
151+ }
152+ }
153+
154+ fn check_crate_post ( & mut self , cx : & LateContext < ' _ > ) {
155+ for & ( _, span) in self
156+ . empty_tuple_enum_variants
157+ . iter ( )
158+ . filter ( |( variant, _) | !self . enum_variants_used_as_functions . contains ( variant) )
159+ {
104160 span_lint_and_then (
105161 cx,
106162 EMPTY_ENUM_VARIANTS_WITH_BRACKETS ,
107- span_after_ident ,
163+ span ,
108164 "enum variant has empty brackets" ,
109165 |diagnostic| {
110- diagnostic. span_suggestion_hidden (
111- span_after_ident,
112- "remove the brackets" ,
113- "" ,
114- Applicability :: MaybeIncorrect ,
115- ) ;
166+ diagnostic. span_suggestion_hidden ( span, "remove the brackets" , "" , Applicability :: MaybeIncorrect ) ;
116167 } ,
117168 ) ;
118169 }
@@ -123,11 +174,11 @@ fn has_no_ident_token(braces_span_str: &str) -> bool {
123174 !rustc_lexer:: tokenize ( braces_span_str) . any ( |t| t. kind == TokenKind :: Ident )
124175}
125176
126- fn has_brackets ( var_data : & VariantData ) -> bool {
127- !matches ! ( var_data, VariantData :: Unit ( _ ) )
177+ fn has_brackets ( var_data : & VariantData < ' _ > ) -> bool {
178+ !matches ! ( var_data, VariantData :: Unit ( .. ) )
128179}
129180
130- fn has_no_fields ( cx : & EarlyContext < ' _ > , var_data : & VariantData , braces_span : Span ) -> bool {
181+ fn has_no_fields ( cx : & LateContext < ' _ > , var_data : & VariantData < ' _ > , braces_span : Span ) -> bool {
131182 if !var_data. fields ( ) . is_empty ( ) {
132183 return false ;
133184 }
@@ -142,6 +193,20 @@ fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Spa
142193 has_no_ident_token ( braces_span_str. as_ref ( ) )
143194}
144195
196+ fn check_expr_for_enum_as_function ( expr : & Expr < ' _ > ) -> Option < DefId > {
197+ let ExprKind :: Path ( QPath :: Resolved (
198+ _,
199+ Path {
200+ res : Def ( Ctor ( CtorOf :: Variant , _) , def_id) ,
201+ ..
202+ } ,
203+ ) ) = expr. kind
204+ else {
205+ return None ;
206+ } ;
207+ Some ( * def_id)
208+ }
209+
145210#[ cfg( test) ]
146211mod unit_test {
147212 use super :: * ;
0 commit comments