diff options
Diffstat (limited to 'crates/ra_hir_ty')
-rw-r--r-- | crates/ra_hir_ty/Cargo.toml | 6 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics.rs | 128 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics/expr.rs | 14 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics/match_check.rs | 8 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics/unsafe_check.rs | 38 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer.rs | 11 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/lower.rs | 5 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/simple.rs | 38 |
8 files changed, 141 insertions, 107 deletions
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index 623ce261a..83397d579 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml | |||
@@ -28,9 +28,9 @@ test_utils = { path = "../test_utils" } | |||
28 | 28 | ||
29 | scoped-tls = "1" | 29 | scoped-tls = "1" |
30 | 30 | ||
31 | chalk-solve = { version = "0.19.0" } | 31 | chalk-solve = { version = "0.21.0" } |
32 | chalk-ir = { version = "0.19.0" } | 32 | chalk-ir = { version = "0.21.0" } |
33 | chalk-recursive = { version = "0.19.0" } | 33 | chalk-recursive = { version = "0.21.0" } |
34 | 34 | ||
35 | [dev-dependencies] | 35 | [dev-dependencies] |
36 | expect = { path = "../expect" } | 36 | expect = { path = "../expect" } |
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index 56acd3bbf..45e31033e 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs | |||
@@ -6,10 +6,10 @@ mod unsafe_check; | |||
6 | use std::any::Any; | 6 | use std::any::Any; |
7 | 7 | ||
8 | use hir_def::DefWithBodyId; | 8 | use hir_def::DefWithBodyId; |
9 | use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; | 9 | use hir_expand::diagnostics::{Diagnostic, DiagnosticSink}; |
10 | use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; | 10 | use hir_expand::{name::Name, HirFileId, InFile}; |
11 | use ra_prof::profile; | 11 | use ra_prof::profile; |
12 | use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; | 12 | use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; |
13 | use stdx::format_to; | 13 | use stdx::format_to; |
14 | 14 | ||
15 | use crate::db::HirDatabase; | 15 | use crate::db::HirDatabase; |
@@ -41,7 +41,7 @@ impl Diagnostic for NoSuchField { | |||
41 | "no such field".to_string() | 41 | "no such field".to_string() |
42 | } | 42 | } |
43 | 43 | ||
44 | fn source(&self) -> InFile<SyntaxNodePtr> { | 44 | fn display_source(&self) -> InFile<SyntaxNodePtr> { |
45 | InFile::new(self.file, self.field.clone().into()) | 45 | InFile::new(self.file, self.field.clone().into()) |
46 | } | 46 | } |
47 | 47 | ||
@@ -50,20 +50,11 @@ impl Diagnostic for NoSuchField { | |||
50 | } | 50 | } |
51 | } | 51 | } |
52 | 52 | ||
53 | impl AstDiagnostic for NoSuchField { | ||
54 | type AST = ast::RecordExprField; | ||
55 | |||
56 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { | ||
57 | let root = db.parse_or_expand(self.source().file_id).unwrap(); | ||
58 | let node = self.source().value.to_node(&root); | ||
59 | ast::RecordExprField::cast(node).unwrap() | ||
60 | } | ||
61 | } | ||
62 | |||
63 | #[derive(Debug)] | 53 | #[derive(Debug)] |
64 | pub struct MissingFields { | 54 | pub struct MissingFields { |
65 | pub file: HirFileId, | 55 | pub file: HirFileId, |
66 | pub field_list: AstPtr<ast::RecordExprFieldList>, | 56 | pub field_list_parent: AstPtr<ast::RecordExpr>, |
57 | pub field_list_parent_path: Option<AstPtr<ast::Path>>, | ||
67 | pub missed_fields: Vec<Name>, | 58 | pub missed_fields: Vec<Name>, |
68 | } | 59 | } |
69 | 60 | ||
@@ -78,28 +69,28 @@ impl Diagnostic for MissingFields { | |||
78 | } | 69 | } |
79 | buf | 70 | buf |
80 | } | 71 | } |
81 | fn source(&self) -> InFile<SyntaxNodePtr> { | 72 | |
82 | InFile { file_id: self.file, value: self.field_list.clone().into() } | 73 | fn display_source(&self) -> InFile<SyntaxNodePtr> { |
74 | InFile { | ||
75 | file_id: self.file, | ||
76 | value: self | ||
77 | .field_list_parent_path | ||
78 | .clone() | ||
79 | .map(SyntaxNodePtr::from) | ||
80 | .unwrap_or_else(|| self.field_list_parent.clone().into()), | ||
81 | } | ||
83 | } | 82 | } |
83 | |||
84 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 84 | fn as_any(&self) -> &(dyn Any + Send + 'static) { |
85 | self | 85 | self |
86 | } | 86 | } |
87 | } | 87 | } |
88 | 88 | ||
89 | impl AstDiagnostic for MissingFields { | ||
90 | type AST = ast::RecordExprFieldList; | ||
91 | |||
92 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { | ||
93 | let root = db.parse_or_expand(self.source().file_id).unwrap(); | ||
94 | let node = self.source().value.to_node(&root); | ||
95 | ast::RecordExprFieldList::cast(node).unwrap() | ||
96 | } | ||
97 | } | ||
98 | |||
99 | #[derive(Debug)] | 89 | #[derive(Debug)] |
100 | pub struct MissingPatFields { | 90 | pub struct MissingPatFields { |
101 | pub file: HirFileId, | 91 | pub file: HirFileId, |
102 | pub field_list: AstPtr<ast::RecordPatFieldList>, | 92 | pub field_list_parent: AstPtr<ast::RecordPat>, |
93 | pub field_list_parent_path: Option<AstPtr<ast::Path>>, | ||
103 | pub missed_fields: Vec<Name>, | 94 | pub missed_fields: Vec<Name>, |
104 | } | 95 | } |
105 | 96 | ||
@@ -114,8 +105,15 @@ impl Diagnostic for MissingPatFields { | |||
114 | } | 105 | } |
115 | buf | 106 | buf |
116 | } | 107 | } |
117 | fn source(&self) -> InFile<SyntaxNodePtr> { | 108 | fn display_source(&self) -> InFile<SyntaxNodePtr> { |
118 | InFile { file_id: self.file, value: self.field_list.clone().into() } | 109 | InFile { |
110 | file_id: self.file, | ||
111 | value: self | ||
112 | .field_list_parent_path | ||
113 | .clone() | ||
114 | .map(SyntaxNodePtr::from) | ||
115 | .unwrap_or_else(|| self.field_list_parent.clone().into()), | ||
116 | } | ||
119 | } | 117 | } |
120 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 118 | fn as_any(&self) -> &(dyn Any + Send + 'static) { |
121 | self | 119 | self |
@@ -136,7 +134,7 @@ impl Diagnostic for MissingMatchArms { | |||
136 | fn message(&self) -> String { | 134 | fn message(&self) -> String { |
137 | String::from("Missing match arm") | 135 | String::from("Missing match arm") |
138 | } | 136 | } |
139 | fn source(&self) -> InFile<SyntaxNodePtr> { | 137 | fn display_source(&self) -> InFile<SyntaxNodePtr> { |
140 | InFile { file_id: self.file, value: self.match_expr.clone().into() } | 138 | InFile { file_id: self.file, value: self.match_expr.clone().into() } |
141 | } | 139 | } |
142 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 140 | fn as_any(&self) -> &(dyn Any + Send + 'static) { |
@@ -157,7 +155,7 @@ impl Diagnostic for MissingOkInTailExpr { | |||
157 | fn message(&self) -> String { | 155 | fn message(&self) -> String { |
158 | "wrap return expression in Ok".to_string() | 156 | "wrap return expression in Ok".to_string() |
159 | } | 157 | } |
160 | fn source(&self) -> InFile<SyntaxNodePtr> { | 158 | fn display_source(&self) -> InFile<SyntaxNodePtr> { |
161 | InFile { file_id: self.file, value: self.expr.clone().into() } | 159 | InFile { file_id: self.file, value: self.expr.clone().into() } |
162 | } | 160 | } |
163 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 161 | fn as_any(&self) -> &(dyn Any + Send + 'static) { |
@@ -165,16 +163,6 @@ impl Diagnostic for MissingOkInTailExpr { | |||
165 | } | 163 | } |
166 | } | 164 | } |
167 | 165 | ||
168 | impl AstDiagnostic for MissingOkInTailExpr { | ||
169 | type AST = ast::Expr; | ||
170 | |||
171 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { | ||
172 | let root = db.parse_or_expand(self.file).unwrap(); | ||
173 | let node = self.source().value.to_node(&root); | ||
174 | ast::Expr::cast(node).unwrap() | ||
175 | } | ||
176 | } | ||
177 | |||
178 | #[derive(Debug)] | 166 | #[derive(Debug)] |
179 | pub struct BreakOutsideOfLoop { | 167 | pub struct BreakOutsideOfLoop { |
180 | pub file: HirFileId, | 168 | pub file: HirFileId, |
@@ -188,7 +176,7 @@ impl Diagnostic for BreakOutsideOfLoop { | |||
188 | fn message(&self) -> String { | 176 | fn message(&self) -> String { |
189 | "break outside of loop".to_string() | 177 | "break outside of loop".to_string() |
190 | } | 178 | } |
191 | fn source(&self) -> InFile<SyntaxNodePtr> { | 179 | fn display_source(&self) -> InFile<SyntaxNodePtr> { |
192 | InFile { file_id: self.file, value: self.expr.clone().into() } | 180 | InFile { file_id: self.file, value: self.expr.clone().into() } |
193 | } | 181 | } |
194 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 182 | fn as_any(&self) -> &(dyn Any + Send + 'static) { |
@@ -196,16 +184,6 @@ impl Diagnostic for BreakOutsideOfLoop { | |||
196 | } | 184 | } |
197 | } | 185 | } |
198 | 186 | ||
199 | impl AstDiagnostic for BreakOutsideOfLoop { | ||
200 | type AST = ast::Expr; | ||
201 | |||
202 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { | ||
203 | let root = db.parse_or_expand(self.file).unwrap(); | ||
204 | let node = self.source().value.to_node(&root); | ||
205 | ast::Expr::cast(node).unwrap() | ||
206 | } | ||
207 | } | ||
208 | |||
209 | #[derive(Debug)] | 187 | #[derive(Debug)] |
210 | pub struct MissingUnsafe { | 188 | pub struct MissingUnsafe { |
211 | pub file: HirFileId, | 189 | pub file: HirFileId, |
@@ -219,7 +197,7 @@ impl Diagnostic for MissingUnsafe { | |||
219 | fn message(&self) -> String { | 197 | fn message(&self) -> String { |
220 | format!("This operation is unsafe and requires an unsafe function or block") | 198 | format!("This operation is unsafe and requires an unsafe function or block") |
221 | } | 199 | } |
222 | fn source(&self) -> InFile<SyntaxNodePtr> { | 200 | fn display_source(&self) -> InFile<SyntaxNodePtr> { |
223 | InFile { file_id: self.file, value: self.expr.clone().into() } | 201 | InFile { file_id: self.file, value: self.expr.clone().into() } |
224 | } | 202 | } |
225 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 203 | fn as_any(&self) -> &(dyn Any + Send + 'static) { |
@@ -227,16 +205,6 @@ impl Diagnostic for MissingUnsafe { | |||
227 | } | 205 | } |
228 | } | 206 | } |
229 | 207 | ||
230 | impl AstDiagnostic for MissingUnsafe { | ||
231 | type AST = ast::Expr; | ||
232 | |||
233 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { | ||
234 | let root = db.parse_or_expand(self.source().file_id).unwrap(); | ||
235 | let node = self.source().value.to_node(&root); | ||
236 | ast::Expr::cast(node).unwrap() | ||
237 | } | ||
238 | } | ||
239 | |||
240 | #[derive(Debug)] | 208 | #[derive(Debug)] |
241 | pub struct MismatchedArgCount { | 209 | pub struct MismatchedArgCount { |
242 | pub file: HirFileId, | 210 | pub file: HirFileId, |
@@ -253,7 +221,7 @@ impl Diagnostic for MismatchedArgCount { | |||
253 | let s = if self.expected == 1 { "" } else { "s" }; | 221 | let s = if self.expected == 1 { "" } else { "s" }; |
254 | format!("Expected {} argument{}, found {}", self.expected, s, self.found) | 222 | format!("Expected {} argument{}, found {}", self.expected, s, self.found) |
255 | } | 223 | } |
256 | fn source(&self) -> InFile<SyntaxNodePtr> { | 224 | fn display_source(&self) -> InFile<SyntaxNodePtr> { |
257 | InFile { file_id: self.file, value: self.call_expr.clone().into() } | 225 | InFile { file_id: self.file, value: self.call_expr.clone().into() } |
258 | } | 226 | } |
259 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 227 | fn as_any(&self) -> &(dyn Any + Send + 'static) { |
@@ -264,19 +232,13 @@ impl Diagnostic for MismatchedArgCount { | |||
264 | } | 232 | } |
265 | } | 233 | } |
266 | 234 | ||
267 | impl AstDiagnostic for MismatchedArgCount { | ||
268 | type AST = ast::CallExpr; | ||
269 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { | ||
270 | let root = db.parse_or_expand(self.source().file_id).unwrap(); | ||
271 | let node = self.source().value.to_node(&root); | ||
272 | ast::CallExpr::cast(node).unwrap() | ||
273 | } | ||
274 | } | ||
275 | |||
276 | #[cfg(test)] | 235 | #[cfg(test)] |
277 | mod tests { | 236 | mod tests { |
278 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; | 237 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; |
279 | use hir_expand::diagnostics::{Diagnostic, DiagnosticSinkBuilder}; | 238 | use hir_expand::{ |
239 | db::AstDatabase, | ||
240 | diagnostics::{Diagnostic, DiagnosticSinkBuilder}, | ||
241 | }; | ||
280 | use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; | 242 | use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; |
281 | use ra_syntax::{TextRange, TextSize}; | 243 | use ra_syntax::{TextRange, TextSize}; |
282 | use rustc_hash::FxHashMap; | 244 | use rustc_hash::FxHashMap; |
@@ -321,9 +283,11 @@ mod tests { | |||
321 | 283 | ||
322 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); | 284 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); |
323 | db.diagnostics(|d| { | 285 | db.diagnostics(|d| { |
324 | // FXIME: macros... | 286 | let src = d.display_source(); |
325 | let file_id = d.source().file_id.original_file(&db); | 287 | let root = db.parse_or_expand(src.file_id).unwrap(); |
326 | let range = d.syntax_node(&db).text_range(); | 288 | // FIXME: macros... |
289 | let file_id = src.file_id.original_file(&db); | ||
290 | let range = src.value.to_node(&root).text_range(); | ||
327 | let message = d.message().to_owned(); | 291 | let message = d.message().to_owned(); |
328 | actual.entry(file_id).or_default().push((range, message)); | 292 | actual.entry(file_id).or_default().push((range, message)); |
329 | }); | 293 | }); |
@@ -351,8 +315,8 @@ struct S { foo: i32, bar: () } | |||
351 | impl S { | 315 | impl S { |
352 | fn new() -> S { | 316 | fn new() -> S { |
353 | S { | 317 | S { |
354 | //^... Missing structure fields: | 318 | //^ Missing structure fields: |
355 | //| - bar | 319 | //| - bar |
356 | foo: 92, | 320 | foo: 92, |
357 | baz: 62, | 321 | baz: 62, |
358 | //^^^^^^^ no such field | 322 | //^^^^^^^ no such field |
@@ -473,8 +437,8 @@ impl Foo { | |||
473 | struct S { foo: i32, bar: () } | 437 | struct S { foo: i32, bar: () } |
474 | fn baz(s: S) { | 438 | fn baz(s: S) { |
475 | let S { foo: _ } = s; | 439 | let S { foo: _ } = s; |
476 | //^^^^^^^^^^ Missing structure fields: | 440 | //^ Missing structure fields: |
477 | // | - bar | 441 | //| - bar |
478 | } | 442 | } |
479 | "#, | 443 | "#, |
480 | ); | 444 | ); |
diff --git a/crates/ra_hir_ty/src/diagnostics/expr.rs b/crates/ra_hir_ty/src/diagnostics/expr.rs index 95bbf2d95..51adcecaf 100644 --- a/crates/ra_hir_ty/src/diagnostics/expr.rs +++ b/crates/ra_hir_ty/src/diagnostics/expr.rs | |||
@@ -100,8 +100,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
100 | 100 | ||
101 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | 101 | if let Ok(source_ptr) = source_map.expr_syntax(id) { |
102 | let root = source_ptr.file_syntax(db.upcast()); | 102 | let root = source_ptr.file_syntax(db.upcast()); |
103 | if let ast::Expr::RecordExpr(record_lit) = &source_ptr.value.to_node(&root) { | 103 | if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) { |
104 | if let Some(field_list) = record_lit.record_expr_field_list() { | 104 | if let Some(_) = record_expr.record_expr_field_list() { |
105 | let variant_data = variant_data(db.upcast(), variant_def); | 105 | let variant_data = variant_data(db.upcast(), variant_def); |
106 | let missed_fields = missed_fields | 106 | let missed_fields = missed_fields |
107 | .into_iter() | 107 | .into_iter() |
@@ -109,7 +109,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
109 | .collect(); | 109 | .collect(); |
110 | self.sink.push(MissingFields { | 110 | self.sink.push(MissingFields { |
111 | file: source_ptr.file_id, | 111 | file: source_ptr.file_id, |
112 | field_list: AstPtr::new(&field_list), | 112 | field_list_parent: AstPtr::new(&record_expr), |
113 | field_list_parent_path: record_expr.path().map(|path| AstPtr::new(&path)), | ||
113 | missed_fields, | 114 | missed_fields, |
114 | }) | 115 | }) |
115 | } | 116 | } |
@@ -131,7 +132,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
131 | if let Some(expr) = source_ptr.value.as_ref().left() { | 132 | if let Some(expr) = source_ptr.value.as_ref().left() { |
132 | let root = source_ptr.file_syntax(db.upcast()); | 133 | let root = source_ptr.file_syntax(db.upcast()); |
133 | if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) { | 134 | if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) { |
134 | if let Some(field_list) = record_pat.record_pat_field_list() { | 135 | if let Some(_) = record_pat.record_pat_field_list() { |
135 | let variant_data = variant_data(db.upcast(), variant_def); | 136 | let variant_data = variant_data(db.upcast(), variant_def); |
136 | let missed_fields = missed_fields | 137 | let missed_fields = missed_fields |
137 | .into_iter() | 138 | .into_iter() |
@@ -139,7 +140,10 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
139 | .collect(); | 140 | .collect(); |
140 | self.sink.push(MissingPatFields { | 141 | self.sink.push(MissingPatFields { |
141 | file: source_ptr.file_id, | 142 | file: source_ptr.file_id, |
142 | field_list: AstPtr::new(&field_list), | 143 | field_list_parent: AstPtr::new(&record_pat), |
144 | field_list_parent_path: record_pat | ||
145 | .path() | ||
146 | .map(|path| AstPtr::new(&path)), | ||
143 | missed_fields, | 147 | missed_fields, |
144 | }) | 148 | }) |
145 | } | 149 | } |
diff --git a/crates/ra_hir_ty/src/diagnostics/match_check.rs b/crates/ra_hir_ty/src/diagnostics/match_check.rs index 507edcb7d..deca244db 100644 --- a/crates/ra_hir_ty/src/diagnostics/match_check.rs +++ b/crates/ra_hir_ty/src/diagnostics/match_check.rs | |||
@@ -1161,15 +1161,15 @@ fn main() { | |||
1161 | //^ Missing match arm | 1161 | //^ Missing match arm |
1162 | match a { | 1162 | match a { |
1163 | Either::A { } => (), | 1163 | Either::A { } => (), |
1164 | //^^^ Missing structure fields: | 1164 | //^^^^^^^^^ Missing structure fields: |
1165 | // | - foo | 1165 | // | - foo |
1166 | Either::B => (), | 1166 | Either::B => (), |
1167 | } | 1167 | } |
1168 | match a { | 1168 | match a { |
1169 | //^ Missing match arm | 1169 | //^ Missing match arm |
1170 | Either::A { } => (), | 1170 | Either::A { } => (), |
1171 | } //^^^ Missing structure fields: | 1171 | } //^^^^^^^^^ Missing structure fields: |
1172 | // | - foo | 1172 | // | - foo |
1173 | 1173 | ||
1174 | match a { | 1174 | match a { |
1175 | Either::A { foo: true } => (), | 1175 | Either::A { foo: true } => (), |
diff --git a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs index 5cc76bdce..61ffbf5d1 100644 --- a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs +++ b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs | |||
@@ -6,6 +6,7 @@ use std::sync::Arc; | |||
6 | use hir_def::{ | 6 | use hir_def::{ |
7 | body::Body, | 7 | body::Body, |
8 | expr::{Expr, ExprId, UnaryOp}, | 8 | expr::{Expr, ExprId, UnaryOp}, |
9 | resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, | ||
9 | DefWithBodyId, | 10 | DefWithBodyId, |
10 | }; | 11 | }; |
11 | use hir_expand::diagnostics::DiagnosticSink; | 12 | use hir_expand::diagnostics::DiagnosticSink; |
@@ -70,7 +71,7 @@ pub fn unsafe_expressions( | |||
70 | ) -> Vec<UnsafeExpr> { | 71 | ) -> Vec<UnsafeExpr> { |
71 | let mut unsafe_exprs = vec![]; | 72 | let mut unsafe_exprs = vec![]; |
72 | let body = db.body(def); | 73 | let body = db.body(def); |
73 | walk_unsafe(&mut unsafe_exprs, db, infer, &body, body.body_expr, false); | 74 | walk_unsafe(&mut unsafe_exprs, db, infer, def, &body, body.body_expr, false); |
74 | 75 | ||
75 | unsafe_exprs | 76 | unsafe_exprs |
76 | } | 77 | } |
@@ -79,6 +80,7 @@ fn walk_unsafe( | |||
79 | unsafe_exprs: &mut Vec<UnsafeExpr>, | 80 | unsafe_exprs: &mut Vec<UnsafeExpr>, |
80 | db: &dyn HirDatabase, | 81 | db: &dyn HirDatabase, |
81 | infer: &InferenceResult, | 82 | infer: &InferenceResult, |
83 | def: DefWithBodyId, | ||
82 | body: &Body, | 84 | body: &Body, |
83 | current: ExprId, | 85 | current: ExprId, |
84 | inside_unsafe_block: bool, | 86 | inside_unsafe_block: bool, |
@@ -97,6 +99,15 @@ fn walk_unsafe( | |||
97 | } | 99 | } |
98 | } | 100 | } |
99 | } | 101 | } |
102 | Expr::Path(path) => { | ||
103 | let resolver = resolver_for_expr(db.upcast(), def, current); | ||
104 | let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path.mod_path()); | ||
105 | if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial { | ||
106 | if db.static_data(id).mutable { | ||
107 | unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); | ||
108 | } | ||
109 | } | ||
110 | } | ||
100 | Expr::MethodCall { .. } => { | 111 | Expr::MethodCall { .. } => { |
101 | if infer | 112 | if infer |
102 | .method_resolution(current) | 113 | .method_resolution(current) |
@@ -112,13 +123,13 @@ fn walk_unsafe( | |||
112 | } | 123 | } |
113 | } | 124 | } |
114 | Expr::Unsafe { body: child } => { | 125 | Expr::Unsafe { body: child } => { |
115 | return walk_unsafe(unsafe_exprs, db, infer, body, *child, true); | 126 | return walk_unsafe(unsafe_exprs, db, infer, def, body, *child, true); |
116 | } | 127 | } |
117 | _ => {} | 128 | _ => {} |
118 | } | 129 | } |
119 | 130 | ||
120 | expr.walk_child_exprs(|child| { | 131 | expr.walk_child_exprs(|child| { |
121 | walk_unsafe(unsafe_exprs, db, infer, body, child, inside_unsafe_block); | 132 | walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block); |
122 | }); | 133 | }); |
123 | } | 134 | } |
124 | 135 | ||
@@ -170,4 +181,25 @@ fn main() { | |||
170 | "#, | 181 | "#, |
171 | ); | 182 | ); |
172 | } | 183 | } |
184 | |||
185 | #[test] | ||
186 | fn missing_unsafe_diagnostic_with_static_mut() { | ||
187 | check_diagnostics( | ||
188 | r#" | ||
189 | struct Ty { | ||
190 | a: u8, | ||
191 | } | ||
192 | |||
193 | static mut static_mut: Ty = Ty { a: 0 }; | ||
194 | |||
195 | fn main() { | ||
196 | let x = static_mut.a; | ||
197 | //^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block | ||
198 | unsafe { | ||
199 | let x = static_mut.a; | ||
200 | } | ||
201 | } | ||
202 | "#, | ||
203 | ); | ||
204 | } | ||
173 | } | 205 | } |
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 28f32a0a4..3d12039a6 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -440,6 +440,12 @@ impl<'a> InferenceContext<'a> { | |||
440 | let ty = self.insert_type_vars(ty.subst(&substs)); | 440 | let ty = self.insert_type_vars(ty.subst(&substs)); |
441 | forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) | 441 | forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) |
442 | } | 442 | } |
443 | TypeNs::AdtId(AdtId::UnionId(u)) => { | ||
444 | let substs = Ty::substs_from_path(&ctx, path, u.into(), true); | ||
445 | let ty = self.db.ty(u.into()); | ||
446 | let ty = self.insert_type_vars(ty.subst(&substs)); | ||
447 | forbid_unresolved_segments((ty, Some(u.into())), unresolved) | ||
448 | } | ||
443 | TypeNs::EnumVariantId(var) => { | 449 | TypeNs::EnumVariantId(var) => { |
444 | let substs = Ty::substs_from_path(&ctx, path, var.into(), true); | 450 | let substs = Ty::substs_from_path(&ctx, path, var.into(), true); |
445 | let ty = self.db.ty(var.parent.into()); | 451 | let ty = self.db.ty(var.parent.into()); |
@@ -490,10 +496,7 @@ impl<'a> InferenceContext<'a> { | |||
490 | // FIXME potentially resolve assoc type | 496 | // FIXME potentially resolve assoc type |
491 | (Ty::Unknown, None) | 497 | (Ty::Unknown, None) |
492 | } | 498 | } |
493 | TypeNs::AdtId(AdtId::EnumId(_)) | 499 | TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::TraitId(_) => { |
494 | | TypeNs::AdtId(AdtId::UnionId(_)) | ||
495 | | TypeNs::BuiltinType(_) | ||
496 | | TypeNs::TraitId(_) => { | ||
497 | // FIXME diagnostic | 500 | // FIXME diagnostic |
498 | (Ty::Unknown, None) | 501 | (Ty::Unknown, None) |
499 | } | 502 | } |
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 1eacc6f95..7638f167b 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs | |||
@@ -518,6 +518,7 @@ impl Ty { | |||
518 | let (segment, generic_def) = match resolved { | 518 | let (segment, generic_def) = match resolved { |
519 | ValueTyDefId::FunctionId(it) => (last, Some(it.into())), | 519 | ValueTyDefId::FunctionId(it) => (last, Some(it.into())), |
520 | ValueTyDefId::StructId(it) => (last, Some(it.into())), | 520 | ValueTyDefId::StructId(it) => (last, Some(it.into())), |
521 | ValueTyDefId::UnionId(it) => (last, Some(it.into())), | ||
521 | ValueTyDefId::ConstId(it) => (last, Some(it.into())), | 522 | ValueTyDefId::ConstId(it) => (last, Some(it.into())), |
522 | ValueTyDefId::StaticId(_) => (last, None), | 523 | ValueTyDefId::StaticId(_) => (last, None), |
523 | ValueTyDefId::EnumVariantId(var) => { | 524 | ValueTyDefId::EnumVariantId(var) => { |
@@ -1148,11 +1149,12 @@ impl_from!(BuiltinType, AdtId(StructId, EnumId, UnionId), TypeAliasId for TyDefI | |||
1148 | pub enum ValueTyDefId { | 1149 | pub enum ValueTyDefId { |
1149 | FunctionId(FunctionId), | 1150 | FunctionId(FunctionId), |
1150 | StructId(StructId), | 1151 | StructId(StructId), |
1152 | UnionId(UnionId), | ||
1151 | EnumVariantId(EnumVariantId), | 1153 | EnumVariantId(EnumVariantId), |
1152 | ConstId(ConstId), | 1154 | ConstId(ConstId), |
1153 | StaticId(StaticId), | 1155 | StaticId(StaticId), |
1154 | } | 1156 | } |
1155 | impl_from!(FunctionId, StructId, EnumVariantId, ConstId, StaticId for ValueTyDefId); | 1157 | impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId); |
1156 | 1158 | ||
1157 | /// Build the declared type of an item. This depends on the namespace; e.g. for | 1159 | /// Build the declared type of an item. This depends on the namespace; e.g. for |
1158 | /// `struct Foo(usize)`, we have two types: The type of the struct itself, and | 1160 | /// `struct Foo(usize)`, we have two types: The type of the struct itself, and |
@@ -1179,6 +1181,7 @@ pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Binders | |||
1179 | match def { | 1181 | match def { |
1180 | ValueTyDefId::FunctionId(it) => type_for_fn(db, it), | 1182 | ValueTyDefId::FunctionId(it) => type_for_fn(db, it), |
1181 | ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it), | 1183 | ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it), |
1184 | ValueTyDefId::UnionId(it) => type_for_adt(db, it.into()), | ||
1182 | ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it), | 1185 | ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it), |
1183 | ValueTyDefId::ConstId(it) => type_for_const(db, it), | 1186 | ValueTyDefId::ConstId(it) => type_for_const(db, it), |
1184 | ValueTyDefId::StaticId(it) => type_for_static(db, it), | 1187 | ValueTyDefId::StaticId(it) => type_for_static(db, it), |
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 3fd7d5cd4..5a7cf9455 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs | |||
@@ -334,16 +334,44 @@ fn infer_union() { | |||
334 | bar: f32, | 334 | bar: f32, |
335 | } | 335 | } |
336 | 336 | ||
337 | fn test() { | ||
338 | let u = MyUnion { foo: 0 }; | ||
339 | unsafe { baz(u); } | ||
340 | let u = MyUnion { bar: 0.0 }; | ||
341 | unsafe { baz(u); } | ||
342 | } | ||
343 | |||
337 | unsafe fn baz(u: MyUnion) { | 344 | unsafe fn baz(u: MyUnion) { |
338 | let inner = u.foo; | 345 | let inner = u.foo; |
346 | let inner = u.bar; | ||
339 | } | 347 | } |
340 | "#, | 348 | "#, |
341 | expect![[r#" | 349 | expect![[r#" |
342 | 61..62 'u': MyUnion | 350 | 57..172 '{ ...); } }': () |
343 | 73..99 '{ ...foo; }': () | 351 | 67..68 'u': MyUnion |
344 | 83..88 'inner': u32 | 352 | 71..89 'MyUnio...o: 0 }': MyUnion |
345 | 91..92 'u': MyUnion | 353 | 86..87 '0': u32 |
346 | 91..96 'u.foo': u32 | 354 | 95..113 'unsafe...(u); }': () |
355 | 102..113 '{ baz(u); }': () | ||
356 | 104..107 'baz': fn baz(MyUnion) | ||
357 | 104..110 'baz(u)': () | ||
358 | 108..109 'u': MyUnion | ||
359 | 122..123 'u': MyUnion | ||
360 | 126..146 'MyUnio... 0.0 }': MyUnion | ||
361 | 141..144 '0.0': f32 | ||
362 | 152..170 'unsafe...(u); }': () | ||
363 | 159..170 '{ baz(u); }': () | ||
364 | 161..164 'baz': fn baz(MyUnion) | ||
365 | 161..167 'baz(u)': () | ||
366 | 165..166 'u': MyUnion | ||
367 | 188..189 'u': MyUnion | ||
368 | 200..249 '{ ...bar; }': () | ||
369 | 210..215 'inner': u32 | ||
370 | 218..219 'u': MyUnion | ||
371 | 218..223 'u.foo': u32 | ||
372 | 233..238 'inner': f32 | ||
373 | 241..242 'u': MyUnion | ||
374 | 241..246 'u.bar': f32 | ||
347 | "#]], | 375 | "#]], |
348 | ); | 376 | ); |
349 | } | 377 | } |