1 //! Provides validations for unsafe code. Currently checks if unsafe functions are missing
6 hir
::{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(_
)
22 | DefWithBodyId
::ConstId(_
)
23 | DefWithBodyId
::VariantId(_
)
24 | DefWithBodyId
::InTypeConstId(_
) => false,
30 let body
= db
.body(def
);
31 unsafe_expressions(db
, &infer
, def
, &body
, body
.body_expr
, &mut |expr
| {
32 if !expr
.inside_unsafe_block
{
40 pub struct UnsafeExpr
{
42 pub inside_unsafe_block
: bool
,
45 // FIXME: Move this out, its not a diagnostic only thing anymore, and handle unsafe pattern accesses as well
46 pub fn unsafe_expressions(
48 infer
: &InferenceResult
,
52 unsafe_expr_cb
: &mut dyn FnMut(UnsafeExpr
),
54 walk_unsafe(db
, infer
, def
, body
, current
, false, unsafe_expr_cb
)
59 infer
: &InferenceResult
,
63 inside_unsafe_block
: bool
,
64 unsafe_expr_cb
: &mut dyn FnMut(UnsafeExpr
),
66 let expr
= &body
.exprs
[current
];
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 }
);
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 }
);
84 Expr
::MethodCall { .. }
=> {
86 .method_resolution(current
)
87 .map(|(func
, _
)| is_fn_unsafe_to_call(db
, func
))
90 unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block }
);
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 }
);
98 Expr
::Unsafe { .. }
=> {
99 return expr
.walk_child_exprs(|child
| {
100 walk_unsafe(db
, infer
, def
, body
, child
, true, unsafe_expr_cb
);
106 expr
.walk_child_exprs(|child
| {
107 walk_unsafe(db
, infer
, def
, body
, child
, inside_unsafe_block
, unsafe_expr_cb
);