1
1
use clippy_utils:: diagnostics:: span_lint_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 :: FxHashSet ;
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:: DefId ;
9
+ use rustc_hir:: { Expr , ExprKind , Item , ItemKind , 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,16 @@ 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( 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 ] ) ;
74
85
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 < ' _ > ) {
77
88
let span_after_ident = item. span . with_lo ( item. ident . span . hi ( ) ) ;
78
89
79
90
if let ItemKind :: Struct ( var_data, _) = & item. kind
@@ -97,22 +108,62 @@ impl EarlyLintPass for EmptyWithBrackets {
97
108
}
98
109
}
99
110
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
+
101
117
let span_after_ident = variant. span . with_lo ( variant. ident . span . hi ( ) ) ;
102
118
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
+ {
104
160
span_lint_and_then (
105
161
cx,
106
162
EMPTY_ENUM_VARIANTS_WITH_BRACKETS ,
107
- span_after_ident ,
163
+ span ,
108
164
"enum variant has empty brackets" ,
109
165
|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 ) ;
116
167
} ,
117
168
) ;
118
169
}
@@ -123,11 +174,11 @@ fn has_no_ident_token(braces_span_str: &str) -> bool {
123
174
!rustc_lexer:: tokenize ( braces_span_str) . any ( |t| t. kind == TokenKind :: Ident )
124
175
}
125
176
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 ( .. ) )
128
179
}
129
180
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 {
131
182
if !var_data. fields ( ) . is_empty ( ) {
132
183
return false ;
133
184
}
@@ -142,6 +193,20 @@ fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Spa
142
193
has_no_ident_token ( braces_span_str. as_ref ( ) )
143
194
}
144
195
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
+
145
210
#[ cfg( test) ]
146
211
mod unit_test {
147
212
use super :: * ;
0 commit comments