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 :: 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:: 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,21 @@ 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
+ 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
+ }
74
88
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 < ' _ > ) {
77
93
let span_after_ident = item. span . with_lo ( item. ident . span . hi ( ) ) ;
78
94
79
95
if let ItemKind :: Struct ( var_data, _) = & item. kind
@@ -97,22 +113,63 @@ impl EarlyLintPass for EmptyWithBrackets {
97
113
}
98
114
}
99
115
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
+
101
122
let span_after_ident = variant. span . with_lo ( variant. ident . span . hi ( ) ) ;
102
123
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
+ } ) {
104
166
span_lint_and_then (
105
167
cx,
106
168
EMPTY_ENUM_VARIANTS_WITH_BRACKETS ,
107
- span_after_ident ,
169
+ span ,
108
170
"enum variant has empty brackets" ,
109
171
|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 ) ;
116
173
} ,
117
174
) ;
118
175
}
@@ -123,11 +180,11 @@ fn has_no_ident_token(braces_span_str: &str) -> bool {
123
180
!rustc_lexer:: tokenize ( braces_span_str) . any ( |t| t. kind == TokenKind :: Ident )
124
181
}
125
182
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 ( .. ) )
128
185
}
129
186
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 {
131
188
if !var_data. fields ( ) . is_empty ( ) {
132
189
return false ;
133
190
}
@@ -142,6 +199,20 @@ fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Spa
142
199
has_no_ident_token ( braces_span_str. as_ref ( ) )
143
200
}
144
201
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
+
145
216
#[ cfg( test) ]
146
217
mod unit_test {
147
218
use super :: * ;
0 commit comments