]> git.proxmox.com Git - rustc.git/blame - src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / hir-ty / src / diagnostics / unsafe_check.rs
CommitLineData
064997fb
FG
1//! Provides validations for unsafe code. Currently checks if unsafe functions are missing
2//! unsafe blocks.
3
4use hir_def::{
5 body::Body,
fe692bf9 6 hir::{Expr, ExprId, UnaryOp},
064997fb
FG
7 resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
8 DefWithBodyId,
9};
10
11use crate::{
12 db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TyExt, TyKind,
13};
14
15pub 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(),
fe692bf9
FG
21 DefWithBodyId::StaticId(_)
22 | DefWithBodyId::ConstId(_)
23 | DefWithBodyId::VariantId(_)
24 | DefWithBodyId::InTypeConstId(_) => false,
064997fb
FG
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
40pub 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
46pub 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
57fn 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);
fe692bf9 77 let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
781aab86 78 if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
064997fb
FG
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 }
353b0b11
FG
98 Expr::Unsafe { .. } => {
99 return expr.walk_child_exprs(|child| {
100 walk_unsafe(db, infer, def, body, child, true, unsafe_expr_cb);
101 });
064997fb
FG
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}