@@ -47,34 +47,46 @@ declare_lint! {
4747
4848declare_lint_pass ! ( NonCamelCaseTypes => [ NON_CAMEL_CASE_TYPES ] ) ;
4949
50- /// Some unicode characters *have* case, are considered upper case or lower case, but they *can't*
51- /// be upper cased or lower cased. For the purposes of the lint suggestion, we care about being able
50+ /// Some unicode characters *have* case, are considered upper, title, or lower case, but they *can't*
51+ /// be title 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- !c. to_lowercase ( ) . eq ( c. to_uppercase ( ) )
54+ !c. to_lowercase ( ) . eq ( c. to_titlecase ( ) )
5555}
5656
57- // contains a capitalisable character followed by, or preceded by, an underscore
58- fn has_underscore_case ( s : & str ) -> bool {
57+ /// FIXME: we should add a more efficient version
58+ /// in the stdlib for this
59+ fn changes_when_titlecased ( c : char ) -> bool {
60+ !c. to_titlecase ( ) . eq ( [ c] )
61+ }
62+
63+ // contains a capitalisable character followed by, or preceded by, an underscore,
64+ // or contains an uppercase character that changes when titlecased,
65+ // or contains `__`
66+ fn not_camel_case ( s : & str ) -> bool {
5967 let mut last = '\0' ;
60- s. chars ( ) . any ( |c| match ( std:: mem:: replace ( & mut last, c) , c) {
61- ( '_' , cs) | ( cs, '_' ) => char_has_case ( cs) ,
62- _ => false ,
68+ s. chars ( ) . any ( |snd| {
69+ let fst = std:: mem:: replace ( & mut last, snd) ;
70+ match ( fst, snd) {
71+ ( '_' , '_' ) => return true ,
72+ ( '_' , _) if char_has_case ( snd) => return true ,
73+ ( _, '_' ) if char_has_case ( fst) => return true ,
74+ _ => snd. is_uppercase ( ) && changes_when_titlecased ( snd) ,
75+ }
6376 } )
6477}
6578
66- fn is_camel_case ( name : & str ) -> bool {
79+ fn is_upper_camel_case ( name : & str ) -> bool {
6780 let name = name. trim_matches ( '_' ) ;
6881 let Some ( first) = name. chars ( ) . next ( ) else {
6982 return true ;
7083 } ;
7184
72- // start with a non-lowercase letter rather than uppercase
73- // ones (some scripts don't have a concept of upper/lowercase)
74- !( first. is_lowercase ( ) || name. contains ( "__" ) || has_underscore_case ( name) )
85+ // some scripts don't have a concept of upper/lowercase
86+ !( changes_when_titlecased ( first) || not_camel_case ( name) )
7587}
7688
77- fn to_camel_case ( s : & str ) -> String {
89+ fn to_upper_camel_case ( s : & str ) -> String {
7890 s. trim_matches ( '_' )
7991 . split ( '_' )
8092 . filter ( |component| !component. is_empty ( ) )
@@ -83,24 +95,31 @@ fn to_camel_case(s: &str) -> String {
8395
8496 let mut new_word = true ;
8597 let mut prev_is_lower_case = true ;
98+ let mut prev_is_lowercased_sigma = false ;
8699
87100 for c in component. chars ( ) {
88101 // Preserve the case if an uppercase letter follows a lowercase letter, so that
89102 // `camelCase` is converted to `CamelCase`.
90- if prev_is_lower_case && c. is_uppercase ( ) {
103+ if prev_is_lower_case && ( c. is_uppercase ( ) | c . is_titlecase ( ) ) {
91104 new_word = true ;
92105 }
93106
94107 if new_word {
95- camel_cased_component. extend ( c. to_uppercase ( ) ) ;
108+ camel_cased_component. extend ( c. to_titlecase ( ) ) ;
96109 } else {
97110 camel_cased_component. extend ( c. to_lowercase ( ) ) ;
98111 }
99112
100- prev_is_lower_case = c. is_lowercase ( ) ;
113+ prev_is_lower_case = c. is_lowercase ( ) || c. is_titlecase ( ) ;
114+ prev_is_lowercased_sigma = !new_word && c == 'Σ' ;
101115 new_word = false ;
102116 }
103117
118+ if prev_is_lowercased_sigma {
119+ camel_cased_component. pop ( ) ;
120+ camel_cased_component. push ( 'ς' ) ;
121+ }
122+
104123 camel_cased_component
105124 } )
106125 . fold ( ( String :: new ( ) , None ) , |( acc, prev) : ( String , Option < String > ) , next| {
@@ -122,8 +141,8 @@ impl NonCamelCaseTypes {
122141 fn check_case ( & self , cx : & EarlyContext < ' _ > , sort : & str , ident : & Ident ) {
123142 let name = ident. name . as_str ( ) ;
124143
125- if !is_camel_case ( name) {
126- let cc = to_camel_case ( name) ;
144+ if !is_upper_camel_case ( name) {
145+ let cc = to_upper_camel_case ( name) ;
127146 let sub = if * name != cc {
128147 NonCamelCaseTypeSub :: Suggestion { span : ident. span , replace : cc }
129148 } else {
@@ -235,14 +254,20 @@ impl NonSnakeCase {
235254 continue ;
236255 }
237256 for ch in s. chars ( ) {
238- if !buf. is_empty ( ) && buf != "'" && ch. is_uppercase ( ) && !last_upper {
239- words. push ( buf) ;
257+ if !buf. is_empty ( )
258+ && buf != "'"
259+ && ( ch. is_uppercase ( ) || ch. is_titlecase ( ) )
260+ && !last_upper
261+ {
262+ // We lowercase only at the end, to handle final sigma correctly
263+ words. push ( buf. to_lowercase ( ) ) ;
240264 buf = String :: new ( ) ;
241265 }
242- last_upper = ch. is_uppercase ( ) ;
243- buf. extend ( ch. to_lowercase ( ) ) ;
266+ last_upper = ch. is_uppercase ( ) || ch . is_titlecase ( ) ;
267+ buf. push ( ch) ;
244268 }
245- words. push ( buf) ;
269+ // We lowercase only at the end, to handle final sigma correctly
270+ words. push ( buf. to_lowercase ( ) ) ;
246271 }
247272 words. join ( "_" )
248273 }
@@ -262,7 +287,8 @@ impl NonSnakeCase {
262287
263288 // This correctly handles letters in languages with and without
264289 // cases, as well as numbers and underscores.
265- !ident. chars ( ) . any ( char:: is_uppercase)
290+ // FIXME: we should add a standard library impl of `c.to_lowercase().eq([c])`
291+ ident. chars ( ) . all ( |c| c. to_lowercase ( ) . eq ( [ c] ) )
266292 }
267293
268294 let name = ident. name . as_str ( ) ;
@@ -474,10 +500,12 @@ impl<'a, 'b, F: FnOnce() -> NonUpperCaseGlobal<'b>> Diagnostic<'a, ()>
474500impl NonUpperCaseGlobals {
475501 fn check_upper_case ( cx : & LateContext < ' _ > , sort : & str , did : Option < LocalDefId > , ident : & Ident ) {
476502 let name = ident. name . as_str ( ) ;
477- if name. chars ( ) . any ( |c| c. is_lowercase ( ) ) {
503+ // FIXME: we should add a more efficient version
504+ // in the stdlib for `c.to_uppercase().eq([c])`
505+ if !name. chars ( ) . all ( |c| c. to_uppercase ( ) . eq ( [ c] ) ) {
478506 let uc = NonSnakeCase :: to_snake_case ( name) . to_uppercase ( ) ;
479507
480- // If the item is exported, suggesting changing it's name would be breaking- change
508+ // If the item is exported, suggesting changing its name would be a breaking change
481509 // and could break users without a "nice" applicable fix, so let's avoid it.
482510 let can_change_usages = if let Some ( did) = did {
483511 !cx. tcx . effective_visibilities ( ( ) ) . is_exported ( did)
0 commit comments