@@ -8,7 +8,6 @@ use rustc_data_structures::fx::FxHashSet;
88use rustc_hir:: { EnumDef , FieldDef , Item , ItemKind , OwnerId , Variant , VariantData } ;
99use rustc_lint:: { LateContext , LateLintPass } ;
1010use rustc_session:: impl_lint_pass;
11- use rustc_span:: Span ;
1211use rustc_span:: symbol:: Symbol ;
1312
1413declare_clippy_lint ! {
@@ -196,80 +195,169 @@ fn have_no_extra_prefix(prefixes: &[&str]) -> bool {
196195 prefixes. iter ( ) . all ( |p| p == & "" || p == & "_" )
197196}
198197
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+ }
203208
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+ }
205214
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) ;
210216 }
211217
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 {
221221 return ;
222222 }
223223
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) ;
236226 }
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 ,
251232 } ;
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 ( ) ;
255256 }
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+ } ;
256265 span_lint_and_help (
257266 cx,
258- STRUCT_FIELD_NAMES ,
267+ ENUM_VARIANT_NAMES ,
259268 item. span ,
260- format ! ( "all fields have the same {what}fix: `{value}`" ) ,
269+ format ! ( "all variants have the same {what}fix: `{value}`" ) ,
261270 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+ ) ,
263275 ) ;
264276 }
265- }
266277
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+
273361 let field_words: Vec < & str > = field. ident . name . as_str ( ) . split ( '_' ) . collect ( ) ;
274362 if field_words. len ( ) >= item_name_words. len ( ) {
275363 // 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<'_>)
337425 }
338426}
339427
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-
399428impl LateLintPass < ' _ > for ItemNameRepetitions {
400429 fn check_item_post ( & mut self , _cx : & LateContext < ' _ > , _item : & Item < ' _ > ) {
401430 let last = self . modules . pop ( ) ;
@@ -458,17 +487,19 @@ impl LateLintPass<'_> for ItemNameRepetitions {
458487 }
459488 }
460489 }
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 ) {
464492 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+ } ,
466496 ItemKind :: Struct ( VariantData :: Struct { fields, .. } , _) => {
467- check_fields ( cx, self . struct_threshold , item, fields) ;
497+ self . check_fields ( cx, item, fields) ;
468498 } ,
469499 _ => ( ) ,
470500 }
471501 }
502+
472503 self . modules . push ( ( item. ident . name , item_camel, item. owner_id ) ) ;
473504 }
474505}
0 commit comments