Skip to content

Commit

Permalink
Rollup merge of rust-lang#46907 - varkor:contrib-8, r=nagisa
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kennytm authored Jan 4, 2018
2 parents 8e7a609 + 3dff918 commit ba11790
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 4 deletions.
30 changes: 26 additions & 4 deletions src/librustc_lint/bad_style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ pub struct NonCamelCaseTypes;

impl NonCamelCaseTypes {
fn check_case(&self, cx: &LateContext, sort: &str, name: ast::Name, span: Span) {
fn char_has_case(c: char) -> bool {
c.is_lowercase() || c.is_uppercase()
}

fn is_camel_case(name: ast::Name) -> bool {
let name = name.as_str();
if name.is_empty() {
Expand All @@ -62,20 +66,38 @@ impl NonCamelCaseTypes {

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

fn to_camel_case(s: &str) -> String {
s.split('_')
.flat_map(|word| {
s.trim_matches('_')
.split('_')
.map(|word| {
word.chars().enumerate().map(|(i, c)| if i == 0 {
c.to_uppercase().collect::<String>()
} else {
c.to_lowercase().collect()
})
.collect::<Vec<_>>()
.concat()
})
.filter(|x| !x.is_empty())
.collect::<Vec<_>>()
.concat()
.iter().fold((String::new(), None), |(acc, prev): (String, Option<&String>), next| {
// separate two components with an underscore if their boundary cannot
// be distinguished using a uppercase/lowercase case distinction
let join = if let Some(prev) = prev {
let l = prev.chars().last().unwrap();
let f = next.chars().next().unwrap();
!char_has_case(l) && !char_has_case(f)
} else { false };
(acc + if join { "_" } else { "" } + next, Some(next))
}).0
}

if !is_camel_case(name) {
Expand Down
8 changes: 8 additions & 0 deletions src/test/compile-fail/lint-non-camel-case-types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,12 @@ struct foo7 {

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

struct X86_64;

struct X86__64; //~ ERROR type `X86__64` should have a camel case name such as `X86_64`

struct Abc_123; //~ ERROR type `Abc_123` should have a camel case name such as `Abc123`

struct A1_b2_c3; //~ ERROR type `A1_b2_c3` should have a camel case name such as `A1B2C3`

fn main() { }

0 comments on commit ba11790

Please sign in to comment.