diff options
Diffstat (limited to 'crates/hir_ty/src')
-rw-r--r-- | crates/hir_ty/src/diagnostics.rs | 55 | ||||
-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 | ||||
-rw-r--r-- | crates/hir_ty/src/traits.rs | 30 | ||||
-rw-r--r-- | crates/hir_ty/src/traits/chalk/mapping.rs | 17 |
6 files changed, 255 insertions, 137 deletions
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index dfe98571e..b58fe0ed7 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs | |||
@@ -36,6 +36,9 @@ pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut Diag | |||
36 | validator.validate_body(db); | 36 | validator.validate_body(db); |
37 | } | 37 | } |
38 | 38 | ||
39 | // Diagnostic: no-such-field | ||
40 | // | ||
41 | // This diagnostic is triggered if created structure does not have field provided in record. | ||
39 | #[derive(Debug)] | 42 | #[derive(Debug)] |
40 | pub struct NoSuchField { | 43 | pub struct NoSuchField { |
41 | pub file: HirFileId, | 44 | pub file: HirFileId, |
@@ -60,6 +63,17 @@ impl Diagnostic for NoSuchField { | |||
60 | } | 63 | } |
61 | } | 64 | } |
62 | 65 | ||
66 | // Diagnostic: missing-structure-fields | ||
67 | // | ||
68 | // This diagnostic is triggered if record lacks some fields that exist in the corresponding structure. | ||
69 | // | ||
70 | // Example: | ||
71 | // | ||
72 | // ```rust | ||
73 | // struct A { a: u8, b: u8 } | ||
74 | // | ||
75 | // let a = A { a: 10 }; | ||
76 | // ``` | ||
63 | #[derive(Debug)] | 77 | #[derive(Debug)] |
64 | pub struct MissingFields { | 78 | pub struct MissingFields { |
65 | pub file: HirFileId, | 79 | pub file: HirFileId, |
@@ -96,6 +110,21 @@ impl Diagnostic for MissingFields { | |||
96 | } | 110 | } |
97 | } | 111 | } |
98 | 112 | ||
113 | // Diagnostic: missing-pat-fields | ||
114 | // | ||
115 | // This diagnostic is triggered if pattern lacks some fields that exist in the corresponding structure. | ||
116 | // | ||
117 | // Example: | ||
118 | // | ||
119 | // ```rust | ||
120 | // struct A { a: u8, b: u8 } | ||
121 | // | ||
122 | // let a = A { a: 10, b: 20 }; | ||
123 | // | ||
124 | // if let A { a } = a { | ||
125 | // // ... | ||
126 | // } | ||
127 | // ``` | ||
99 | #[derive(Debug)] | 128 | #[derive(Debug)] |
100 | pub struct MissingPatFields { | 129 | pub struct MissingPatFields { |
101 | pub file: HirFileId, | 130 | pub file: HirFileId, |
@@ -130,6 +159,9 @@ impl Diagnostic for MissingPatFields { | |||
130 | } | 159 | } |
131 | } | 160 | } |
132 | 161 | ||
162 | // Diagnostic: missing-match-arm | ||
163 | // | ||
164 | // This diagnostic is triggered if `match` block is missing one or more match arms. | ||
133 | #[derive(Debug)] | 165 | #[derive(Debug)] |
134 | pub struct MissingMatchArms { | 166 | pub struct MissingMatchArms { |
135 | pub file: HirFileId, | 167 | pub file: HirFileId, |
@@ -152,6 +184,17 @@ impl Diagnostic for MissingMatchArms { | |||
152 | } | 184 | } |
153 | } | 185 | } |
154 | 186 | ||
187 | // Diagnostic: missing-ok-in-tail-expr | ||
188 | // | ||
189 | // This diagnostic is triggered if block that should return `Result` returns a value not wrapped in `Ok`. | ||
190 | // | ||
191 | // Example: | ||
192 | // | ||
193 | // ```rust | ||
194 | // fn foo() -> Result<u8, ()> { | ||
195 | // 10 | ||
196 | // } | ||
197 | // ``` | ||
155 | #[derive(Debug)] | 198 | #[derive(Debug)] |
156 | pub struct MissingOkInTailExpr { | 199 | pub struct MissingOkInTailExpr { |
157 | pub file: HirFileId, | 200 | pub file: HirFileId, |
@@ -173,6 +216,9 @@ impl Diagnostic for MissingOkInTailExpr { | |||
173 | } | 216 | } |
174 | } | 217 | } |
175 | 218 | ||
219 | // Diagnostic: break-outside-of-loop | ||
220 | // | ||
221 | // This diagnostic is triggered if `break` keyword is used outside of a loop. | ||
176 | #[derive(Debug)] | 222 | #[derive(Debug)] |
177 | pub struct BreakOutsideOfLoop { | 223 | pub struct BreakOutsideOfLoop { |
178 | pub file: HirFileId, | 224 | pub file: HirFileId, |
@@ -194,6 +240,9 @@ impl Diagnostic for BreakOutsideOfLoop { | |||
194 | } | 240 | } |
195 | } | 241 | } |
196 | 242 | ||
243 | // Diagnostic: missing-unsafe | ||
244 | // | ||
245 | // This diagnostic is triggered if operation marked as `unsafe` is used outside of `unsafe` function or block. | ||
197 | #[derive(Debug)] | 246 | #[derive(Debug)] |
198 | pub struct MissingUnsafe { | 247 | pub struct MissingUnsafe { |
199 | pub file: HirFileId, | 248 | pub file: HirFileId, |
@@ -215,6 +264,9 @@ impl Diagnostic for MissingUnsafe { | |||
215 | } | 264 | } |
216 | } | 265 | } |
217 | 266 | ||
267 | // Diagnostic: mismatched-arg-count | ||
268 | // | ||
269 | // This diagnostic is triggered if function is invoked with an incorrect amount of arguments. | ||
218 | #[derive(Debug)] | 270 | #[derive(Debug)] |
219 | pub struct MismatchedArgCount { | 271 | pub struct MismatchedArgCount { |
220 | pub file: HirFileId, | 272 | pub file: HirFileId, |
@@ -264,6 +316,9 @@ impl fmt::Display for CaseType { | |||
264 | } | 316 | } |
265 | } | 317 | } |
266 | 318 | ||
319 | // Diagnostic: incorrect-ident-case | ||
320 | // | ||
321 | // This diagnostic is triggered if item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. | ||
267 | #[derive(Debug)] | 322 | #[derive(Debug)] |
268 | pub struct IncorrectCase { | 323 | pub struct IncorrectCase { |
269 | pub file: HirFileId, | 324 | pub file: HirFileId, |
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 | } |
diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs index 14cd3a2b4..ce1174cbe 100644 --- a/crates/hir_ty/src/traits.rs +++ b/crates/hir_ty/src/traits.rs | |||
@@ -5,6 +5,7 @@ use base_db::CrateId; | |||
5 | use chalk_ir::cast::Cast; | 5 | use chalk_ir::cast::Cast; |
6 | use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver}; | 6 | use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver}; |
7 | use hir_def::{lang_item::LangItemTarget, TraitId}; | 7 | use hir_def::{lang_item::LangItemTarget, TraitId}; |
8 | use stdx::panic_context; | ||
8 | 9 | ||
9 | use crate::{db::HirDatabase, DebruijnIndex, Substs}; | 10 | use crate::{db::HirDatabase, DebruijnIndex, Substs}; |
10 | 11 | ||
@@ -168,14 +169,23 @@ fn solve( | |||
168 | }; | 169 | }; |
169 | 170 | ||
170 | let mut solve = || { | 171 | let mut solve = || { |
171 | if is_chalk_print() { | 172 | let _ctx = if is_chalk_debug() || is_chalk_print() { |
172 | let logging_db = LoggingRustIrDatabase::new(context); | 173 | Some(panic_context::enter(format!("solving {:?}", goal))) |
173 | let solution = solver.solve_limited(&logging_db, goal, &should_continue); | 174 | } else { |
174 | log::debug!("chalk program:\n{}", logging_db); | 175 | None |
176 | }; | ||
177 | let solution = if is_chalk_print() { | ||
178 | let logging_db = | ||
179 | LoggingRustIrDatabaseLoggingOnDrop(LoggingRustIrDatabase::new(context)); | ||
180 | let solution = solver.solve_limited(&logging_db.0, goal, &should_continue); | ||
175 | solution | 181 | solution |
176 | } else { | 182 | } else { |
177 | solver.solve_limited(&context, goal, &should_continue) | 183 | solver.solve_limited(&context, goal, &should_continue) |
178 | } | 184 | }; |
185 | |||
186 | log::debug!("solve({:?}) => {:?}", goal, solution); | ||
187 | |||
188 | solution | ||
179 | }; | 189 | }; |
180 | 190 | ||
181 | // don't set the TLS for Chalk unless Chalk debugging is active, to make | 191 | // don't set the TLS for Chalk unless Chalk debugging is active, to make |
@@ -183,11 +193,17 @@ fn solve( | |||
183 | let solution = | 193 | let solution = |
184 | if is_chalk_debug() { chalk::tls::set_current_program(db, solve) } else { solve() }; | 194 | if is_chalk_debug() { chalk::tls::set_current_program(db, solve) } else { solve() }; |
185 | 195 | ||
186 | log::debug!("solve({:?}) => {:?}", goal, solution); | ||
187 | |||
188 | solution | 196 | solution |
189 | } | 197 | } |
190 | 198 | ||
199 | struct LoggingRustIrDatabaseLoggingOnDrop<'a>(LoggingRustIrDatabase<Interner, ChalkContext<'a>>); | ||
200 | |||
201 | impl<'a> Drop for LoggingRustIrDatabaseLoggingOnDrop<'a> { | ||
202 | fn drop(&mut self) { | ||
203 | eprintln!("chalk program:\n{}", self.0); | ||
204 | } | ||
205 | } | ||
206 | |||
191 | fn is_chalk_debug() -> bool { | 207 | fn is_chalk_debug() -> bool { |
192 | std::env::var("CHALK_DEBUG").is_ok() | 208 | std::env::var("CHALK_DEBUG").is_ok() |
193 | } | 209 | } |
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs index be3301313..dd7affcec 100644 --- a/crates/hir_ty/src/traits/chalk/mapping.rs +++ b/crates/hir_ty/src/traits/chalk/mapping.rs | |||
@@ -4,8 +4,8 @@ | |||
4 | //! conversions. | 4 | //! conversions. |
5 | 5 | ||
6 | use chalk_ir::{ | 6 | use chalk_ir::{ |
7 | cast::Cast, fold::shift::Shift, interner::HasInterner, PlaceholderIndex, Scalar, TypeName, | 7 | cast::Cast, fold::shift::Shift, interner::HasInterner, LifetimeData, PlaceholderIndex, Scalar, |
8 | UniverseIndex, | 8 | TypeName, UniverseIndex, |
9 | }; | 9 | }; |
10 | use chalk_solve::rust_ir; | 10 | use chalk_solve::rust_ir; |
11 | 11 | ||
@@ -76,7 +76,7 @@ impl ToChalk for Ty { | |||
76 | ); | 76 | ); |
77 | let bounded_ty = chalk_ir::DynTy { | 77 | let bounded_ty = chalk_ir::DynTy { |
78 | bounds: make_binders(where_clauses, 1), | 78 | bounds: make_binders(where_clauses, 1), |
79 | lifetime: FAKE_PLACEHOLDER.to_lifetime(&Interner), | 79 | lifetime: LifetimeData::Static.intern(&Interner), |
80 | }; | 80 | }; |
81 | chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner) | 81 | chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner) |
82 | } | 82 | } |
@@ -161,9 +161,6 @@ impl ToChalk for Ty { | |||
161 | } | 161 | } |
162 | } | 162 | } |
163 | 163 | ||
164 | const FAKE_PLACEHOLDER: PlaceholderIndex = | ||
165 | PlaceholderIndex { ui: UniverseIndex::ROOT, idx: usize::MAX }; | ||
166 | |||
167 | /// We currently don't model lifetimes, but Chalk does. So, we have to insert a | 164 | /// We currently don't model lifetimes, but Chalk does. So, we have to insert a |
168 | /// fake lifetime here, because Chalks built-in logic may expect it to be there. | 165 | /// fake lifetime here, because Chalks built-in logic may expect it to be there. |
169 | fn ref_to_chalk( | 166 | fn ref_to_chalk( |
@@ -172,7 +169,7 @@ fn ref_to_chalk( | |||
172 | subst: Substs, | 169 | subst: Substs, |
173 | ) -> chalk_ir::Ty<Interner> { | 170 | ) -> chalk_ir::Ty<Interner> { |
174 | let arg = subst[0].clone().to_chalk(db); | 171 | let arg = subst[0].clone().to_chalk(db); |
175 | let lifetime = FAKE_PLACEHOLDER.to_lifetime(&Interner); | 172 | let lifetime = LifetimeData::Static.intern(&Interner); |
176 | chalk_ir::ApplicationTy { | 173 | chalk_ir::ApplicationTy { |
177 | name: TypeName::Ref(mutability.to_chalk(db)), | 174 | name: TypeName::Ref(mutability.to_chalk(db)), |
178 | substitution: chalk_ir::Substitution::from_iter( | 175 | substitution: chalk_ir::Substitution::from_iter( |
@@ -205,7 +202,11 @@ fn array_to_chalk(db: &dyn HirDatabase, subst: Substs) -> chalk_ir::Ty<Interner> | |||
205 | substitution: chalk_ir::Substitution::empty(&Interner), | 202 | substitution: chalk_ir::Substitution::empty(&Interner), |
206 | } | 203 | } |
207 | .intern(&Interner); | 204 | .intern(&Interner); |
208 | let const_ = FAKE_PLACEHOLDER.to_const(&Interner, usize_ty); | 205 | let const_ = chalk_ir::ConstData { |
206 | ty: usize_ty, | ||
207 | value: chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: () }), | ||
208 | } | ||
209 | .intern(&Interner); | ||
209 | chalk_ir::ApplicationTy { | 210 | chalk_ir::ApplicationTy { |
210 | name: TypeName::Array, | 211 | name: TypeName::Array, |
211 | substitution: chalk_ir::Substitution::from_iter( | 212 | substitution: chalk_ir::Substitution::from_iter( |