• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Provides validations for unsafe code. Currently checks if unsafe functions are missing
2 //! unsafe blocks.
3 
4 use hir_def::{
5     body::Body,
6     hir::{Expr, ExprId, UnaryOp},
7     resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
8     DefWithBodyId,
9 };
10 
11 use crate::{
12     db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TyExt, TyKind,
13 };
14 
missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId>15 pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
16     let infer = db.infer(def);
17     let mut res = Vec::new();
18 
19     let is_unsafe = match def {
20         DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(),
21         DefWithBodyId::StaticId(_)
22         | DefWithBodyId::ConstId(_)
23         | DefWithBodyId::VariantId(_)
24         | DefWithBodyId::InTypeConstId(_) => false,
25     };
26     if is_unsafe {
27         return res;
28     }
29 
30     let body = db.body(def);
31     unsafe_expressions(db, &infer, def, &body, body.body_expr, &mut |expr| {
32         if !expr.inside_unsafe_block {
33             res.push(expr.expr);
34         }
35     });
36 
37     res
38 }
39 
40 pub struct UnsafeExpr {
41     pub expr: ExprId,
42     pub inside_unsafe_block: bool,
43 }
44 
45 // FIXME: Move this out, its not a diagnostic only thing anymore, and handle unsafe pattern accesses as well
unsafe_expressions( db: &dyn HirDatabase, infer: &InferenceResult, def: DefWithBodyId, body: &Body, current: ExprId, unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr), )46 pub fn unsafe_expressions(
47     db: &dyn HirDatabase,
48     infer: &InferenceResult,
49     def: DefWithBodyId,
50     body: &Body,
51     current: ExprId,
52     unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
53 ) {
54     walk_unsafe(db, infer, def, body, current, false, unsafe_expr_cb)
55 }
56 
walk_unsafe( db: &dyn HirDatabase, infer: &InferenceResult, def: DefWithBodyId, body: &Body, current: ExprId, inside_unsafe_block: bool, unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr), )57 fn walk_unsafe(
58     db: &dyn HirDatabase,
59     infer: &InferenceResult,
60     def: DefWithBodyId,
61     body: &Body,
62     current: ExprId,
63     inside_unsafe_block: bool,
64     unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
65 ) {
66     let expr = &body.exprs[current];
67     match expr {
68         &Expr::Call { callee, .. } => {
69             if let Some(func) = infer[callee].as_fn_def(db) {
70                 if is_fn_unsafe_to_call(db, func) {
71                     unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
72                 }
73             }
74         }
75         Expr::Path(path) => {
76             let resolver = resolver_for_expr(db.upcast(), def, current);
77             let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
78             if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial {
79                 if db.static_data(id).mutable {
80                     unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
81                 }
82             }
83         }
84         Expr::MethodCall { .. } => {
85             if infer
86                 .method_resolution(current)
87                 .map(|(func, _)| is_fn_unsafe_to_call(db, func))
88                 .unwrap_or(false)
89             {
90                 unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
91             }
92         }
93         Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
94             if let TyKind::Raw(..) = &infer[*expr].kind(Interner) {
95                 unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
96             }
97         }
98         Expr::Unsafe { .. } => {
99             return expr.walk_child_exprs(|child| {
100                 walk_unsafe(db, infer, def, body, child, true, unsafe_expr_cb);
101             });
102         }
103         _ => {}
104     }
105 
106     expr.walk_child_exprs(|child| {
107         walk_unsafe(db, infer, def, body, child, inside_unsafe_block, unsafe_expr_cb);
108     });
109 }
110