diff options
author | Paul Daniel Faria <[email protected]> | 2020-05-23 22:49:53 +0100 |
---|---|---|
committer | Paul Daniel Faria <[email protected]> | 2020-06-27 15:08:14 +0100 |
commit | 0b95bed83fc8db897f54b350168567f14527e8de (patch) | |
tree | 94fc33b8e7f160ae2b45e38b32a70856006c93a2 /crates | |
parent | 9d1e2c4d9dc6c7f5fbaee5d9907d135f618d7ac6 (diff) |
Add unsafe diagnostics and unsafe highlighting
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/code_model.rs | 5 | ||||
-rw-r--r-- | crates/ra_hir/src/diagnostics.rs | 50 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics.rs | 58 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/expr.rs | 24 | ||||
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting/tests.rs | 28 |
5 files changed, 163 insertions, 2 deletions
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index a379b9f49..131180a63 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -36,6 +36,7 @@ use rustc_hash::FxHashSet; | |||
36 | 36 | ||
37 | use crate::{ | 37 | use crate::{ |
38 | db::{DefDatabase, HirDatabase}, | 38 | db::{DefDatabase, HirDatabase}, |
39 | diagnostics::UnsafeValidator, | ||
39 | has_source::HasSource, | 40 | has_source::HasSource, |
40 | CallableDef, HirDisplay, InFile, Name, | 41 | CallableDef, HirDisplay, InFile, Name, |
41 | }; | 42 | }; |
@@ -677,7 +678,9 @@ impl Function { | |||
677 | let _p = profile("Function::diagnostics"); | 678 | let _p = profile("Function::diagnostics"); |
678 | let infer = db.infer(self.id.into()); | 679 | let infer = db.infer(self.id.into()); |
679 | infer.add_diagnostics(db, self.id, sink); | 680 | infer.add_diagnostics(db, self.id, sink); |
680 | let mut validator = ExprValidator::new(self.id, infer, sink); | 681 | let mut validator = ExprValidator::new(self.id, infer.clone(), sink); |
682 | validator.validate_body(db); | ||
683 | let mut validator = UnsafeValidator::new(&self, infer, sink); | ||
681 | validator.validate_body(db); | 684 | validator.validate_body(db); |
682 | } | 685 | } |
683 | } | 686 | } |
diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs index c82883d0c..562f3fe5c 100644 --- a/crates/ra_hir/src/diagnostics.rs +++ b/crates/ra_hir/src/diagnostics.rs | |||
@@ -2,3 +2,53 @@ | |||
2 | pub use hir_def::diagnostics::UnresolvedModule; | 2 | pub use hir_def::diagnostics::UnresolvedModule; |
3 | pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; | 3 | pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; |
4 | pub use hir_ty::diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField}; | 4 | pub use hir_ty::diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField}; |
5 | |||
6 | use std::sync::Arc; | ||
7 | |||
8 | use crate::code_model::Function; | ||
9 | use crate::db::HirDatabase; | ||
10 | use crate::has_source::HasSource; | ||
11 | use hir_ty::{ | ||
12 | diagnostics::{MissingUnsafe, UnnecessaryUnsafe}, | ||
13 | expr::unsafe_expressions, | ||
14 | InferenceResult, | ||
15 | }; | ||
16 | use ra_syntax::AstPtr; | ||
17 | |||
18 | pub struct UnsafeValidator<'a, 'b: 'a> { | ||
19 | func: &'a Function, | ||
20 | infer: Arc<InferenceResult>, | ||
21 | sink: &'a mut DiagnosticSink<'b>, | ||
22 | } | ||
23 | |||
24 | impl<'a, 'b> UnsafeValidator<'a, 'b> { | ||
25 | pub fn new( | ||
26 | func: &'a Function, | ||
27 | infer: Arc<InferenceResult>, | ||
28 | sink: &'a mut DiagnosticSink<'b>, | ||
29 | ) -> UnsafeValidator<'a, 'b> { | ||
30 | UnsafeValidator { func, infer, sink } | ||
31 | } | ||
32 | |||
33 | pub fn validate_body(&mut self, db: &dyn HirDatabase) { | ||
34 | let def = self.func.id.into(); | ||
35 | let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); | ||
36 | let func_data = db.function_data(self.func.id); | ||
37 | let unnecessary = func_data.is_unsafe && unsafe_expressions.len() == 0; | ||
38 | let missing = !func_data.is_unsafe && unsafe_expressions.len() > 0; | ||
39 | if !(unnecessary || missing) { | ||
40 | return; | ||
41 | } | ||
42 | |||
43 | let in_file = self.func.source(db); | ||
44 | let file = in_file.file_id; | ||
45 | let fn_def = AstPtr::new(&in_file.value); | ||
46 | let fn_name = func_data.name.clone().into(); | ||
47 | |||
48 | if unnecessary { | ||
49 | self.sink.push(UnnecessaryUnsafe { file, fn_def, fn_name }) | ||
50 | } else { | ||
51 | self.sink.push(MissingUnsafe { file, fn_def, fn_name }) | ||
52 | } | ||
53 | } | ||
54 | } | ||
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index ebd9cb08f..3469cc680 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs | |||
@@ -169,3 +169,61 @@ impl AstDiagnostic for BreakOutsideOfLoop { | |||
169 | ast::Expr::cast(node).unwrap() | 169 | ast::Expr::cast(node).unwrap() |
170 | } | 170 | } |
171 | } | 171 | } |
172 | |||
173 | #[derive(Debug)] | ||
174 | pub struct MissingUnsafe { | ||
175 | pub file: HirFileId, | ||
176 | pub fn_def: AstPtr<ast::FnDef>, | ||
177 | pub fn_name: Name, | ||
178 | } | ||
179 | |||
180 | impl Diagnostic for MissingUnsafe { | ||
181 | fn message(&self) -> String { | ||
182 | format!("Missing unsafe marker on fn `{}`", self.fn_name) | ||
183 | } | ||
184 | fn source(&self) -> InFile<SyntaxNodePtr> { | ||
185 | InFile { file_id: self.file, value: self.fn_def.clone().into() } | ||
186 | } | ||
187 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
188 | self | ||
189 | } | ||
190 | } | ||
191 | |||
192 | impl AstDiagnostic for MissingUnsafe { | ||
193 | type AST = ast::FnDef; | ||
194 | |||
195 | fn ast(&self, db: &impl AstDatabase) -> Self::AST { | ||
196 | let root = db.parse_or_expand(self.source().file_id).unwrap(); | ||
197 | let node = self.source().value.to_node(&root); | ||
198 | ast::FnDef::cast(node).unwrap() | ||
199 | } | ||
200 | } | ||
201 | |||
202 | #[derive(Debug)] | ||
203 | pub struct UnnecessaryUnsafe { | ||
204 | pub file: HirFileId, | ||
205 | pub fn_def: AstPtr<ast::FnDef>, | ||
206 | pub fn_name: Name, | ||
207 | } | ||
208 | |||
209 | impl Diagnostic for UnnecessaryUnsafe { | ||
210 | fn message(&self) -> String { | ||
211 | format!("Unnecessary unsafe marker on fn `{}`", self.fn_name) | ||
212 | } | ||
213 | fn source(&self) -> InFile<SyntaxNodePtr> { | ||
214 | InFile { file_id: self.file, value: self.fn_def.clone().into() } | ||
215 | } | ||
216 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
217 | self | ||
218 | } | ||
219 | } | ||
220 | |||
221 | impl AstDiagnostic for UnnecessaryUnsafe { | ||
222 | type AST = ast::FnDef; | ||
223 | |||
224 | fn ast(&self, db: &impl AstDatabase) -> Self::AST { | ||
225 | let root = db.parse_or_expand(self.source().file_id).unwrap(); | ||
226 | let node = self.source().value.to_node(&root); | ||
227 | ast::FnDef::cast(node).unwrap() | ||
228 | } | ||
229 | } | ||
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs index 7db928dde..795f1762c 100644 --- a/crates/ra_hir_ty/src/expr.rs +++ b/crates/ra_hir_ty/src/expr.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId}; | 5 | use hir_def::{path::path, resolver::HasResolver, AdtId, DefWithBodyId, FunctionId}; |
6 | use hir_expand::diagnostics::DiagnosticSink; | 6 | use hir_expand::diagnostics::DiagnosticSink; |
7 | use ra_syntax::{ast, AstPtr}; | 7 | use ra_syntax::{ast, AstPtr}; |
8 | use rustc_hash::FxHashSet; | 8 | use rustc_hash::FxHashSet; |
@@ -312,3 +312,25 @@ pub fn record_pattern_missing_fields( | |||
312 | } | 312 | } |
313 | Some((variant_def, missed_fields, exhaustive)) | 313 | Some((variant_def, missed_fields, exhaustive)) |
314 | } | 314 | } |
315 | |||
316 | pub fn unsafe_expressions( | ||
317 | db: &dyn HirDatabase, | ||
318 | infer: &InferenceResult, | ||
319 | def: DefWithBodyId, | ||
320 | ) -> Vec<ExprId> { | ||
321 | let mut unsafe_expr_ids = vec![]; | ||
322 | let body = db.body(def); | ||
323 | for (id, expr) in body.exprs.iter() { | ||
324 | if let Expr::Call { callee, .. } = expr { | ||
325 | if infer | ||
326 | .method_resolution(*callee) | ||
327 | .map(|func| db.function_data(func).is_unsafe) | ||
328 | .unwrap_or(false) | ||
329 | { | ||
330 | unsafe_expr_ids.push(id); | ||
331 | } | ||
332 | } | ||
333 | } | ||
334 | |||
335 | unsafe_expr_ids | ||
336 | } | ||
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index b7fad9719..39cd74ac3 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs | |||
@@ -370,3 +370,31 @@ fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) { | |||
370 | fs::write(dst_file, &actual_html).unwrap(); | 370 | fs::write(dst_file, &actual_html).unwrap(); |
371 | assert_eq_text!(expected_html, actual_html); | 371 | assert_eq_text!(expected_html, actual_html); |
372 | } | 372 | } |
373 | |||
374 | #[test] | ||
375 | fn test_unsafe_highlighting() { | ||
376 | let (analysis, file_id) = single_file( | ||
377 | r#" | ||
378 | unsafe fn unsafe_fn() {} | ||
379 | |||
380 | struct HasUnsafeFn; | ||
381 | |||
382 | impl HasUnsafeFn { | ||
383 | unsafe fn unsafe_method(&self) {} | ||
384 | } | ||
385 | |||
386 | fn main() { | ||
387 | unsafe { | ||
388 | unsafe_fn(); | ||
389 | HasUnsafeFn.unsafe_method(); | ||
390 | } | ||
391 | } | ||
392 | "# | ||
393 | .trim(), | ||
394 | ); | ||
395 | let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_unsafe.html"); | ||
396 | let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); | ||
397 | let expected_html = &read_text(&dst_file); | ||
398 | fs::write(dst_file, &actual_html).unwrap(); | ||
399 | assert_eq_text!(expected_html, actual_html); | ||
400 | } | ||