aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty/src/diagnostics
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_ty/src/diagnostics')
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs28
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check/case_conv.rs244
-rw-r--r--crates/hir_ty/src/diagnostics/unsafe_check.rs18
3 files changed, 168 insertions, 122 deletions
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index f987636fe..f179c62b7 100644
--- a/crates/hir_ty/src/diagnostics/decl_check.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check.rs
@@ -708,11 +708,23 @@ fn foo() {
708 } 708 }
709 709
710 #[test] 710 #[test]
711 fn incorrect_struct_name() { 711 fn incorrect_struct_names() {
712 check_diagnostics( 712 check_diagnostics(
713 r#" 713 r#"
714struct non_camel_case_name {} 714struct non_camel_case_name {}
715 // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName` 715 // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName`
716
717struct SCREAMING_CASE {}
718 // ^^^^^^^^^^^^^^ Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase`
719"#,
720 );
721 }
722
723 #[test]
724 fn no_diagnostic_for_camel_cased_acronyms_in_struct_name() {
725 check_diagnostics(
726 r#"
727struct AABB {}
716"#, 728"#,
717 ); 729 );
718 } 730 }
@@ -728,11 +740,23 @@ struct SomeStruct { SomeField: u8 }
728 } 740 }
729 741
730 #[test] 742 #[test]
731 fn incorrect_enum_name() { 743 fn incorrect_enum_names() {
732 check_diagnostics( 744 check_diagnostics(
733 r#" 745 r#"
734enum some_enum { Val(u8) } 746enum some_enum { Val(u8) }
735 // ^^^^^^^^^ Enum `some_enum` should have CamelCase name, e.g. `SomeEnum` 747 // ^^^^^^^^^ Enum `some_enum` should have CamelCase name, e.g. `SomeEnum`
748
749enum SOME_ENUM
750 // ^^^^^^^^^ Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum`
751"#,
752 );
753 }
754
755 #[test]
756 fn no_diagnostic_for_camel_cased_acronyms_in_enum_name() {
757 check_diagnostics(
758 r#"
759enum AABB {}
736"#, 760"#,
737 ); 761 );
738 } 762 }
diff --git a/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs b/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs
index 3800f2a6b..b0144a289 100644
--- a/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs
@@ -1,150 +1,145 @@
1//! Functions for string case manipulation, such as detecting the identifier case, 1//! Functions for string case manipulation, such as detecting the identifier case,
2//! and converting it into appropriate form. 2//! and converting it into appropriate form.
3 3
4#[derive(Debug)] 4// Code that was taken from rustc was taken at commit 89fdb30,
5enum DetectedCase { 5// from file /compiler/rustc_lint/src/nonstandard_style.rs
6 LowerCamelCase,
7 UpperCamelCase,
8 LowerSnakeCase,
9 UpperSnakeCase,
10 Unknown,
11}
12
13fn detect_case(ident: &str) -> DetectedCase {
14 let trimmed_ident = ident.trim_matches('_');
15 let first_lowercase = trimmed_ident.starts_with(|chr: char| chr.is_ascii_lowercase());
16 let mut has_lowercase = first_lowercase;
17 let mut has_uppercase = false;
18 let mut has_underscore = false;
19
20 for chr in trimmed_ident.chars() {
21 if chr == '_' {
22 has_underscore = true;
23 } else if chr.is_ascii_uppercase() {
24 has_uppercase = true;
25 } else if chr.is_ascii_lowercase() {
26 has_lowercase = true;
27 }
28 }
29
30 if has_uppercase {
31 if !has_lowercase {
32 DetectedCase::UpperSnakeCase
33 } else if !has_underscore {
34 if first_lowercase {
35 DetectedCase::LowerCamelCase
36 } else {
37 DetectedCase::UpperCamelCase
38 }
39 } else {
40 // It has uppercase, it has lowercase, it has underscore.
41 // No assumptions here
42 DetectedCase::Unknown
43 }
44 } else {
45 DetectedCase::LowerSnakeCase
46 }
47}
48 6
49/// Converts an identifier to an UpperCamelCase form. 7/// Converts an identifier to an UpperCamelCase form.
50/// Returns `None` if the string is already is UpperCamelCase. 8/// Returns `None` if the string is already is UpperCamelCase.
51pub fn to_camel_case(ident: &str) -> Option<String> { 9pub fn to_camel_case(ident: &str) -> Option<String> {
52 let detected_case = detect_case(ident); 10 if is_camel_case(ident) {
53 11 return None;
54 match detected_case {
55 DetectedCase::UpperCamelCase => return None,
56 DetectedCase::LowerCamelCase => {
57 let mut first_capitalized = false;
58 let output = ident
59 .chars()
60 .map(|chr| {
61 if !first_capitalized && chr.is_ascii_lowercase() {
62 first_capitalized = true;
63 chr.to_ascii_uppercase()
64 } else {
65 chr
66 }
67 })
68 .collect();
69 return Some(output);
70 }
71 _ => {}
72 } 12 }
73 13
74 let mut output = String::with_capacity(ident.len()); 14 // Taken from rustc.
75 15 let ret = ident
76 let mut capital_added = false; 16 .trim_matches('_')
77 for chr in ident.chars() { 17 .split('_')
78 if chr.is_alphabetic() { 18 .filter(|component| !component.is_empty())
79 if !capital_added { 19 .map(|component| {
80 output.push(chr.to_ascii_uppercase()); 20 let mut camel_cased_component = String::new();
81 capital_added = true; 21
82 } else { 22 let mut new_word = true;
83 output.push(chr.to_ascii_lowercase()); 23 let mut prev_is_lower_case = true;
24
25 for c in component.chars() {
26 // Preserve the case if an uppercase letter follows a lowercase letter, so that
27 // `camelCase` is converted to `CamelCase`.
28 if prev_is_lower_case && c.is_uppercase() {
29 new_word = true;
30 }
31
32 if new_word {
33 camel_cased_component.push_str(&c.to_uppercase().to_string());
34 } else {
35 camel_cased_component.push_str(&c.to_lowercase().to_string());
36 }
37
38 prev_is_lower_case = c.is_lowercase();
39 new_word = false;
84 } 40 }
85 } else if chr == '_' {
86 // Skip this character and make the next one capital.
87 capital_added = false;
88 } else {
89 // Put the characted as-is.
90 output.push(chr);
91 }
92 }
93 41
94 if output == ident { 42 camel_cased_component
95 // While we didn't detect the correct case at the beginning, there 43 })
96 // may be special cases: e.g. `A` is both valid CamelCase and UPPER_SNAKE_CASE. 44 .fold((String::new(), None), |(acc, prev): (String, Option<String>), next| {
97 None 45 // separate two components with an underscore if their boundary cannot
98 } else { 46 // be distinguished using a uppercase/lowercase case distinction
99 Some(output) 47 let join = if let Some(prev) = prev {
100 } 48 let l = prev.chars().last().unwrap();
49 let f = next.chars().next().unwrap();
50 !char_has_case(l) && !char_has_case(f)
51 } else {
52 false
53 };
54 (acc + if join { "_" } else { "" } + &next, Some(next))
55 })
56 .0;
57 Some(ret)
101} 58}
102 59
103/// Converts an identifier to a lower_snake_case form. 60/// Converts an identifier to a lower_snake_case form.
104/// Returns `None` if the string is already in lower_snake_case. 61/// Returns `None` if the string is already in lower_snake_case.
105pub fn to_lower_snake_case(ident: &str) -> Option<String> { 62pub fn to_lower_snake_case(ident: &str) -> Option<String> {
106 // First, assume that it's UPPER_SNAKE_CASE. 63 if is_lower_snake_case(ident) {
107 match detect_case(ident) { 64 return None;
108 DetectedCase::LowerSnakeCase => return None, 65 } else if is_upper_snake_case(ident) {
109 DetectedCase::UpperSnakeCase => { 66 return Some(ident.to_lowercase());
110 return Some(ident.chars().map(|chr| chr.to_ascii_lowercase()).collect())
111 }
112 _ => {}
113 } 67 }
114 68
115 // Otherwise, assume that it's CamelCase. 69 Some(stdx::to_lower_snake_case(ident))
116 let lower_snake_case = stdx::to_lower_snake_case(ident);
117
118 if lower_snake_case == ident {
119 // While we didn't detect the correct case at the beginning, there
120 // may be special cases: e.g. `a` is both valid camelCase and snake_case.
121 None
122 } else {
123 Some(lower_snake_case)
124 }
125} 70}
126 71
127/// Converts an identifier to an UPPER_SNAKE_CASE form. 72/// Converts an identifier to an UPPER_SNAKE_CASE form.
128/// Returns `None` if the string is already is UPPER_SNAKE_CASE. 73/// Returns `None` if the string is already is UPPER_SNAKE_CASE.
129pub fn to_upper_snake_case(ident: &str) -> Option<String> { 74pub fn to_upper_snake_case(ident: &str) -> Option<String> {
130 match detect_case(ident) { 75 if is_upper_snake_case(ident) {
131 DetectedCase::UpperSnakeCase => return None, 76 return None;
132 DetectedCase::LowerSnakeCase => { 77 } else if is_lower_snake_case(ident) {
133 return Some(ident.chars().map(|chr| chr.to_ascii_uppercase()).collect()) 78 return Some(ident.to_uppercase());
134 }
135 _ => {}
136 } 79 }
137 80
138 // Normalize the string from whatever form it's in currently, and then just make it uppercase. 81 Some(stdx::to_upper_snake_case(ident))
139 let upper_snake_case = stdx::to_upper_snake_case(ident); 82}
140 83
141 if upper_snake_case == ident { 84// Taken from rustc.
142 // While we didn't detect the correct case at the beginning, there 85// Modified by replacing the use of unstable feature `array_windows`.
143 // may be special cases: e.g. `A` is both valid CamelCase and UPPER_SNAKE_CASE. 86fn is_camel_case(name: &str) -> bool {
144 None 87 let name = name.trim_matches('_');
145 } else { 88 if name.is_empty() {
146 Some(upper_snake_case) 89 return true;
147 } 90 }
91
92 let mut fst = None;
93 // start with a non-lowercase letter rather than non-uppercase
94 // ones (some scripts don't have a concept of upper/lowercase)
95 !name.chars().next().unwrap().is_lowercase()
96 && !name.contains("__")
97 && !name.chars().any(|snd| {
98 let ret = match (fst, snd) {
99 (None, _) => false,
100 (Some(fst), snd) => {
101 char_has_case(fst) && snd == '_' || char_has_case(snd) && fst == '_'
102 }
103 };
104 fst = Some(snd);
105
106 ret
107 })
108}
109
110fn is_lower_snake_case(ident: &str) -> bool {
111 is_snake_case(ident, char::is_uppercase)
112}
113
114fn is_upper_snake_case(ident: &str) -> bool {
115 is_snake_case(ident, char::is_lowercase)
116}
117
118// Taken from rustc.
119// Modified to allow checking for both upper and lower snake case.
120fn is_snake_case<F: Fn(char) -> bool>(ident: &str, wrong_case: F) -> bool {
121 if ident.is_empty() {
122 return true;
123 }
124 let ident = ident.trim_matches('_');
125
126 let mut allow_underscore = true;
127 ident.chars().all(|c| {
128 allow_underscore = match c {
129 '_' if !allow_underscore => return false,
130 '_' => false,
131 // It would be more obvious to check for the correct case,
132 // but some characters do not have a case.
133 c if !wrong_case(c) => true,
134 _ => return false,
135 };
136 true
137 })
138}
139
140// Taken from rustc.
141fn char_has_case(c: char) -> bool {
142 c.is_lowercase() || c.is_uppercase()
148} 143}
149 144
150#[cfg(test)] 145#[cfg(test)]
@@ -167,6 +162,7 @@ mod tests {
167 check(to_lower_snake_case, "CamelCase", expect![["camel_case"]]); 162 check(to_lower_snake_case, "CamelCase", expect![["camel_case"]]);
168 check(to_lower_snake_case, "lowerCamelCase", expect![["lower_camel_case"]]); 163 check(to_lower_snake_case, "lowerCamelCase", expect![["lower_camel_case"]]);
169 check(to_lower_snake_case, "a", expect![[""]]); 164 check(to_lower_snake_case, "a", expect![[""]]);
165 check(to_lower_snake_case, "abc", expect![[""]]);
170 } 166 }
171 167
172 #[test] 168 #[test]
@@ -180,6 +176,12 @@ mod tests {
180 check(to_camel_case, "Weird_Case", expect![["WeirdCase"]]); 176 check(to_camel_case, "Weird_Case", expect![["WeirdCase"]]);
181 check(to_camel_case, "name", expect![["Name"]]); 177 check(to_camel_case, "name", expect![["Name"]]);
182 check(to_camel_case, "A", expect![[""]]); 178 check(to_camel_case, "A", expect![[""]]);
179 check(to_camel_case, "AABB", expect![[""]]);
180 // Taken from rustc: /compiler/rustc_lint/src/nonstandard_style/tests.rs
181 check(to_camel_case, "X86_64", expect![[""]]);
182 check(to_camel_case, "x86__64", expect![["X86_64"]]);
183 check(to_camel_case, "Abc_123", expect![["Abc123"]]);
184 check(to_camel_case, "A1_b2_c3", expect![["A1B2C3"]]);
183 } 185 }
184 186
185 #[test] 187 #[test]
@@ -190,5 +192,7 @@ mod tests {
190 check(to_upper_snake_case, "CamelCase", expect![["CAMEL_CASE"]]); 192 check(to_upper_snake_case, "CamelCase", expect![["CAMEL_CASE"]]);
191 check(to_upper_snake_case, "lowerCamelCase", expect![["LOWER_CAMEL_CASE"]]); 193 check(to_upper_snake_case, "lowerCamelCase", expect![["LOWER_CAMEL_CASE"]]);
192 check(to_upper_snake_case, "A", expect![[""]]); 194 check(to_upper_snake_case, "A", expect![[""]]);
195 check(to_upper_snake_case, "ABC", expect![[""]]);
196 check(to_upper_snake_case, "X86_64", expect![[""]]);
193 } 197 }
194} 198}
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs
index 21a121aad..2da9688ca 100644
--- a/crates/hir_ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs
@@ -202,4 +202,22 @@ fn main() {
202"#, 202"#,
203 ); 203 );
204 } 204 }
205
206 #[test]
207 fn no_missing_unsafe_diagnostic_with_safe_intrinsic() {
208 check_diagnostics(
209 r#"
210extern "rust-intrinsic" {
211 pub fn bitreverse(x: u32) -> u32; // Safe intrinsic
212 pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic
213}
214
215fn main() {
216 let _ = bitreverse(12);
217 let _ = floorf32(12.0);
218 //^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
219}
220"#,
221 );
222 }
205} 223}