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 | 123 |
1 files changed, 123 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..b8ff95ee1 --- /dev/null +++ b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs | |||
@@ -0,0 +1,123 @@ | |||
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, | ||
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(super) struct UnsafeValidator<'a, 'b: 'a> { | ||
19 | owner: DefWithBodyId, | ||
20 | infer: Arc<InferenceResult>, | ||
21 | sink: &'a mut DiagnosticSink<'b>, | ||
22 | } | ||
23 | |||
24 | impl<'a, 'b> UnsafeValidator<'a, 'b> { | ||
25 | pub(super) fn new( | ||
26 | owner: DefWithBodyId, | ||
27 | infer: Arc<InferenceResult>, | ||
28 | sink: &'a mut DiagnosticSink<'b>, | ||
29 | ) -> UnsafeValidator<'a, 'b> { | ||
30 | UnsafeValidator { owner, infer, sink } | ||
31 | } | ||
32 | |||
33 | pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { | ||
34 | let def = self.owner.into(); | ||
35 | let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); | ||
36 | let is_unsafe = match self.owner { | ||
37 | DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe, | ||
38 | DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false, | ||
39 | }; | ||
40 | if is_unsafe | ||
41 | || unsafe_expressions | ||
42 | .iter() | ||
43 | .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block) | ||
44 | .count() | ||
45 | == 0 | ||
46 | { | ||
47 | return; | ||
48 | } | ||
49 | |||
50 | let (_, body_source) = db.body_with_source_map(def); | ||
51 | for unsafe_expr in unsafe_expressions { | ||
52 | if !unsafe_expr.inside_unsafe_block { | ||
53 | if let Ok(in_file) = body_source.as_ref().expr_syntax(unsafe_expr.expr) { | ||
54 | self.sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value }) | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | |||
61 | pub struct UnsafeExpr { | ||
62 | pub expr: ExprId, | ||
63 | pub inside_unsafe_block: bool, | ||
64 | } | ||
65 | |||
66 | pub fn unsafe_expressions( | ||
67 | db: &dyn HirDatabase, | ||
68 | infer: &InferenceResult, | ||
69 | def: DefWithBodyId, | ||
70 | ) -> Vec<UnsafeExpr> { | ||
71 | let mut unsafe_exprs = vec![]; | ||
72 | let body = db.body(def); | ||
73 | walk_unsafe(&mut unsafe_exprs, db, infer, &body, body.body_expr, false); | ||
74 | |||
75 | unsafe_exprs | ||
76 | } | ||
77 | |||
78 | fn walk_unsafe( | ||
79 | unsafe_exprs: &mut Vec<UnsafeExpr>, | ||
80 | db: &dyn HirDatabase, | ||
81 | infer: &InferenceResult, | ||
82 | body: &Body, | ||
83 | current: ExprId, | ||
84 | inside_unsafe_block: bool, | ||
85 | ) { | ||
86 | let expr = &body.exprs[current]; | ||
87 | match expr { | ||
88 | Expr::Call { callee, .. } => { | ||
89 | let ty = &infer[*callee]; | ||
90 | if let &Ty::Apply(ApplicationTy { | ||
91 | ctor: TypeCtor::FnDef(CallableDef::FunctionId(func)), | ||
92 | .. | ||
93 | }) = ty | ||
94 | { | ||
95 | if db.function_data(func).is_unsafe { | ||
96 | unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); | ||
97 | } | ||
98 | } | ||
99 | } | ||
100 | Expr::MethodCall { .. } => { | ||
101 | if infer | ||
102 | .method_resolution(current) | ||
103 | .map(|func| db.function_data(func).is_unsafe) | ||
104 | .unwrap_or(false) | ||
105 | { | ||
106 | unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); | ||
107 | } | ||
108 | } | ||
109 | Expr::UnaryOp { expr, op: UnaryOp::Deref } => { | ||
110 | if let Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. }) = &infer[*expr] { | ||
111 | unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); | ||
112 | } | ||
113 | } | ||
114 | Expr::Unsafe { body: child } => { | ||
115 | return walk_unsafe(unsafe_exprs, db, infer, body, *child, true); | ||
116 | } | ||
117 | _ => {} | ||
118 | } | ||
119 | |||
120 | expr.walk_child_exprs(|child| { | ||
121 | walk_unsafe(unsafe_exprs, db, infer, body, child, inside_unsafe_block); | ||
122 | }); | ||
123 | } | ||