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 :: FxIndexMap ;
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,21 @@ 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+ enum Usage {
79+ Unused ( Span ) ,
80+ Used ,
81+ }
82+
83+ #[ derive( Default ) ]
84+ pub struct EmptyWithBrackets {
85+ // Value holds `true` if the empty tuple variant was used as a function
86+ empty_tuple_enum_variants : FxIndexMap < DefId , Usage > ,
87+ }
7488
75- impl EarlyLintPass for EmptyWithBrackets {
76- fn check_item ( & mut self , cx : & EarlyContext < ' _ > , item : & Item ) {
89+ impl_lint_pass ! ( EmptyWithBrackets => [ EMPTY_STRUCTS_WITH_BRACKETS , EMPTY_ENUM_VARIANTS_WITH_BRACKETS ] ) ;
90+
91+ impl LateLintPass < ' _ > for EmptyWithBrackets {
92+ fn check_item ( & mut self , cx : & LateContext < ' _ > , item : & Item < ' _ > ) {
7793 let span_after_ident = item. span . with_lo ( item. ident . span . hi ( ) ) ;
7894
7995 if let ItemKind :: Struct ( var_data, _) = & item. kind
@@ -97,22 +113,63 @@ impl EarlyLintPass for EmptyWithBrackets {
97113 }
98114 }
99115
100- fn check_variant ( & mut self , cx : & EarlyContext < ' _ > , variant : & Variant ) {
116+ fn check_variant ( & mut self , cx : & LateContext < ' _ > , variant : & Variant < ' _ > ) {
117+ // Don't lint pub enums
118+ if cx. effective_visibilities . is_reachable ( variant. def_id ) {
119+ return ;
120+ }
121+
101122 let span_after_ident = variant. span . with_lo ( variant. ident . span . hi ( ) ) ;
102123
103- if has_brackets ( & variant. data ) && has_no_fields ( cx, & variant. data , span_after_ident) {
124+ if has_no_fields ( cx, & variant. data , span_after_ident) {
125+ match variant. data {
126+ VariantData :: Struct { .. } => {
127+ span_lint_and_then (
128+ cx,
129+ EMPTY_ENUM_VARIANTS_WITH_BRACKETS ,
130+ span_after_ident,
131+ "enum variant has empty brackets" ,
132+ |diagnostic| {
133+ diagnostic. span_suggestion_hidden (
134+ span_after_ident,
135+ "remove the brackets" ,
136+ "" ,
137+ Applicability :: MaybeIncorrect ,
138+ ) ;
139+ } ,
140+ ) ;
141+ } ,
142+ VariantData :: Tuple ( ..) => {
143+ if let Some ( local_def_id) = variant. data . ctor_def_id ( )
144+ && !self . empty_tuple_enum_variants . contains_key ( & local_def_id. to_def_id ( ) )
145+ {
146+ self . empty_tuple_enum_variants
147+ . insert ( local_def_id. to_def_id ( ) , Usage :: Unused ( span_after_ident) ) ;
148+ }
149+ } ,
150+ VariantData :: Unit ( ..) => { } ,
151+ }
152+ }
153+ }
154+
155+ fn check_expr ( & mut self , _cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
156+ if let Some ( def_id) = check_expr_for_enum_as_function ( expr) {
157+ self . empty_tuple_enum_variants . insert ( def_id, Usage :: Used ) ;
158+ }
159+ }
160+
161+ fn check_crate_post ( & mut self , cx : & LateContext < ' _ > ) {
162+ for & span in self . empty_tuple_enum_variants . values ( ) . filter_map ( |v| match v {
163+ Usage :: Unused ( span) => Some ( span) ,
164+ Usage :: Used => None ,
165+ } ) {
104166 span_lint_and_then (
105167 cx,
106168 EMPTY_ENUM_VARIANTS_WITH_BRACKETS ,
107- span_after_ident ,
169+ span ,
108170 "enum variant has empty brackets" ,
109171 |diagnostic| {
110- diagnostic. span_suggestion_hidden (
111- span_after_ident,
112- "remove the brackets" ,
113- "" ,
114- Applicability :: MaybeIncorrect ,
115- ) ;
172+ diagnostic. span_suggestion_hidden ( span, "remove the brackets" , "" , Applicability :: MaybeIncorrect ) ;
116173 } ,
117174 ) ;
118175 }
@@ -123,11 +180,11 @@ fn has_no_ident_token(braces_span_str: &str) -> bool {
123180 !rustc_lexer:: tokenize ( braces_span_str) . any ( |t| t. kind == TokenKind :: Ident )
124181}
125182
126- fn has_brackets ( var_data : & VariantData ) -> bool {
127- !matches ! ( var_data, VariantData :: Unit ( _ ) )
183+ fn has_brackets ( var_data : & VariantData < ' _ > ) -> bool {
184+ !matches ! ( var_data, VariantData :: Unit ( .. ) )
128185}
129186
130- fn has_no_fields ( cx : & EarlyContext < ' _ > , var_data : & VariantData , braces_span : Span ) -> bool {
187+ fn has_no_fields ( cx : & LateContext < ' _ > , var_data : & VariantData < ' _ > , braces_span : Span ) -> bool {
131188 if !var_data. fields ( ) . is_empty ( ) {
132189 return false ;
133190 }
@@ -142,6 +199,20 @@ fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Spa
142199 has_no_ident_token ( braces_span_str. as_ref ( ) )
143200}
144201
202+ fn check_expr_for_enum_as_function ( expr : & Expr < ' _ > ) -> Option < DefId > {
203+ let ExprKind :: Path ( QPath :: Resolved (
204+ _,
205+ Path {
206+ res : Def ( Ctor ( CtorOf :: Variant , _) , def_id) ,
207+ ..
208+ } ,
209+ ) ) = expr. kind
210+ else {
211+ return None ;
212+ } ;
213+ Some ( * def_id)
214+ }
215+
145216#[ cfg( test) ]
146217mod unit_test {
147218 use super :: * ;
0 commit comments