@@ -196,80 +196,100 @@ fn have_no_extra_prefix(prefixes: &[&str]) -> bool {
196196 prefixes. iter ( ) . all ( |p| p == & "" || p == & "_" )
197197}
198198
199- fn check_fields ( cx : & LateContext < ' _ > , threshold : u64 , item : & Item < ' _ > , fields : & [ FieldDef < ' _ > ] ) {
200- if ( fields. len ( ) as u64 ) < threshold {
201- return ;
202- }
203-
204- check_struct_name_repetition ( cx , item , fields ) ;
199+ impl ItemNameRepetitions {
200+ /// Lint the names of struct fields against the name of the struct.
201+ fn check_fields ( & self , cx : & LateContext < ' _ > , item : & Item < ' _ > , fields : & [ FieldDef < ' _ > ] ) {
202+ if ( fields . len ( ) as u64 ) < self . struct_threshold {
203+ return ;
204+ }
205205
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 ;
206+ self . check_struct_name_repetition ( cx, item, fields) ;
207+ self . check_common_affix ( cx, item, fields) ;
210208 }
211209
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 {
210+ fn check_common_affix ( & self , cx : & LateContext < ' _ > , item : & Item < ' _ > , fields : & [ FieldDef < ' _ > ] ) {
211+ // if the SyntaxContext of the identifiers of the fields and struct differ dont lint them.
212+ // this prevents linting in macros in which the location of the field identifier names differ
213+ if !fields. iter ( ) . all ( |field| item. ident . span . eq_ctxt ( field. ident . span ) ) {
221214 return ;
222215 }
223216
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 ( ) ;
236- }
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) ,
251- } ;
252- if fields. iter ( ) . all ( |field| is_bool ( field. ty ) ) {
253- // If all fields are booleans, we don't want to emit this lint.
217+ if self . avoid_breaking_exported_api
218+ && fields
219+ . iter ( )
220+ . any ( |field| cx. effective_visibilities . is_exported ( field. def_id ) )
221+ {
254222 return ;
255223 }
256- span_lint_and_help (
257- cx,
258- STRUCT_FIELD_NAMES ,
259- item. span ,
260- format ! ( "all fields have the same {what}fix: `{value}`" ) ,
261- None ,
262- format ! ( "remove the {what}fixes" ) ,
263- ) ;
224+
225+ let mut pre: Vec < & str > = match fields. first ( ) {
226+ Some ( first_field) => first_field. ident . name . as_str ( ) . split ( '_' ) . collect ( ) ,
227+ None => return ,
228+ } ;
229+ let mut post = pre. clone ( ) ;
230+ post. reverse ( ) ;
231+ for field in fields {
232+ let field_split: Vec < & str > = field. ident . name . as_str ( ) . split ( '_' ) . collect ( ) ;
233+ if field_split. len ( ) == 1 {
234+ return ;
235+ }
236+
237+ pre = pre
238+ . into_iter ( )
239+ . zip ( field_split. iter ( ) )
240+ . take_while ( |( a, b) | & a == b)
241+ . map ( |e| e. 0 )
242+ . collect ( ) ;
243+ post = post
244+ . into_iter ( )
245+ . zip ( field_split. iter ( ) . rev ( ) )
246+ . take_while ( |( a, b) | & a == b)
247+ . map ( |e| e. 0 )
248+ . collect ( ) ;
249+ }
250+ let prefix = pre. join ( "_" ) ;
251+ post. reverse ( ) ;
252+ let postfix = match post. last ( ) {
253+ Some ( & "" ) => post. join ( "_" ) + "_" ,
254+ Some ( _) | None => post. join ( "_" ) ,
255+ } ;
256+ if fields. len ( ) > 1 {
257+ let ( what, value) = match (
258+ prefix. is_empty ( ) || prefix. chars ( ) . all ( |c| c == '_' ) ,
259+ postfix. is_empty ( ) ,
260+ ) {
261+ ( true , true ) => return ,
262+ ( false , _) => ( "pre" , prefix) ,
263+ ( true , false ) => ( "post" , postfix) ,
264+ } ;
265+ if fields. iter ( ) . all ( |field| is_bool ( field. ty ) ) {
266+ // If all fields are booleans, we don't want to emit this lint.
267+ return ;
268+ }
269+ span_lint_and_help (
270+ cx,
271+ STRUCT_FIELD_NAMES ,
272+ item. span ,
273+ format ! ( "all fields have the same {what}fix: `{value}`" ) ,
274+ None ,
275+ format ! ( "remove the {what}fixes" ) ,
276+ ) ;
277+ }
264278 }
265- }
266279
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)
280+ fn check_struct_name_repetition ( & self , cx : & LateContext < ' _ > , item : & Item < ' _ > , fields : & [ FieldDef < ' _ > ] ) {
281+ let snake_name = to_snake_case ( item. ident . name . as_str ( ) ) ;
282+ let item_name_words: Vec < & str > = snake_name. split ( '_' ) . collect ( ) ;
283+ for field in fields {
284+ if self . avoid_breaking_exported_api && cx. effective_visibilities . is_exported ( field. def_id ) {
285+ continue ;
286+ }
287+
288+ if !field. ident . span . eq_ctxt ( item. ident . span ) {
289+ // consider linting only if the field identifier has the same SyntaxContext as the item(struct)
290+ continue ;
291+ }
292+
273293 let field_words: Vec < & str > = field. ident . name . as_str ( ) . split ( '_' ) . collect ( ) ;
274294 if field_words. len ( ) >= item_name_words. len ( ) {
275295 // if the field name is shorter than the struct name it cannot contain it
@@ -458,17 +478,23 @@ impl LateLintPass<'_> for ItemNameRepetitions {
458478 }
459479 }
460480 }
461- if !( self . avoid_breaking_exported_api && cx. effective_visibilities . is_exported ( item. owner_id . def_id ) )
462- && span_is_local ( item. span )
463- {
481+
482+ if span_is_local ( item. span ) {
464483 match item. kind {
465- ItemKind :: Enum ( def, _) => check_variant ( cx, self . enum_threshold , & def, item_name, item. span ) ,
484+ ItemKind :: Enum ( def, _) => {
485+ if !( self . avoid_breaking_exported_api
486+ && cx. effective_visibilities . is_exported ( item. owner_id . def_id ) )
487+ {
488+ check_variant ( cx, self . enum_threshold , & def, item_name, item. span ) ;
489+ }
490+ } ,
466491 ItemKind :: Struct ( VariantData :: Struct { fields, .. } , _) => {
467- check_fields ( cx, self . struct_threshold , item, fields) ;
492+ self . check_fields ( cx, item, fields) ;
468493 } ,
469494 _ => ( ) ,
470495 }
471496 }
497+
472498 self . modules . push ( ( item. ident . name , item_camel, item. owner_id ) ) ;
473499 }
474500}
0 commit comments