1
- use clippy_utils:: diagnostics:: span_lint_and_then;
1
+ use clippy_utils:: diagnostics:: { span_lint_and_then, span_lint_hir_and_then } ;
2
2
use clippy_utils:: source:: snippet_opt;
3
- use rustc_ast :: ast :: { Item , ItemKind , Variant , VariantData } ;
3
+ use rustc_data_structures :: fx :: FxIndexMap ;
4
4
use 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:: LocalDefId ;
9
+ use rustc_hir:: { Expr , ExprKind , Item , ItemKind , Node , Path , QPath , Variant , VariantData } ;
5
10
use 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 ;
8
13
use rustc_span:: Span ;
9
14
10
15
declare_clippy_lint ! {
@@ -70,10 +75,22 @@ declare_clippy_lint! {
70
75
"finds enum variants with empty brackets"
71
76
}
72
77
73
- declare_lint_pass ! ( EmptyWithBrackets => [ EMPTY_STRUCTS_WITH_BRACKETS , EMPTY_ENUM_VARIANTS_WITH_BRACKETS ] ) ;
78
+ #[ derive( Debug ) ]
79
+ enum Usage {
80
+ Unused ,
81
+ Used ,
82
+ }
83
+
84
+ #[ derive( Default ) ]
85
+ pub struct EmptyWithBrackets {
86
+ // Value holds `true` if the empty tuple variant was used as a function
87
+ empty_tuple_enum_variants : FxIndexMap < LocalDefId , Usage > ,
88
+ }
89
+
90
+ impl_lint_pass ! ( EmptyWithBrackets => [ EMPTY_STRUCTS_WITH_BRACKETS , EMPTY_ENUM_VARIANTS_WITH_BRACKETS ] ) ;
74
91
75
- impl EarlyLintPass for EmptyWithBrackets {
76
- fn check_item ( & mut self , cx : & EarlyContext < ' _ > , item : & Item ) {
92
+ impl LateLintPass < ' _ > for EmptyWithBrackets {
93
+ fn check_item ( & mut self , cx : & LateContext < ' _ > , item : & Item < ' _ > ) {
77
94
let span_after_ident = item. span . with_lo ( item. ident . span . hi ( ) ) ;
78
95
79
96
if let ItemKind :: Struct ( var_data, _) = & item. kind
@@ -97,22 +114,69 @@ impl EarlyLintPass for EmptyWithBrackets {
97
114
}
98
115
}
99
116
100
- fn check_variant ( & mut self , cx : & EarlyContext < ' _ > , variant : & Variant ) {
117
+ fn check_variant ( & mut self , cx : & LateContext < ' _ > , variant : & Variant < ' _ > ) {
118
+ // Don't lint pub enums
119
+ if cx. effective_visibilities . is_reachable ( variant. def_id ) {
120
+ return ;
121
+ }
122
+
101
123
let span_after_ident = variant. span . with_lo ( variant. ident . span . hi ( ) ) ;
102
124
103
- if has_brackets ( & variant. data ) && has_no_fields ( cx, & variant. data , span_after_ident) {
104
- span_lint_and_then (
125
+ if has_no_fields ( cx, & variant. data , span_after_ident) {
126
+ match variant. data {
127
+ VariantData :: Struct { .. } => {
128
+ span_lint_and_then (
129
+ cx,
130
+ EMPTY_ENUM_VARIANTS_WITH_BRACKETS ,
131
+ span_after_ident,
132
+ "enum variant has empty brackets" ,
133
+ |diagnostic| {
134
+ diagnostic. span_suggestion_hidden (
135
+ span_after_ident,
136
+ "remove the brackets" ,
137
+ "" ,
138
+ Applicability :: MaybeIncorrect ,
139
+ ) ;
140
+ } ,
141
+ ) ;
142
+ } ,
143
+ VariantData :: Tuple ( .., local_def_id) => {
144
+ self . empty_tuple_enum_variants
145
+ . entry ( local_def_id)
146
+ . or_insert ( Usage :: Unused ) ;
147
+ } ,
148
+ VariantData :: Unit ( ..) => { } ,
149
+ }
150
+ }
151
+ }
152
+
153
+ fn check_expr ( & mut self , _cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
154
+ if let Some ( def_id) = check_expr_for_enum_as_function ( expr) {
155
+ self . empty_tuple_enum_variants . insert ( def_id, Usage :: Used ) ;
156
+ }
157
+ }
158
+
159
+ fn check_crate_post ( & mut self , cx : & LateContext < ' _ > ) {
160
+ for ( local_def_id, _) in self
161
+ . empty_tuple_enum_variants
162
+ . iter ( )
163
+ . filter ( |( _, v) | matches ! ( v, Usage :: Unused ) )
164
+ {
165
+ let Node :: Variant ( variant) = cx. tcx . hir_node (
166
+ cx. tcx
167
+ . local_def_id_to_hir_id ( cx. tcx . parent ( local_def_id. to_def_id ( ) ) . expect_local ( ) ) ,
168
+ ) else {
169
+ continue ;
170
+ } ;
171
+ let span = variant. span . with_lo ( variant. ident . span . hi ( ) ) ;
172
+ span_lint_hir_and_then (
105
173
cx,
106
174
EMPTY_ENUM_VARIANTS_WITH_BRACKETS ,
107
- span_after_ident,
175
+ variant. hir_id ,
176
+ span,
108
177
"enum variant has empty brackets" ,
109
178
|diagnostic| {
110
- diagnostic. span_suggestion_hidden (
111
- span_after_ident,
112
- "remove the brackets" ,
113
- "" ,
114
- Applicability :: MaybeIncorrect ,
115
- ) ;
179
+ diagnostic. span_suggestion_hidden ( span, "remove the brackets" , "" , Applicability :: MaybeIncorrect ) ;
116
180
} ,
117
181
) ;
118
182
}
@@ -123,11 +187,11 @@ fn has_no_ident_token(braces_span_str: &str) -> bool {
123
187
!rustc_lexer:: tokenize ( braces_span_str) . any ( |t| t. kind == TokenKind :: Ident )
124
188
}
125
189
126
- fn has_brackets ( var_data : & VariantData ) -> bool {
127
- !matches ! ( var_data, VariantData :: Unit ( _ ) )
190
+ fn has_brackets ( var_data : & VariantData < ' _ > ) -> bool {
191
+ !matches ! ( var_data, VariantData :: Unit ( .. ) )
128
192
}
129
193
130
- fn has_no_fields ( cx : & EarlyContext < ' _ > , var_data : & VariantData , braces_span : Span ) -> bool {
194
+ fn has_no_fields ( cx : & LateContext < ' _ > , var_data : & VariantData < ' _ > , braces_span : Span ) -> bool {
131
195
if !var_data. fields ( ) . is_empty ( ) {
132
196
return false ;
133
197
}
@@ -142,6 +206,21 @@ fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Spa
142
206
has_no_ident_token ( braces_span_str. as_ref ( ) )
143
207
}
144
208
209
+ fn check_expr_for_enum_as_function ( expr : & Expr < ' _ > ) -> Option < LocalDefId > {
210
+ if let ExprKind :: Path ( QPath :: Resolved (
211
+ _,
212
+ Path {
213
+ res : Def ( Ctor ( CtorOf :: Variant , _) , def_id) ,
214
+ ..
215
+ } ,
216
+ ) ) = expr. kind
217
+ {
218
+ def_id. as_local ( )
219
+ } else {
220
+ None
221
+ }
222
+ }
223
+
145
224
#[ cfg( test) ]
146
225
mod unit_test {
147
226
use super :: * ;
0 commit comments