1 //! Provides validations for unsafe code. Currently checks if unsafe functions are missing
6 expr::{Expr, ExprId, UnaryOp},
7 resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
12 db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TyExt, TyKind,
15 pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
16 let infer = db.infer(def);
17 let mut res = Vec::new();
19 let is_unsafe = match def {
20 DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(),
21 DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
27 let body = db.body(def);
28 unsafe_expressions(db, &infer, def, &body, body.body_expr, &mut |expr| {
29 if !expr.inside_unsafe_block {
37 pub struct UnsafeExpr {
39 pub inside_unsafe_block: bool,
42 // FIXME: Move this out, its not a diagnostic only thing anymore, and handle unsafe pattern accesses as well
43 pub fn unsafe_expressions(
45 infer: &InferenceResult,
49 unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
51 walk_unsafe(db, infer, def, body, current, false, unsafe_expr_cb)
56 infer: &InferenceResult,
60 inside_unsafe_block: bool,
61 unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
63 let expr = &body.exprs[current];
65 &Expr::Call { callee, .. } => {
66 if let Some(func) = infer[callee].as_fn_def(db) {
67 if is_fn_unsafe_to_call(db, func) {
68 unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
73 let resolver = resolver_for_expr(db.upcast(), def, current);
74 let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path.mod_path());
75 if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial {
76 if db.static_data(id).mutable {
77 unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
81 Expr::MethodCall { .. } => {
83 .method_resolution(current)
84 .map(|(func, _)| is_fn_unsafe_to_call(db, func))
87 unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
90 Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
91 if let TyKind::Raw(..) = &infer[*expr].kind(Interner) {
92 unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
95 Expr::Unsafe { body: child } => {
96 return walk_unsafe(db, infer, def, body, *child, true, unsafe_expr_cb);
101 expr.walk_child_exprs(|child| {
102 walk_unsafe(db, infer, def, body, child, inside_unsafe_block, unsafe_expr_cb);