11use clippy_utils:: attrs:: span_contains_cfg;
22use clippy_utils:: diagnostics:: { span_lint_and_then, span_lint_hir_and_then} ;
3- use rustc_data_structures:: fx:: FxIndexMap ;
3+ use clippy_utils:: source:: SpanRangeExt ;
4+ use clippy_utils:: span_contains_non_whitespace;
5+ use rustc_data_structures:: fx:: { FxIndexMap , IndexEntry } ;
46use rustc_errors:: Applicability ;
5- use rustc_hir:: def:: CtorOf ;
67use rustc_hir:: def:: DefKind :: Ctor ;
78use rustc_hir:: def:: Res :: Def ;
9+ use rustc_hir:: def:: { CtorOf , DefKind } ;
810use rustc_hir:: def_id:: LocalDefId ;
9- use rustc_hir:: { Expr , ExprKind , Item , ItemKind , Node , Path , QPath , Variant , VariantData } ;
11+ use rustc_hir:: { Expr , ExprKind , Item , ItemKind , Node , Pat , PatKind , Path , QPath , Variant , VariantData } ;
1012use rustc_lint:: { LateContext , LateLintPass } ;
11- use rustc_middle:: ty:: TyCtxt ;
13+ use rustc_middle:: ty:: { self , TyCtxt } ;
1214use rustc_session:: impl_lint_pass;
13- use rustc_span:: Span ;
15+ use rustc_span:: { BytePos , Span } ;
1416
1517declare_clippy_lint ! {
1618 /// ### What it does
@@ -118,109 +120,80 @@ impl LateLintPass<'_> for EmptyWithBrackets {
118120 }
119121
120122 fn check_variant ( & mut self , cx : & LateContext < ' _ > , variant : & Variant < ' _ > ) {
121- // FIXME: handle `$name {}`
122123 if !variant. span . from_expansion ( )
123124 && !variant. ident . span . from_expansion ( )
124125 && let span_after_ident = variant. span . with_lo ( variant. ident . span . hi ( ) )
125126 && has_no_fields ( cx, & variant. data , span_after_ident)
126127 {
127128 match variant. data {
128129 VariantData :: Struct { .. } => {
129- // Empty struct variants can be linted immediately
130- span_lint_and_then (
131- cx,
132- EMPTY_ENUM_VARIANTS_WITH_BRACKETS ,
133- span_after_ident,
134- "enum variant has empty brackets" ,
135- |diagnostic| {
136- diagnostic. span_suggestion_hidden (
137- span_after_ident,
138- "remove the brackets" ,
139- "" ,
140- Applicability :: MaybeIncorrect ,
141- ) ;
142- } ,
143- ) ;
130+ self . add_enum_variant ( variant. def_id ) ;
144131 } ,
145132 VariantData :: Tuple ( .., local_def_id) => {
146133 // Don't lint reachable tuple enums
147134 if cx. effective_visibilities . is_reachable ( variant. def_id ) {
148135 return ;
149136 }
150- if let Some ( entry) = self . empty_tuple_enum_variants . get_mut ( & local_def_id) {
151- // empty_tuple_enum_variants contains Usage::NoDefinition if the variant was called before the
152- // definition was encountered. Now that there's a definition, convert it
153- // to Usage::Unused.
154- if let Usage :: NoDefinition { redundant_use_sites } = entry {
155- * entry = Usage :: Unused {
156- redundant_use_sites : redundant_use_sites. clone ( ) ,
157- } ;
158- }
159- } else {
160- self . empty_tuple_enum_variants . insert (
161- local_def_id,
162- Usage :: Unused {
163- redundant_use_sites : vec ! [ ] ,
164- } ,
165- ) ;
166- }
137+ self . add_enum_variant ( local_def_id) ;
167138 } ,
168139 VariantData :: Unit ( ..) => { } ,
169140 }
170141 }
171142 }
172143
173144 fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
174- if let Some ( def_id) = check_expr_for_enum_as_function ( expr) {
175- if let Some ( parentheses_span) = call_parentheses_span ( cx. tcx , expr) {
145+ if let Some ( ( def_id, mut span) ) = check_expr_for_enum_as_function ( cx, expr) {
146+ if span. is_empty ( )
147+ && let Some ( parentheses_span) = call_parentheses_span ( cx. tcx , expr)
148+ {
149+ span = parentheses_span;
150+ }
151+
152+ if span. is_empty ( ) {
153+ // The parentheses are not redundant.
154+ self . empty_tuple_enum_variants . insert ( def_id, Usage :: Used ) ;
155+ } else {
176156 // Do not count expressions from macro expansion as a redundant use site.
177157 if expr. span . from_expansion ( ) {
178158 return ;
179159 }
180- match self . empty_tuple_enum_variants . get_mut ( & def_id) {
181- Some (
182- & mut ( Usage :: Unused {
183- ref mut redundant_use_sites,
184- }
185- | Usage :: NoDefinition {
186- ref mut redundant_use_sites,
187- } ) ,
188- ) => {
189- redundant_use_sites. push ( parentheses_span) ;
190- } ,
191- None => {
192- // The variant isn't in the IndexMap which means its definition wasn't encountered yet.
193- self . empty_tuple_enum_variants . insert (
194- def_id,
195- Usage :: NoDefinition {
196- redundant_use_sites : vec ! [ parentheses_span] ,
197- } ,
198- ) ;
199- } ,
200- _ => { } ,
201- }
202- } else {
203- // The parentheses are not redundant.
204- self . empty_tuple_enum_variants . insert ( def_id, Usage :: Used ) ;
160+ self . update_enum_variant_usage ( def_id, span) ;
205161 }
206162 }
207163 }
208164
165+ fn check_pat ( & mut self , cx : & LateContext < ' _ > , pat : & Pat < ' _ > ) {
166+ if !pat. span . from_expansion ( )
167+ && let Some ( ( def_id, span) ) = check_pat_for_enum_as_function ( cx, pat)
168+ {
169+ self . update_enum_variant_usage ( def_id, span) ;
170+ }
171+ }
172+
209173 fn check_crate_post ( & mut self , cx : & LateContext < ' _ > ) {
210- for ( local_def_id, usage) in & self . empty_tuple_enum_variants {
174+ for ( & local_def_id, usage) in & self . empty_tuple_enum_variants {
211175 // Ignore all variants with Usage::Used or Usage::NoDefinition
212176 let Usage :: Unused { redundant_use_sites } = usage else {
213177 continue ;
214178 } ;
179+
215180 // Attempt to fetch the Variant from LocalDefId.
216- let Node :: Variant ( variant) = cx. tcx . hir_node (
217- cx. tcx
218- . local_def_id_to_hir_id ( cx. tcx . parent ( local_def_id. to_def_id ( ) ) . expect_local ( ) ) ,
219- ) else {
181+ let variant = if let Node :: Variant ( variant) = cx. tcx . hir_node_by_def_id ( local_def_id) {
182+ variant
183+ } else if let Node :: Variant ( variant) = cx. tcx . hir_node_by_def_id ( cx. tcx . local_parent ( local_def_id) ) {
184+ variant
185+ } else {
220186 continue ;
221187 } ;
188+
222189 // Span of the parentheses in variant definition
223190 let span = variant. span . with_lo ( variant. ident . span . hi ( ) ) ;
191+ let span_inner = span
192+ . with_lo ( SpanRangeExt :: trim_start ( span, cx) . start + BytePos ( 1 ) )
193+ . with_hi ( span. hi ( ) - BytePos ( 1 ) ) ;
194+ if span_contains_non_whitespace ( cx, span_inner, false ) {
195+ continue ;
196+ }
224197 span_lint_hir_and_then (
225198 cx,
226199 EMPTY_ENUM_VARIANTS_WITH_BRACKETS ,
@@ -252,6 +225,43 @@ impl LateLintPass<'_> for EmptyWithBrackets {
252225 }
253226}
254227
228+ impl EmptyWithBrackets {
229+ fn add_enum_variant ( & mut self , local_def_id : LocalDefId ) {
230+ self . empty_tuple_enum_variants
231+ . entry ( local_def_id)
232+ . and_modify ( |entry| {
233+ // empty_tuple_enum_variants contains Usage::NoDefinition if the variant was called before
234+ // the definition was encountered. Now that there's a
235+ // definition, convert it to Usage::Unused.
236+ if let Usage :: NoDefinition { redundant_use_sites } = entry {
237+ * entry = Usage :: Unused {
238+ redundant_use_sites : redundant_use_sites. clone ( ) ,
239+ } ;
240+ }
241+ } )
242+ . or_insert_with ( || Usage :: Unused {
243+ redundant_use_sites : vec ! [ ] ,
244+ } ) ;
245+ }
246+
247+ fn update_enum_variant_usage ( & mut self , def_id : LocalDefId , parentheses_span : Span ) {
248+ match self . empty_tuple_enum_variants . entry ( def_id) {
249+ IndexEntry :: Occupied ( mut e) => {
250+ if let Usage :: Unused { redundant_use_sites } | Usage :: NoDefinition { redundant_use_sites } = e. get_mut ( )
251+ {
252+ redundant_use_sites. push ( parentheses_span) ;
253+ }
254+ } ,
255+ IndexEntry :: Vacant ( e) => {
256+ // The variant isn't in the IndexMap which means its definition wasn't encountered yet.
257+ e. insert ( Usage :: NoDefinition {
258+ redundant_use_sites : vec ! [ parentheses_span] ,
259+ } ) ;
260+ } ,
261+ }
262+ }
263+ }
264+
255265fn has_brackets ( var_data : & VariantData < ' _ > ) -> bool {
256266 !matches ! ( var_data, VariantData :: Unit ( ..) )
257267}
@@ -277,17 +287,47 @@ fn call_parentheses_span(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option<Span> {
277287}
278288
279289// Returns the LocalDefId of the variant being called as a function if it exists.
280- fn check_expr_for_enum_as_function ( expr : & Expr < ' _ > ) -> Option < LocalDefId > {
281- if let ExprKind :: Path ( QPath :: Resolved (
282- _,
283- Path {
284- res : Def ( Ctor ( CtorOf :: Variant , _) , def_id) ,
285- ..
290+ fn check_expr_for_enum_as_function ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> Option < ( LocalDefId , Span ) > {
291+ match expr. kind {
292+ ExprKind :: Path ( QPath :: Resolved (
293+ _,
294+ Path {
295+ res : Def ( Ctor ( CtorOf :: Variant , _) , def_id) ,
296+ span,
297+ ..
298+ } ,
299+ ) ) => def_id. as_local ( ) . map ( |id| ( id, span. with_lo ( expr. span . hi ( ) ) ) ) ,
300+ ExprKind :: Struct ( qpath, ..)
301+ if let Def ( DefKind :: Variant , mut def_id) = cx. typeck_results ( ) . qpath_res ( qpath, expr. hir_id ) =>
302+ {
303+ let ty = cx. tcx . type_of ( def_id) . instantiate_identity ( ) ;
304+ if let ty:: FnDef ( ctor_def_id, _) = ty. kind ( ) {
305+ def_id = * ctor_def_id;
306+ }
307+
308+ def_id. as_local ( ) . map ( |id| ( id, qpath. span ( ) . with_lo ( expr. span . hi ( ) ) ) )
286309 } ,
287- ) ) = expr. kind
288- {
289- def_id. as_local ( )
290- } else {
291- None
310+ _ => None ,
311+ }
312+ }
313+
314+ fn check_pat_for_enum_as_function ( cx : & LateContext < ' _ > , pat : & Pat < ' _ > ) -> Option < ( LocalDefId , Span ) > {
315+ match pat. kind {
316+ PatKind :: TupleStruct ( qpath, ..)
317+ if let Def ( Ctor ( CtorOf :: Variant , _) , def_id) = cx. typeck_results ( ) . qpath_res ( & qpath, pat. hir_id ) =>
318+ {
319+ def_id. as_local ( ) . map ( |id| ( id, qpath. span ( ) . with_lo ( pat. span . hi ( ) ) ) )
320+ } ,
321+ PatKind :: Struct ( qpath, ..)
322+ if let Def ( DefKind :: Variant , mut def_id) = cx. typeck_results ( ) . qpath_res ( & qpath, pat. hir_id ) =>
323+ {
324+ let ty = cx. tcx . type_of ( def_id) . instantiate_identity ( ) ;
325+ if let ty:: FnDef ( ctor_def_id, _) = ty. kind ( ) {
326+ def_id = * ctor_def_id;
327+ }
328+
329+ def_id. as_local ( ) . map ( |id| ( id, qpath. span ( ) . with_lo ( pat. span . hi ( ) ) ) )
330+ } ,
331+ _ => None ,
292332 }
293333}
0 commit comments