diff options
Diffstat (limited to 'crates/hir_ty/src/diagnostics')
-rw-r--r-- | crates/hir_ty/src/diagnostics/decl_check.rs | 28 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/decl_check/case_conv.rs | 244 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/unsafe_check.rs | 18 |
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#" |
714 | struct non_camel_case_name {} | 714 | struct 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 | |||
717 | struct 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#" | ||
727 | struct 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#" |
734 | enum some_enum { Val(u8) } | 746 | enum 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 | |||
749 | enum 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#" | ||
759 | enum 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, |
5 | enum DetectedCase { | 5 | // from file /compiler/rustc_lint/src/nonstandard_style.rs |
6 | LowerCamelCase, | ||
7 | UpperCamelCase, | ||
8 | LowerSnakeCase, | ||
9 | UpperSnakeCase, | ||
10 | Unknown, | ||
11 | } | ||
12 | |||
13 | fn 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. |
51 | pub fn to_camel_case(ident: &str) -> Option<String> { | 9 | pub 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. |
105 | pub fn to_lower_snake_case(ident: &str) -> Option<String> { | 62 | pub 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. |
129 | pub fn to_upper_snake_case(ident: &str) -> Option<String> { | 74 | pub 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. | 86 | fn 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 | |||
110 | fn is_lower_snake_case(ident: &str) -> bool { | ||
111 | is_snake_case(ident, char::is_uppercase) | ||
112 | } | ||
113 | |||
114 | fn 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. | ||
120 | fn 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. | ||
141 | fn 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#" | ||
210 | extern "rust-intrinsic" { | ||
211 | pub fn bitreverse(x: u32) -> u32; // Safe intrinsic | ||
212 | pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic | ||
213 | } | ||
214 | |||
215 | fn 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 | } |