aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_ty/src/diagnostics/unsafe_check.rs')
-rw-r--r--crates/ra_hir_ty/src/diagnostics/unsafe_check.rs173
1 files changed, 0 insertions, 173 deletions
diff --git a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs
deleted file mode 100644
index 5cc76bdce..000000000
--- a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs
+++ /dev/null
@@ -1,173 +0,0 @@
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,
10};
11use hir_expand::diagnostics::DiagnosticSink;
12
13use crate::{
14 db::HirDatabase, diagnostics::MissingUnsafe, lower::CallableDefId, ApplicationTy,
15 InferenceResult, Ty, TypeCtor,
16};
17
18pub(super) struct UnsafeValidator<'a, 'b: 'a> {
19 owner: DefWithBodyId,
20 infer: Arc<InferenceResult>,
21 sink: &'a mut DiagnosticSink<'b>,
22}
23
24impl<'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
61pub struct UnsafeExpr {
62 pub expr: ExprId,
63 pub inside_unsafe_block: bool,
64}
65
66pub 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
78fn 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(CallableDefId::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}
124
125#[cfg(test)]
126mod tests {
127 use crate::diagnostics::tests::check_diagnostics;
128
129 #[test]
130 fn missing_unsafe_diagnostic_with_raw_ptr() {
131 check_diagnostics(
132 r#"
133fn main() {
134 let x = &5 as *const usize;
135 unsafe { let y = *x; }
136 let z = *x;
137} //^^ This operation is unsafe and requires an unsafe function or block
138"#,
139 )
140 }
141
142 #[test]
143 fn missing_unsafe_diagnostic_with_unsafe_call() {
144 check_diagnostics(
145 r#"
146struct HasUnsafe;
147
148impl HasUnsafe {
149 unsafe fn unsafe_fn(&self) {
150 let x = &5 as *const usize;
151 let y = *x;
152 }
153}
154
155unsafe fn unsafe_fn() {
156 let x = &5 as *const usize;
157 let y = *x;
158}
159
160fn main() {
161 unsafe_fn();
162 //^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
163 HasUnsafe.unsafe_fn();
164 //^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
165 unsafe {
166 unsafe_fn();
167 HasUnsafe.unsafe_fn();
168 }
169}
170"#,
171 );
172 }
173}