aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/code_model.rs5
-rw-r--r--crates/ra_hir/src/diagnostics.rs50
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs58
-rw-r--r--crates/ra_hir_ty/src/expr.rs24
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs28
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
37use crate::{ 37use 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 @@
2pub use hir_def::diagnostics::UnresolvedModule; 2pub use hir_def::diagnostics::UnresolvedModule;
3pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; 3pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
4pub use hir_ty::diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField}; 4pub use hir_ty::diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField};
5
6use std::sync::Arc;
7
8use crate::code_model::Function;
9use crate::db::HirDatabase;
10use crate::has_source::HasSource;
11use hir_ty::{
12 diagnostics::{MissingUnsafe, UnnecessaryUnsafe},
13 expr::unsafe_expressions,
14 InferenceResult,
15};
16use ra_syntax::AstPtr;
17
18pub struct UnsafeValidator<'a, 'b: 'a> {
19 func: &'a Function,
20 infer: Arc<InferenceResult>,
21 sink: &'a mut DiagnosticSink<'b>,
22}
23
24impl<'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)]
174pub struct MissingUnsafe {
175 pub file: HirFileId,
176 pub fn_def: AstPtr<ast::FnDef>,
177 pub fn_name: Name,
178}
179
180impl 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
192impl 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)]
203pub struct UnnecessaryUnsafe {
204 pub file: HirFileId,
205 pub fn_def: AstPtr<ast::FnDef>,
206 pub fn_name: Name,
207}
208
209impl 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
221impl 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
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId}; 5use hir_def::{path::path, resolver::HasResolver, AdtId, DefWithBodyId, FunctionId};
6use hir_expand::diagnostics::DiagnosticSink; 6use hir_expand::diagnostics::DiagnosticSink;
7use ra_syntax::{ast, AstPtr}; 7use ra_syntax::{ast, AstPtr};
8use rustc_hash::FxHashSet; 8use 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
316pub 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]
375fn test_unsafe_highlighting() {
376 let (analysis, file_id) = single_file(
377 r#"
378unsafe fn unsafe_fn() {}
379
380struct HasUnsafeFn;
381
382impl HasUnsafeFn {
383 unsafe fn unsafe_method(&self) {}
384}
385
386fn 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}