Skip to content

Commit ba11790

Browse files
authored
Rollup merge of rust-lang#46907 - varkor:contrib-8, r=nagisa
Allow non-alphabetic underscores in camel case Certain identifiers, such as `X86_64`, cannot currently be unambiguously represented in camel case (`X8664`, `X86_64`, `X8_664`, etc. are all transformed to the same identifier). This change relaxes the rules so that underscores are permitted between two non-alphabetic characters under `#[forbid(non_camel_case_types)]`. Fixes rust-lang#34633 and fixes rust-lang#41621.
2 parents 8e7a609 + 3dff918 commit ba11790

File tree

2 files changed

+34
-4
lines changed

2 files changed

+34
-4
lines changed

src/librustc_lint/bad_style.rs

+26-4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ pub struct NonCamelCaseTypes;
5353

5454
impl NonCamelCaseTypes {
5555
fn check_case(&self, cx: &LateContext, sort: &str, name: ast::Name, span: Span) {
56+
fn char_has_case(c: char) -> bool {
57+
c.is_lowercase() || c.is_uppercase()
58+
}
59+
5660
fn is_camel_case(name: ast::Name) -> bool {
5761
let name = name.as_str();
5862
if name.is_empty() {
@@ -62,20 +66,38 @@ impl NonCamelCaseTypes {
6266

6367
// start with a non-lowercase letter rather than non-uppercase
6468
// ones (some scripts don't have a concept of upper/lowercase)
65-
!name.is_empty() && !name.chars().next().unwrap().is_lowercase() && !name.contains('_')
69+
!name.is_empty() && !name.chars().next().unwrap().is_lowercase() &&
70+
!name.contains("__") && !name.chars().collect::<Vec<_>>().windows(2).any(|pair| {
71+
// contains a capitalisable character followed by, or preceded by, an underscore
72+
char_has_case(pair[0]) && pair[1] == '_' ||
73+
char_has_case(pair[1]) && pair[0] == '_'
74+
})
6675
}
6776

6877
fn to_camel_case(s: &str) -> String {
69-
s.split('_')
70-
.flat_map(|word| {
78+
s.trim_matches('_')
79+
.split('_')
80+
.map(|word| {
7181
word.chars().enumerate().map(|(i, c)| if i == 0 {
7282
c.to_uppercase().collect::<String>()
7383
} else {
7484
c.to_lowercase().collect()
7585
})
86+
.collect::<Vec<_>>()
87+
.concat()
7688
})
89+
.filter(|x| !x.is_empty())
7790
.collect::<Vec<_>>()
78-
.concat()
91+
.iter().fold((String::new(), None), |(acc, prev): (String, Option<&String>), next| {
92+
// separate two components with an underscore if their boundary cannot
93+
// be distinguished using a uppercase/lowercase case distinction
94+
let join = if let Some(prev) = prev {
95+
let l = prev.chars().last().unwrap();
96+
let f = next.chars().next().unwrap();
97+
!char_has_case(l) && !char_has_case(f)
98+
} else { false };
99+
(acc + if join { "_" } else { "" } + next, Some(next))
100+
}).0
79101
}
80102

81103
if !is_camel_case(name) {

src/test/compile-fail/lint-non-camel-case-types.rs

+8
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,12 @@ struct foo7 {
4545

4646
type __ = isize; //~ ERROR type `__` should have a camel case name such as `CamelCase`
4747

48+
struct X86_64;
49+
50+
struct X86__64; //~ ERROR type `X86__64` should have a camel case name such as `X86_64`
51+
52+
struct Abc_123; //~ ERROR type `Abc_123` should have a camel case name such as `Abc123`
53+
54+
struct A1_b2_c3; //~ ERROR type `A1_b2_c3` should have a camel case name such as `A1B2C3`
55+
4856
fn main() { }

0 commit comments

Comments
 (0)