aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty/src/unsafe_validation.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_ty/src/unsafe_validation.rs')
-rw-r--r--crates/ra_hir_ty/src/unsafe_validation.rs120
1 files changed, 120 insertions, 0 deletions
diff --git a/crates/ra_hir_ty/src/unsafe_validation.rs b/crates/ra_hir_ty/src/unsafe_validation.rs
new file mode 100644
index 000000000..c512c4f8e
--- /dev/null
+++ b/crates/ra_hir_ty/src/unsafe_validation.rs
@@ -0,0 +1,120 @@
1//! Provides validations for unsafe code. Currently checks if unsafe functions are missing
2//! unsafe blocks.
3
4use std::sync::Arc;
5
6use hir_def::{
7 body::Body,
8 expr::{Expr, ExprId, UnaryOp},
9 DefWithBodyId, FunctionId,
10};
11use hir_expand::diagnostics::DiagnosticSink;
12
13use crate::{
14 db::HirDatabase, diagnostics::MissingUnsafe, lower::CallableDef, ApplicationTy,
15 InferenceResult, Ty, TypeCtor,
16};
17
18pub struct UnsafeValidator<'a, 'b: 'a> {
19 func: FunctionId,
20 infer: Arc<InferenceResult>,
21 sink: &'a mut DiagnosticSink<'b>,
22}
23
24impl<'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
58pub struct UnsafeExpr {
59 pub expr: ExprId,
60 pub inside_unsafe_block: bool,
61}
62
63pub 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
75fn 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}