@@ -51,34 +51,41 @@ declare_lint_pass!(NonCamelCaseTypes => [NON_CAMEL_CASE_TYPES]);
5151/// be upper cased or lower cased. For the purposes of the lint suggestion, we care about being able
5252/// to change the char's case.
5353fn char_has_case ( c : char ) -> bool {
54- let mut l = c. to_lowercase ( ) ;
55- let mut u = c . to_uppercase ( ) ;
56- while let Some ( l ) = l . next ( ) {
57- match u . next ( ) {
58- Some ( u ) if l != u = > return true ,
59- _ => { }
54+ let ( mut l, mut t ) = ( c. to_lowercase ( ) , c . to_titlecase ( ) ) ;
55+ loop {
56+ match ( l . next ( ) , t . next ( ) ) {
57+ ( lc , tc ) if lc != tc => return true ,
58+ ( None , None ) = > return false ,
59+ ( _ , _ ) => { }
6060 }
6161 }
62- u. next ( ) . is_some ( )
6362}
6463
65- fn is_camel_case ( name : & str ) -> bool {
64+ /// FIXME: we should add a more efficient version
65+ /// in the stdlib for this
66+ fn changes_when_titlecased ( c : char ) -> bool {
67+ !c. to_titlecase ( ) . eq ( [ c] )
68+ }
69+
70+ fn is_upper_camel_case ( name : & str ) -> bool {
6671 let name = name. trim_matches ( '_' ) ;
67- if name. is_empty ( ) {
72+ let Some ( fst ) = name. chars ( ) . next ( ) else {
6873 return true ;
69- }
74+ } ;
7075
71- // start with a non-lowercase letter rather than non-uppercase
72- // ones (some scripts don't have a concept of upper/lowercase)
73- !name. chars ( ) . next ( ) . unwrap ( ) . is_lowercase ( )
76+ // some scripts don't have a concept of upper/lowercase
77+ !changes_when_titlecased ( fst)
7478 && !name. contains ( "__" )
7579 && !name. chars ( ) . collect :: < Vec < _ > > ( ) . array_windows ( ) . any ( |& [ fst, snd] | {
7680 // contains a capitalisable character followed by, or preceded by, an underscore
77- char_has_case ( fst) && snd == '_' || char_has_case ( snd) && fst == '_'
81+ char_has_case ( fst) && snd == '_'
82+ || char_has_case ( snd) && fst == '_'
83+ // uses uppercase when it should use titlecase
84+ || snd. is_uppercase ( ) && changes_when_titlecased ( snd)
7885 } )
7986}
8087
81- fn to_camel_case ( s : & str ) -> String {
88+ fn to_upper_camel_case ( s : & str ) -> String {
8289 s. trim_matches ( '_' )
8390 . split ( '_' )
8491 . filter ( |component| !component. is_empty ( ) )
@@ -87,24 +94,31 @@ fn to_camel_case(s: &str) -> String {
8794
8895 let mut new_word = true ;
8996 let mut prev_is_lower_case = true ;
97+ let mut prev_is_lowercased_sigma = false ;
9098
9199 for c in component. chars ( ) {
92100 // Preserve the case if an uppercase letter follows a lowercase letter, so that
93101 // `camelCase` is converted to `CamelCase`.
94- if prev_is_lower_case && c. is_uppercase ( ) {
102+ if prev_is_lower_case && ( c. is_uppercase ( ) | c . is_titlecase ( ) ) {
95103 new_word = true ;
96104 }
97105
98106 if new_word {
99- camel_cased_component. extend ( c. to_uppercase ( ) ) ;
107+ camel_cased_component. extend ( c. to_titlecase ( ) ) ;
100108 } else {
101109 camel_cased_component. extend ( c. to_lowercase ( ) ) ;
102110 }
103111
104- prev_is_lower_case = c. is_lowercase ( ) ;
112+ prev_is_lower_case = c. is_lowercase ( ) || c. is_titlecase ( ) ;
113+ prev_is_lowercased_sigma = !new_word && c == 'Σ' ;
105114 new_word = false ;
106115 }
107116
117+ if prev_is_lowercased_sigma {
118+ camel_cased_component. pop ( ) ;
119+ camel_cased_component. push ( 'ς' ) ;
120+ }
121+
108122 camel_cased_component
109123 } )
110124 . fold ( ( String :: new ( ) , None ) , |( acc, prev) : ( String , Option < String > ) , next| {
@@ -126,8 +140,8 @@ impl NonCamelCaseTypes {
126140 fn check_case ( & self , cx : & EarlyContext < ' _ > , sort : & str , ident : & Ident ) {
127141 let name = ident. name . as_str ( ) ;
128142
129- if !is_camel_case ( name) {
130- let cc = to_camel_case ( name) ;
143+ if !is_upper_camel_case ( name) {
144+ let cc = to_upper_camel_case ( name) ;
131145 let sub = if * name != cc {
132146 NonCamelCaseTypeSub :: Suggestion { span : ident. span , replace : cc }
133147 } else {
@@ -239,14 +253,20 @@ impl NonSnakeCase {
239253 continue ;
240254 }
241255 for ch in s. chars ( ) {
242- if !buf. is_empty ( ) && buf != "'" && ch. is_uppercase ( ) && !last_upper {
243- words. push ( buf) ;
256+ if !buf. is_empty ( )
257+ && buf != "'"
258+ && ( ch. is_uppercase ( ) || ch. is_titlecase ( ) )
259+ && !last_upper
260+ {
261+ // We lowercase only at the end, to handle final sigma correctly
262+ words. push ( buf. to_lowercase ( ) ) ;
244263 buf = String :: new ( ) ;
245264 }
246- last_upper = ch. is_uppercase ( ) ;
247- buf. extend ( ch. to_lowercase ( ) ) ;
265+ last_upper = ch. is_uppercase ( ) || ch . is_titlecase ( ) ;
266+ buf. push ( ch) ;
248267 }
249- words. push ( buf) ;
268+ // We lowercase only at the end, to handle final sigma correctly
269+ words. push ( buf. to_lowercase ( ) ) ;
250270 }
251271 words. join ( "_" )
252272 }
@@ -266,7 +286,8 @@ impl NonSnakeCase {
266286
267287 // This correctly handles letters in languages with and without
268288 // cases, as well as numbers and underscores.
269- !ident. chars ( ) . any ( char:: is_uppercase)
289+ // FIXME: we should add a standard library impl of `c.to_lowercase().eq([c])`
290+ ident. chars ( ) . all ( |c| c. to_lowercase ( ) . eq ( [ c] ) )
270291 }
271292
272293 let name = ident. name . as_str ( ) ;
@@ -478,10 +499,12 @@ impl<'a, 'b, F: FnOnce() -> NonUpperCaseGlobal<'b>> Diagnostic<'a, ()>
478499impl NonUpperCaseGlobals {
479500 fn check_upper_case ( cx : & LateContext < ' _ > , sort : & str , did : Option < LocalDefId > , ident : & Ident ) {
480501 let name = ident. name . as_str ( ) ;
481- if name. chars ( ) . any ( |c| c. is_lowercase ( ) ) {
502+ // FIXME: we should add a more efficient version
503+ // in the stdlib for `c.to_uppercase().eq([c])`
504+ if !name. chars ( ) . all ( |c| c. to_uppercase ( ) . eq ( [ c] ) ) {
482505 let uc = NonSnakeCase :: to_snake_case ( name) . to_uppercase ( ) ;
483506
484- // If the item is exported, suggesting changing it's name would be breaking- change
507+ // If the item is exported, suggesting changing its name would be a breaking change
485508 // and could break users without a "nice" applicable fix, so let's avoid it.
486509 let can_change_usages = if let Some ( did) = did {
487510 !cx. tcx . effective_visibilities ( ( ) ) . is_exported ( did)
0 commit comments