@@ -38,66 +38,87 @@ declare_lint! {
3838 "types, variants, traits and type parameters should have camel case names"
3939}
4040
41- #[ derive( Copy , Clone ) ]
42- pub struct NonCamelCaseTypes ;
41+ fn char_has_case ( c : char ) -> bool {
42+ c. is_lowercase ( ) || c. is_uppercase ( )
43+ }
4344
44- impl NonCamelCaseTypes {
45- fn check_case ( & self , cx : & EarlyContext < ' _ > , sort : & str , ident : & Ident ) {
46- fn char_has_case ( c : char ) -> bool {
47- c . is_lowercase ( ) || c . is_uppercase ( )
48- }
45+ fn is_camel_case ( name : & str ) -> bool {
46+ let name = name . trim_matches ( '_' ) ;
47+ if name . is_empty ( ) {
48+ return true ;
49+ }
4950
50- fn is_camel_case ( name : & str ) -> bool {
51- let name = name. trim_matches ( '_' ) ;
52- if name. is_empty ( ) {
53- return true ;
51+ // start with a non-lowercase letter rather than non-uppercase
52+ // ones (some scripts don't have a concept of upper/lowercase)
53+ !name. chars ( ) . next ( ) . unwrap ( ) . is_lowercase ( )
54+ && !name. contains ( "__" )
55+ && !name. chars ( ) . collect :: < Vec < _ > > ( ) . windows ( 2 ) . any ( |pair| {
56+ // contains a capitalisable character followed by, or preceded by, an underscore
57+ char_has_case ( pair[ 0 ] ) && pair[ 1 ] == '_' || char_has_case ( pair[ 1 ] ) && pair[ 0 ] == '_'
58+ } )
59+ }
60+
61+ fn to_camel_case ( s : & str ) -> String {
62+ s. trim_matches ( '_' )
63+ . split ( '_' )
64+ . filter ( |component| !component. is_empty ( ) )
65+ . map ( |component| {
66+ let mut camel_cased_component = String :: new ( ) ;
67+
68+ let mut new_word = true ;
69+ let mut prev_is_lower_case = true ;
70+
71+ for c in component. chars ( ) {
72+ // Preserve the case if an uppercase letter follows a lowercase letter, so that
73+ // `camelCase` is converted to `CamelCase`.
74+ if prev_is_lower_case && c. is_uppercase ( ) {
75+ new_word = true ;
76+ }
77+
78+ if new_word {
79+ camel_cased_component. push_str ( & c. to_uppercase ( ) . to_string ( ) ) ;
80+ } else {
81+ camel_cased_component. push_str ( & c. to_lowercase ( ) . to_string ( ) ) ;
82+ }
83+
84+ prev_is_lower_case = c. is_lowercase ( ) ;
85+ new_word = false ;
5486 }
5587
56- // start with a non-lowercase letter rather than non-uppercase
57- // ones (some scripts don't have a concept of upper/lowercase)
58- !name. is_empty ( ) && !name. chars ( ) . next ( ) . unwrap ( ) . is_lowercase ( ) &&
59- !name. contains ( "__" ) && !name. chars ( ) . collect :: < Vec < _ > > ( ) . windows ( 2 ) . any ( |pair| {
60- // contains a capitalisable character followed by, or preceded by, an underscore
61- char_has_case ( pair[ 0 ] ) && pair[ 1 ] == '_' ||
62- char_has_case ( pair[ 1 ] ) && pair[ 0 ] == '_'
63- } )
64- }
88+ camel_cased_component
89+ } )
90+ . fold (
91+ ( String :: new ( ) , None ) ,
92+ |( acc, prev) : ( String , Option < String > ) , next| {
93+ // separate two components with an underscore if their boundary cannot
94+ // be distinguished using a uppercase/lowercase case distinction
95+ let join = if let Some ( prev) = prev {
96+ let l = prev. chars ( ) . last ( ) . unwrap ( ) ;
97+ let f = next. chars ( ) . next ( ) . unwrap ( ) ;
98+ !char_has_case ( l) && !char_has_case ( f)
99+ } else {
100+ false
101+ } ;
102+ ( acc + if join { "_" } else { "" } + & next, Some ( next) )
103+ } ,
104+ )
105+ . 0
106+ }
65107
66- fn to_camel_case ( s : & str ) -> String {
67- s. trim_matches ( '_' )
68- . split ( '_' )
69- . map ( |word| {
70- word. chars ( ) . enumerate ( ) . map ( |( i, c) | if i == 0 {
71- c. to_uppercase ( ) . collect :: < String > ( )
72- } else {
73- c. to_lowercase ( ) . collect ( )
74- } )
75- . collect :: < String > ( )
76- } )
77- . filter ( |x| !x. is_empty ( ) )
78- . fold ( ( String :: new ( ) , None ) , |( acc, prev) : ( String , Option < String > ) , next| {
79- // separate two components with an underscore if their boundary cannot
80- // be distinguished using a uppercase/lowercase case distinction
81- let join = if let Some ( prev) = prev {
82- let l = prev. chars ( ) . last ( ) . unwrap ( ) ;
83- let f = next. chars ( ) . next ( ) . unwrap ( ) ;
84- !char_has_case ( l) && !char_has_case ( f)
85- } else { false } ;
86- ( acc + if join { "_" } else { "" } + & next, Some ( next) )
87- } ) . 0
88- }
108+ #[ derive( Copy , Clone ) ]
109+ pub struct NonCamelCaseTypes ;
89110
111+ impl NonCamelCaseTypes {
112+ fn check_case ( & self , cx : & EarlyContext < ' _ > , sort : & str , ident : & Ident ) {
90113 let name = & ident. name . as_str ( ) ;
91114
92115 if !is_camel_case ( name) {
93- let c = to_camel_case ( name) ;
94-
95- let msg = format ! ( "{} `{}` should have a camel case name" , sort, name) ;
116+ let msg = format ! ( "{} `{}` should have an upper camel case name" , sort, name) ;
96117 cx. struct_span_lint ( NON_CAMEL_CASE_TYPES , ident. span , & msg)
97118 . span_suggestion (
98119 ident. span ,
99- "convert the identifier to camel case" ,
100- c ,
120+ "convert the identifier to upper camel case" ,
121+ to_camel_case ( name ) ,
101122 Applicability :: MaybeIncorrect ,
102123 )
103124 . emit ( ) ;
@@ -119,11 +140,7 @@ impl EarlyLintPass for NonCamelCaseTypes {
119140 fn check_item ( & mut self , cx : & EarlyContext < ' _ > , it : & ast:: Item ) {
120141 let has_repr_c = it. attrs
121142 . iter ( )
122- . any ( |attr| {
123- attr:: find_repr_attrs ( & cx. sess . parse_sess , attr)
124- . iter ( )
125- . any ( |r| r == & attr:: ReprC )
126- } ) ;
143+ . any ( |attr| attr:: find_repr_attrs ( & cx. sess . parse_sess , attr) . contains ( & attr:: ReprC ) ) ;
127144
128145 if has_repr_c {
129146 return ;
@@ -439,3 +456,28 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonUpperCaseGlobals {
439456 }
440457 }
441458}
459+
460+ #[ cfg( test) ]
461+ mod tests {
462+ use super :: { is_camel_case, to_camel_case} ;
463+
464+ #[ test]
465+ fn camel_case ( ) {
466+ assert ! ( !is_camel_case( "userData" ) ) ;
467+ assert_eq ! ( to_camel_case( "userData" ) , "UserData" ) ;
468+
469+ assert ! ( is_camel_case( "X86_64" ) ) ;
470+
471+ assert ! ( !is_camel_case( "X86__64" ) ) ;
472+ assert_eq ! ( to_camel_case( "X86__64" ) , "X86_64" ) ;
473+
474+ assert ! ( !is_camel_case( "Abc_123" ) ) ;
475+ assert_eq ! ( to_camel_case( "Abc_123" ) , "Abc123" ) ;
476+
477+ assert ! ( !is_camel_case( "A1_b2_c3" ) ) ;
478+ assert_eq ! ( to_camel_case( "A1_b2_c3" ) , "A1B2C3" ) ;
479+
480+ assert ! ( !is_camel_case( "ONE_TWO_THREE" ) ) ;
481+ assert_eq ! ( to_camel_case( "ONE_TWO_THREE" ) , "OneTwoThree" ) ;
482+ }
483+ }
0 commit comments