@@ -8,7 +8,6 @@ use rustc_data_structures::fx::FxHashSet;
8
8
use rustc_hir:: { EnumDef , FieldDef , Item , ItemKind , OwnerId , Variant , VariantData } ;
9
9
use rustc_lint:: { LateContext , LateLintPass } ;
10
10
use rustc_session:: impl_lint_pass;
11
- use rustc_span:: Span ;
12
11
use rustc_span:: symbol:: Symbol ;
13
12
14
13
declare_clippy_lint ! {
@@ -196,80 +195,169 @@ fn have_no_extra_prefix(prefixes: &[&str]) -> bool {
196
195
prefixes. iter ( ) . all ( |p| p == & "" || p == & "_" )
197
196
}
198
197
199
- fn check_fields ( cx : & LateContext < ' _ > , threshold : u64 , item : & Item < ' _ > , fields : & [ FieldDef < ' _ > ] ) {
200
- if ( fields. len ( ) as u64 ) < threshold {
201
- return ;
202
- }
198
+ impl ItemNameRepetitions {
199
+ /// Lint the names of enum variants against the name of the enum.
200
+ fn check_variants ( & self , cx : & LateContext < ' _ > , item : & Item < ' _ > , def : & EnumDef < ' _ > ) {
201
+ if self . avoid_breaking_exported_api && cx. effective_visibilities . is_exported ( item. owner_id . def_id ) {
202
+ return ;
203
+ }
204
+
205
+ if ( def. variants . len ( ) as u64 ) < self . enum_threshold {
206
+ return ;
207
+ }
203
208
204
- check_struct_name_repetition ( cx, item, fields) ;
209
+ let item_name = item. ident . name . as_str ( ) ;
210
+ for var in def. variants {
211
+ check_enum_start ( cx, item_name, var) ;
212
+ check_enum_end ( cx, item_name, var) ;
213
+ }
205
214
206
- // if the SyntaxContext of the identifiers of the fields and struct differ dont lint them.
207
- // this prevents linting in macros in which the location of the field identifier names differ
208
- if !fields. iter ( ) . all ( |field| item. ident . span . eq_ctxt ( field. ident . span ) ) {
209
- return ;
215
+ Self :: check_enum_common_affix ( cx, item, def) ;
210
216
}
211
217
212
- let mut pre: Vec < & str > = match fields. first ( ) {
213
- Some ( first_field) => first_field. ident . name . as_str ( ) . split ( '_' ) . collect ( ) ,
214
- None => return ,
215
- } ;
216
- let mut post = pre. clone ( ) ;
217
- post. reverse ( ) ;
218
- for field in fields {
219
- let field_split: Vec < & str > = field. ident . name . as_str ( ) . split ( '_' ) . collect ( ) ;
220
- if field_split. len ( ) == 1 {
218
+ /// Lint the names of struct fields against the name of the struct.
219
+ fn check_fields ( & self , cx : & LateContext < ' _ > , item : & Item < ' _ > , fields : & [ FieldDef < ' _ > ] ) {
220
+ if ( fields. len ( ) as u64 ) < self . struct_threshold {
221
221
return ;
222
222
}
223
223
224
- pre = pre
225
- . into_iter ( )
226
- . zip ( field_split. iter ( ) )
227
- . take_while ( |( a, b) | & a == b)
228
- . map ( |e| e. 0 )
229
- . collect ( ) ;
230
- post = post
231
- . into_iter ( )
232
- . zip ( field_split. iter ( ) . rev ( ) )
233
- . take_while ( |( a, b) | & a == b)
234
- . map ( |e| e. 0 )
235
- . collect ( ) ;
224
+ self . check_struct_name_repetition ( cx, item, fields) ;
225
+ self . check_struct_common_affix ( cx, item, fields) ;
236
226
}
237
- let prefix = pre. join ( "_" ) ;
238
- post. reverse ( ) ;
239
- let postfix = match post. last ( ) {
240
- Some ( & "" ) => post. join ( "_" ) + "_" ,
241
- Some ( _) | None => post. join ( "_" ) ,
242
- } ;
243
- if fields. len ( ) > 1 {
244
- let ( what, value) = match (
245
- prefix. is_empty ( ) || prefix. chars ( ) . all ( |c| c == '_' ) ,
246
- postfix. is_empty ( ) ,
247
- ) {
248
- ( true , true ) => return ,
249
- ( false , _) => ( "pre" , prefix) ,
250
- ( true , false ) => ( "post" , postfix) ,
227
+
228
+ fn check_enum_common_affix ( cx : & LateContext < ' _ > , item : & Item < ' _ > , def : & EnumDef < ' _ > ) {
229
+ let first = match def. variants . first ( ) {
230
+ Some ( variant) => variant. ident . name . as_str ( ) ,
231
+ None => return ,
251
232
} ;
252
- if fields. iter ( ) . all ( |field| is_bool ( field. ty ) ) {
253
- // If all fields are booleans, we don't want to emit this lint.
254
- return ;
233
+ let mut pre = camel_case_split ( first) ;
234
+ let mut post = pre. clone ( ) ;
235
+ post. reverse ( ) ;
236
+ for var in def. variants {
237
+ let name = var. ident . name . as_str ( ) ;
238
+
239
+ let variant_split = camel_case_split ( name) ;
240
+ if variant_split. len ( ) == 1 {
241
+ return ;
242
+ }
243
+
244
+ pre = pre
245
+ . iter ( )
246
+ . zip ( variant_split. iter ( ) )
247
+ . take_while ( |( a, b) | a == b)
248
+ . map ( |e| * e. 0 )
249
+ . collect ( ) ;
250
+ post = post
251
+ . iter ( )
252
+ . zip ( variant_split. iter ( ) . rev ( ) )
253
+ . take_while ( |( a, b) | a == b)
254
+ . map ( |e| * e. 0 )
255
+ . collect ( ) ;
255
256
}
257
+ let ( what, value) = match ( have_no_extra_prefix ( & pre) , post. is_empty ( ) ) {
258
+ ( true , true ) => return ,
259
+ ( false , _) => ( "pre" , pre. join ( "" ) ) ,
260
+ ( true , false ) => {
261
+ post. reverse ( ) ;
262
+ ( "post" , post. join ( "" ) )
263
+ } ,
264
+ } ;
256
265
span_lint_and_help (
257
266
cx,
258
- STRUCT_FIELD_NAMES ,
267
+ ENUM_VARIANT_NAMES ,
259
268
item. span ,
260
- format ! ( "all fields have the same {what}fix: `{value}`" ) ,
269
+ format ! ( "all variants have the same {what}fix: `{value}`" ) ,
261
270
None ,
262
- format ! ( "remove the {what}fixes" ) ,
271
+ format ! (
272
+ "remove the {what}fixes and use full paths to \
273
+ the variants instead of glob imports"
274
+ ) ,
263
275
) ;
264
276
}
265
- }
266
277
267
- fn check_struct_name_repetition ( cx : & LateContext < ' _ > , item : & Item < ' _ > , fields : & [ FieldDef < ' _ > ] ) {
268
- let snake_name = to_snake_case ( item. ident . name . as_str ( ) ) ;
269
- let item_name_words: Vec < & str > = snake_name. split ( '_' ) . collect ( ) ;
270
- for field in fields {
271
- if field. ident . span . eq_ctxt ( item. ident . span ) {
272
- //consider linting only if the field identifier has the same SyntaxContext as the item(struct)
278
+ fn check_struct_common_affix ( & self , cx : & LateContext < ' _ > , item : & Item < ' _ > , fields : & [ FieldDef < ' _ > ] ) {
279
+ // if the SyntaxContext of the identifiers of the fields and struct differ dont lint them.
280
+ // this prevents linting in macros in which the location of the field identifier names differ
281
+ if !fields. iter ( ) . all ( |field| item. ident . span . eq_ctxt ( field. ident . span ) ) {
282
+ return ;
283
+ }
284
+
285
+ if self . avoid_breaking_exported_api
286
+ && fields
287
+ . iter ( )
288
+ . any ( |field| cx. effective_visibilities . is_exported ( field. def_id ) )
289
+ {
290
+ return ;
291
+ }
292
+
293
+ let mut pre: Vec < & str > = match fields. first ( ) {
294
+ Some ( first_field) => first_field. ident . name . as_str ( ) . split ( '_' ) . collect ( ) ,
295
+ None => return ,
296
+ } ;
297
+ let mut post = pre. clone ( ) ;
298
+ post. reverse ( ) ;
299
+ for field in fields {
300
+ let field_split: Vec < & str > = field. ident . name . as_str ( ) . split ( '_' ) . collect ( ) ;
301
+ if field_split. len ( ) == 1 {
302
+ return ;
303
+ }
304
+
305
+ pre = pre
306
+ . into_iter ( )
307
+ . zip ( field_split. iter ( ) )
308
+ . take_while ( |( a, b) | & a == b)
309
+ . map ( |e| e. 0 )
310
+ . collect ( ) ;
311
+ post = post
312
+ . into_iter ( )
313
+ . zip ( field_split. iter ( ) . rev ( ) )
314
+ . take_while ( |( a, b) | & a == b)
315
+ . map ( |e| e. 0 )
316
+ . collect ( ) ;
317
+ }
318
+ let prefix = pre. join ( "_" ) ;
319
+ post. reverse ( ) ;
320
+ let postfix = match post. last ( ) {
321
+ Some ( & "" ) => post. join ( "_" ) + "_" ,
322
+ Some ( _) | None => post. join ( "_" ) ,
323
+ } ;
324
+ if fields. len ( ) > 1 {
325
+ let ( what, value) = match (
326
+ prefix. is_empty ( ) || prefix. chars ( ) . all ( |c| c == '_' ) ,
327
+ postfix. is_empty ( ) ,
328
+ ) {
329
+ ( true , true ) => return ,
330
+ ( false , _) => ( "pre" , prefix) ,
331
+ ( true , false ) => ( "post" , postfix) ,
332
+ } ;
333
+ if fields. iter ( ) . all ( |field| is_bool ( field. ty ) ) {
334
+ // If all fields are booleans, we don't want to emit this lint.
335
+ return ;
336
+ }
337
+ span_lint_and_help (
338
+ cx,
339
+ STRUCT_FIELD_NAMES ,
340
+ item. span ,
341
+ format ! ( "all fields have the same {what}fix: `{value}`" ) ,
342
+ None ,
343
+ format ! ( "remove the {what}fixes" ) ,
344
+ ) ;
345
+ }
346
+ }
347
+
348
+ fn check_struct_name_repetition ( & self , cx : & LateContext < ' _ > , item : & Item < ' _ > , fields : & [ FieldDef < ' _ > ] ) {
349
+ let snake_name = to_snake_case ( item. ident . name . as_str ( ) ) ;
350
+ let item_name_words: Vec < & str > = snake_name. split ( '_' ) . collect ( ) ;
351
+ for field in fields {
352
+ if self . avoid_breaking_exported_api && cx. effective_visibilities . is_exported ( field. def_id ) {
353
+ continue ;
354
+ }
355
+
356
+ if !field. ident . span . eq_ctxt ( item. ident . span ) {
357
+ // consider linting only if the field identifier has the same SyntaxContext as the item(struct)
358
+ continue ;
359
+ }
360
+
273
361
let field_words: Vec < & str > = field. ident . name . as_str ( ) . split ( '_' ) . collect ( ) ;
274
362
if field_words. len ( ) >= item_name_words. len ( ) {
275
363
// if the field name is shorter than the struct name it cannot contain it
@@ -337,65 +425,6 @@ fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>)
337
425
}
338
426
}
339
427
340
- fn check_variant ( cx : & LateContext < ' _ > , threshold : u64 , def : & EnumDef < ' _ > , item_name : & str , span : Span ) {
341
- if ( def. variants . len ( ) as u64 ) < threshold {
342
- return ;
343
- }
344
-
345
- for var in def. variants {
346
- check_enum_start ( cx, item_name, var) ;
347
- check_enum_end ( cx, item_name, var) ;
348
- }
349
-
350
- let first = match def. variants . first ( ) {
351
- Some ( variant) => variant. ident . name . as_str ( ) ,
352
- None => return ,
353
- } ;
354
- let mut pre = camel_case_split ( first) ;
355
- let mut post = pre. clone ( ) ;
356
- post. reverse ( ) ;
357
- for var in def. variants {
358
- let name = var. ident . name . as_str ( ) ;
359
-
360
- let variant_split = camel_case_split ( name) ;
361
- if variant_split. len ( ) == 1 {
362
- return ;
363
- }
364
-
365
- pre = pre
366
- . iter ( )
367
- . zip ( variant_split. iter ( ) )
368
- . take_while ( |( a, b) | a == b)
369
- . map ( |e| * e. 0 )
370
- . collect ( ) ;
371
- post = post
372
- . iter ( )
373
- . zip ( variant_split. iter ( ) . rev ( ) )
374
- . take_while ( |( a, b) | a == b)
375
- . map ( |e| * e. 0 )
376
- . collect ( ) ;
377
- }
378
- let ( what, value) = match ( have_no_extra_prefix ( & pre) , post. is_empty ( ) ) {
379
- ( true , true ) => return ,
380
- ( false , _) => ( "pre" , pre. join ( "" ) ) ,
381
- ( true , false ) => {
382
- post. reverse ( ) ;
383
- ( "post" , post. join ( "" ) )
384
- } ,
385
- } ;
386
- span_lint_and_help (
387
- cx,
388
- ENUM_VARIANT_NAMES ,
389
- span,
390
- format ! ( "all variants have the same {what}fix: `{value}`" ) ,
391
- None ,
392
- format ! (
393
- "remove the {what}fixes and use full paths to \
394
- the variants instead of glob imports"
395
- ) ,
396
- ) ;
397
- }
398
-
399
428
impl LateLintPass < ' _ > for ItemNameRepetitions {
400
429
fn check_item_post ( & mut self , _cx : & LateContext < ' _ > , _item : & Item < ' _ > ) {
401
430
let last = self . modules . pop ( ) ;
@@ -458,17 +487,19 @@ impl LateLintPass<'_> for ItemNameRepetitions {
458
487
}
459
488
}
460
489
}
461
- if !( self . avoid_breaking_exported_api && cx. effective_visibilities . is_exported ( item. owner_id . def_id ) )
462
- && span_is_local ( item. span )
463
- {
490
+
491
+ if span_is_local ( item. span ) {
464
492
match item. kind {
465
- ItemKind :: Enum ( def, _) => check_variant ( cx, self . enum_threshold , & def, item_name, item. span ) ,
493
+ ItemKind :: Enum ( def, _) => {
494
+ self . check_variants ( cx, item, & def) ;
495
+ } ,
466
496
ItemKind :: Struct ( VariantData :: Struct { fields, .. } , _) => {
467
- check_fields ( cx, self . struct_threshold , item, fields) ;
497
+ self . check_fields ( cx, item, fields) ;
468
498
} ,
469
499
_ => ( ) ,
470
500
}
471
501
}
502
+
472
503
self . modules . push ( ( item. ident . name , item_camel, item. owner_id ) ) ;
473
504
}
474
505
}
0 commit comments