aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty/src/diagnostics/unsafe_check.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_ty/src/diagnostics/unsafe_check.rs')
-rw-r--r--crates/hir_ty/src/diagnostics/unsafe_check.rs151
1 files changed, 16 insertions, 135 deletions
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs
index c3c483425..777f347b8 100644
--- a/crates/hir_ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs
@@ -1,8 +1,6 @@
1//! Provides validations for unsafe code. Currently checks if unsafe functions are missing 1//! Provides validations for unsafe code. Currently checks if unsafe functions are missing
2//! unsafe blocks. 2//! unsafe blocks.
3 3
4use std::sync::Arc;
5
6use hir_def::{ 4use hir_def::{
7 body::Body, 5 body::Body,
8 expr::{Expr, ExprId, UnaryOp}, 6 expr::{Expr, ExprId, UnaryOp},
@@ -10,60 +8,32 @@ use hir_def::{
10 DefWithBodyId, 8 DefWithBodyId,
11}; 9};
12 10
13use crate::{ 11use crate::{db::HirDatabase, InferenceResult, Interner, TyExt, TyKind};
14 db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult,
15 Interner, TyExt, TyKind,
16};
17 12
18pub(super) struct UnsafeValidator<'a, 'b: 'a> { 13pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
19 owner: DefWithBodyId, 14 let infer = db.infer(def);
20 infer: Arc<InferenceResult>,
21 sink: &'a mut DiagnosticSink<'b>,
22}
23 15
24impl<'a, 'b> UnsafeValidator<'a, 'b> { 16 let is_unsafe = match def {
25 pub(super) fn new( 17 DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
26 owner: DefWithBodyId, 18 DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
27 infer: Arc<InferenceResult>, 19 };
28 sink: &'a mut DiagnosticSink<'b>, 20 if is_unsafe {
29 ) -> UnsafeValidator<'a, 'b> { 21 return Vec::new();
30 UnsafeValidator { owner, infer, sink }
31 } 22 }
32 23
33 pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { 24 unsafe_expressions(db, &infer, def)
34 let def = self.owner; 25 .into_iter()
35 let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); 26 .filter(|it| !it.inside_unsafe_block)
36 let is_unsafe = match self.owner { 27 .map(|it| it.expr)
37 DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(), 28 .collect()
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} 29}
60 30
61pub(crate) struct UnsafeExpr { 31struct UnsafeExpr {
62 pub(crate) expr: ExprId, 32 pub(crate) expr: ExprId,
63 pub(crate) inside_unsafe_block: bool, 33 pub(crate) inside_unsafe_block: bool,
64} 34}
65 35
66pub(crate) fn unsafe_expressions( 36fn unsafe_expressions(
67 db: &dyn HirDatabase, 37 db: &dyn HirDatabase,
68 infer: &InferenceResult, 38 infer: &InferenceResult,
69 def: DefWithBodyId, 39 def: DefWithBodyId,
@@ -126,92 +96,3 @@ fn walk_unsafe(
126 walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block); 96 walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block);
127 }); 97 });
128} 98}
129
130#[cfg(test)]
131mod tests {
132 use crate::diagnostics::tests::check_diagnostics;
133
134 #[test]
135 fn missing_unsafe_diagnostic_with_raw_ptr() {
136 check_diagnostics(
137 r#"
138fn main() {
139 let x = &5 as *const usize;
140 unsafe { let y = *x; }
141 let z = *x;
142} //^^ This operation is unsafe and requires an unsafe function or block
143"#,
144 )
145 }
146
147 #[test]
148 fn missing_unsafe_diagnostic_with_unsafe_call() {
149 check_diagnostics(
150 r#"
151struct HasUnsafe;
152
153impl HasUnsafe {
154 unsafe fn unsafe_fn(&self) {
155 let x = &5 as *const usize;
156 let y = *x;
157 }
158}
159
160unsafe fn unsafe_fn() {
161 let x = &5 as *const usize;
162 let y = *x;
163}
164
165fn main() {
166 unsafe_fn();
167 //^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
168 HasUnsafe.unsafe_fn();
169 //^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
170 unsafe {
171 unsafe_fn();
172 HasUnsafe.unsafe_fn();
173 }
174}
175"#,
176 );
177 }
178
179 #[test]
180 fn missing_unsafe_diagnostic_with_static_mut() {
181 check_diagnostics(
182 r#"
183struct Ty {
184 a: u8,
185}
186
187static mut STATIC_MUT: Ty = Ty { a: 0 };
188
189fn main() {
190 let x = STATIC_MUT.a;
191 //^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
192 unsafe {
193 let x = STATIC_MUT.a;
194 }
195}
196"#,
197 );
198 }
199
200 #[test]
201 fn no_missing_unsafe_diagnostic_with_safe_intrinsic() {
202 check_diagnostics(
203 r#"
204extern "rust-intrinsic" {
205 pub fn bitreverse(x: u32) -> u32; // Safe intrinsic
206 pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic
207}
208
209fn main() {
210 let _ = bitreverse(12);
211 let _ = floorf32(12.0);
212 //^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
213}
214"#,
215 );
216 }
217}