diff options
Diffstat (limited to 'crates/ra_hir_ty/src/diagnostics/unsafe_check.rs')
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics/unsafe_check.rs | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs new file mode 100644 index 000000000..c512c4f8e --- /dev/null +++ b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs | |||
@@ -0,0 +1,120 @@ | |||
1 | //! Provides validations for unsafe code. Currently checks if unsafe functions are missing | ||
2 | //! unsafe blocks. | ||
3 | |||
4 | use std::sync::Arc; | ||
5 | |||
6 | use hir_def::{ | ||
7 | body::Body, | ||
8 | expr::{Expr, ExprId, UnaryOp}, | ||
9 | DefWithBodyId, FunctionId, | ||
10 | }; | ||
11 | use hir_expand::diagnostics::DiagnosticSink; | ||
12 | |||
13 | use crate::{ | ||
14 | db::HirDatabase, diagnostics::MissingUnsafe, lower::CallableDef, ApplicationTy, | ||
15 | InferenceResult, Ty, TypeCtor, | ||
16 | }; | ||
17 | |||
18 | pub struct UnsafeValidator<'a, 'b: 'a> { | ||
19 | func: FunctionId, | ||
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: FunctionId, | ||
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.into(); | ||
35 | let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); | ||
36 | let func_data = db.function_data(self.func); | ||
37 | if func_data.is_unsafe | ||
38 | || unsafe_expressions | ||
39 | .iter() | ||
40 | .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block) | ||
41 | .count() | ||
42 | == 0 | ||
43 | { | ||
44 | return; | ||
45 | } | ||
46 | |||
47 | let (_, body_source) = db.body_with_source_map(def); | ||
48 | for unsafe_expr in unsafe_expressions { | ||
49 | if !unsafe_expr.inside_unsafe_block { | ||
50 | if let Ok(in_file) = body_source.as_ref().expr_syntax(unsafe_expr.expr) { | ||
51 | self.sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value }) | ||
52 | } | ||
53 | } | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | |||
58 | pub struct UnsafeExpr { | ||
59 | pub expr: ExprId, | ||
60 | pub inside_unsafe_block: bool, | ||
61 | } | ||
62 | |||
63 | pub fn unsafe_expressions( | ||
64 | db: &dyn HirDatabase, | ||
65 | infer: &InferenceResult, | ||
66 | def: DefWithBodyId, | ||
67 | ) -> Vec<UnsafeExpr> { | ||
68 | let mut unsafe_exprs = vec![]; | ||
69 | let body = db.body(def); | ||
70 | walk_unsafe(&mut unsafe_exprs, db, infer, &body, body.body_expr, false); | ||
71 | |||
72 | unsafe_exprs | ||
73 | } | ||
74 | |||
75 | fn walk_unsafe( | ||
76 | unsafe_exprs: &mut Vec<UnsafeExpr>, | ||
77 | db: &dyn HirDatabase, | ||
78 | infer: &InferenceResult, | ||
79 | body: &Body, | ||
80 | current: ExprId, | ||
81 | inside_unsafe_block: bool, | ||
82 | ) { | ||
83 | let expr = &body.exprs[current]; | ||
84 | match expr { | ||
85 | Expr::Call { callee, .. } => { | ||
86 | let ty = &infer[*callee]; | ||
87 | if let &Ty::Apply(ApplicationTy { | ||
88 | ctor: TypeCtor::FnDef(CallableDef::FunctionId(func)), | ||
89 | .. | ||
90 | }) = ty | ||
91 | { | ||
92 | if db.function_data(func).is_unsafe { | ||
93 | unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | Expr::MethodCall { .. } => { | ||
98 | if infer | ||
99 | .method_resolution(current) | ||
100 | .map(|func| db.function_data(func).is_unsafe) | ||
101 | .unwrap_or(false) | ||
102 | { | ||
103 | unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); | ||
104 | } | ||
105 | } | ||
106 | Expr::UnaryOp { expr, op: UnaryOp::Deref } => { | ||
107 | if let Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. }) = &infer[*expr] { | ||
108 | unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); | ||
109 | } | ||
110 | } | ||
111 | Expr::Unsafe { body: child } => { | ||
112 | return walk_unsafe(unsafe_exprs, db, infer, body, *child, true); | ||
113 | } | ||
114 | _ => {} | ||
115 | } | ||
116 | |||
117 | expr.walk_child_exprs(|child| { | ||
118 | walk_unsafe(unsafe_exprs, db, infer, body, child, inside_unsafe_block); | ||
119 | }); | ||
120 | } | ||