2
2
3
3
use clippy_utils:: diagnostics:: { span_lint, span_lint_and_help} ;
4
4
use clippy_utils:: source:: is_present_in_source;
5
- use clippy_utils:: str_utils:: { self , count_match_end, count_match_start} ;
6
- use rustc_hir:: { EnumDef , Item , ItemKind } ;
5
+ use clippy_utils:: str_utils:: { camel_case_split , count_match_end, count_match_start} ;
6
+ use rustc_hir:: { EnumDef , Item , ItemKind , Variant } ;
7
7
use rustc_lint:: { LateContext , LateLintPass } ;
8
8
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
9
9
use rustc_span:: source_map:: Span ;
@@ -18,6 +18,12 @@ declare_clippy_lint! {
18
18
/// Enumeration variant names should specify their variant,
19
19
/// not repeat the enumeration name.
20
20
///
21
+ /// ### Limitations
22
+ /// Characters with no casing will be considered when comparing prefixes/suffixes
23
+ /// This applies to numbers and non-ascii characters without casing
24
+ /// e.g. `Foo1` and `Foo2` is considered to have different prefixes
25
+ /// (the prefixes are `Foo1` and `Foo2` respectively), as also `Bar螃`, `Bar蟹`
26
+ ///
21
27
/// ### Example
22
28
/// ```rust
23
29
/// enum Cake {
@@ -120,72 +126,73 @@ impl_lint_pass!(EnumVariantNames => [
120
126
MODULE_INCEPTION
121
127
] ) ;
122
128
123
- fn check_variant (
124
- cx : & LateContext < ' _ > ,
125
- threshold : u64 ,
126
- def : & EnumDef < ' _ > ,
127
- item_name : & str ,
128
- item_name_chars : usize ,
129
- span : Span ,
130
- ) {
129
+ fn check_enum_start ( cx : & LateContext < ' _ > , item_name : & str , variant : & Variant < ' _ > ) {
130
+ let name = variant. ident . name . as_str ( ) ;
131
+ let item_name_chars = item_name. chars ( ) . count ( ) ;
132
+
133
+ if count_match_start ( item_name, & name) . char_count == item_name_chars
134
+ && name. chars ( ) . nth ( item_name_chars) . map_or ( false , |c| !c. is_lowercase ( ) )
135
+ && name. chars ( ) . nth ( item_name_chars + 1 ) . map_or ( false , |c| !c. is_numeric ( ) )
136
+ {
137
+ span_lint (
138
+ cx,
139
+ ENUM_VARIANT_NAMES ,
140
+ variant. span ,
141
+ "variant name starts with the enum's name" ,
142
+ ) ;
143
+ }
144
+ }
145
+
146
+ fn check_enum_end ( cx : & LateContext < ' _ > , item_name : & str , variant : & Variant < ' _ > ) {
147
+ let name = variant. ident . name . as_str ( ) ;
148
+ let item_name_chars = item_name. chars ( ) . count ( ) ;
149
+
150
+ if count_match_end ( item_name, & name) . char_count == item_name_chars {
151
+ span_lint (
152
+ cx,
153
+ ENUM_VARIANT_NAMES ,
154
+ variant. span ,
155
+ "variant name ends with the enum's name" ,
156
+ ) ;
157
+ }
158
+ }
159
+
160
+ fn check_variant ( cx : & LateContext < ' _ > , threshold : u64 , def : & EnumDef < ' _ > , item_name : & str , span : Span ) {
131
161
if ( def. variants . len ( ) as u64 ) < threshold {
132
162
return ;
133
163
}
134
- for var in def. variants {
135
- let name = var. ident . name . as_str ( ) ;
136
- if count_match_start ( item_name, & name) . char_count == item_name_chars
137
- && name. chars ( ) . nth ( item_name_chars) . map_or ( false , |c| !c. is_lowercase ( ) )
138
- && name. chars ( ) . nth ( item_name_chars + 1 ) . map_or ( false , |c| !c. is_numeric ( ) )
139
- {
140
- span_lint (
141
- cx,
142
- ENUM_VARIANT_NAMES ,
143
- var. span ,
144
- "variant name starts with the enum's name" ,
145
- ) ;
146
- }
147
- if count_match_end ( item_name, & name) . char_count == item_name_chars {
148
- span_lint (
149
- cx,
150
- ENUM_VARIANT_NAMES ,
151
- var. span ,
152
- "variant name ends with the enum's name" ,
153
- ) ;
154
- }
155
- }
164
+
156
165
let first = & def. variants [ 0 ] . ident . name . as_str ( ) ;
157
- let mut pre = & first[ ..str_utils:: camel_case_until ( & * first) . byte_index ] ;
158
- let mut post = & first[ str_utils:: camel_case_start ( & * first) . byte_index ..] ;
166
+ let mut pre = camel_case_split ( first) ;
167
+ let mut post = pre. clone ( ) ;
168
+ post. reverse ( ) ;
159
169
for var in def. variants {
170
+ check_enum_start ( cx, item_name, var) ;
171
+ check_enum_end ( cx, item_name, var) ;
160
172
let name = var. ident . name . as_str ( ) ;
161
173
162
- let pre_match = count_match_start ( pre, & name) . byte_count ;
163
- pre = & pre[ ..pre_match] ;
164
- let pre_camel = str_utils:: camel_case_until ( pre) . byte_index ;
165
- pre = & pre[ ..pre_camel] ;
166
- while let Some ( ( next, last) ) = name[ pre. len ( ) ..] . chars ( ) . zip ( pre. chars ( ) . rev ( ) ) . next ( ) {
167
- if next. is_numeric ( ) {
168
- return ;
169
- }
170
- if next. is_lowercase ( ) {
171
- let last = pre. len ( ) - last. len_utf8 ( ) ;
172
- let last_camel = str_utils:: camel_case_until ( & pre[ ..last] ) ;
173
- pre = & pre[ ..last_camel. byte_index ] ;
174
- } else {
175
- break ;
176
- }
177
- }
174
+ let variant_split = camel_case_split ( & name) ;
178
175
179
- let post_match = count_match_end ( post, & name) ;
180
- let post_end = post. len ( ) - post_match. byte_count ;
181
- post = & post[ post_end..] ;
182
- let post_camel = str_utils:: camel_case_start ( post) ;
183
- post = & post[ post_camel. byte_index ..] ;
176
+ pre = pre
177
+ . iter ( )
178
+ . zip ( variant_split. iter ( ) )
179
+ . take_while ( |( a, b) | a == b)
180
+ . map ( |e| * e. 0 )
181
+ . collect ( ) ;
182
+ post = post
183
+ . iter ( )
184
+ . zip ( variant_split. iter ( ) . rev ( ) )
185
+ . take_while ( |( a, b) | a == b)
186
+ . map ( |e| * e. 0 )
187
+ . collect ( ) ;
184
188
}
185
189
let ( what, value) = match ( pre. is_empty ( ) , post. is_empty ( ) ) {
186
190
( true , true ) => return ,
187
- ( false , _) => ( "pre" , pre) ,
188
- ( true , false ) => ( "post" , post) ,
191
+ ( false , _) => ( "pre" , pre. join ( "" ) ) ,
192
+ ( true , false ) => {
193
+ post. reverse ( ) ;
194
+ ( "post" , post. join ( "" ) )
195
+ } ,
189
196
} ;
190
197
span_lint_and_help (
191
198
cx,
@@ -233,7 +240,6 @@ impl LateLintPass<'_> for EnumVariantNames {
233
240
#[ allow( clippy:: similar_names) ]
234
241
fn check_item ( & mut self , cx : & LateContext < ' _ > , item : & Item < ' _ > ) {
235
242
let item_name = item. ident . name . as_str ( ) ;
236
- let item_name_chars = item_name. chars ( ) . count ( ) ;
237
243
let item_camel = to_camel_case ( & item_name) ;
238
244
if !item. span . from_expansion ( ) && is_present_in_source ( cx, item. span ) {
239
245
if let Some ( & ( ref mod_name, ref mod_camel) ) = self . modules . last ( ) {
@@ -283,7 +289,7 @@ impl LateLintPass<'_> for EnumVariantNames {
283
289
}
284
290
if let ItemKind :: Enum ( ref def, _) = item. kind {
285
291
if !( self . avoid_breaking_exported_api && cx. access_levels . is_exported ( item. def_id ) ) {
286
- check_variant ( cx, self . threshold , def, & item_name, item_name_chars , item. span ) ;
292
+ check_variant ( cx, self . threshold , def, & item_name, item. span ) ;
287
293
}
288
294
}
289
295
self . modules . push ( ( item. ident . name , item_camel) ) ;
0 commit comments