]> git.proxmox.com Git - rustc.git/blame - src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
New upstream version 1.64.0+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,
6 expr::{Expr, ExprId, UnaryOp},
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(),
21 DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
22 };
23 if is_unsafe {
24 return res;
25 }
26
27 let body = db.body(def);
28 unsafe_expressions(db, &infer, def, &body, body.body_expr, &mut |expr| {
29 if !expr.inside_unsafe_block {
30 res.push(expr.expr);
31 }
32 });
33
34 res
35}
36
37pub struct UnsafeExpr {
38 pub expr: ExprId,
39 pub inside_unsafe_block: bool,
40}
41
42// FIXME: Move this out, its not a diagnostic only thing anymore, and handle unsafe pattern accesses as well
43pub fn unsafe_expressions(
44 db: &dyn HirDatabase,
45 infer: &InferenceResult,
46 def: DefWithBodyId,
47 body: &Body,
48 current: ExprId,
49 unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
50) {
51 walk_unsafe(db, infer, def, body, current, false, unsafe_expr_cb)
52}
53
54fn walk_unsafe(
55 db: &dyn HirDatabase,
56 infer: &InferenceResult,
57 def: DefWithBodyId,
58 body: &Body,
59 current: ExprId,
60 inside_unsafe_block: bool,
61 unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
62) {
63 let expr = &body.exprs[current];
64 match expr {
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 });
69 }
70 }
71 }
72 Expr::Path(path) => {
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 });
78 }
79 }
80 }
81 Expr::MethodCall { .. } => {
82 if infer
83 .method_resolution(current)
84 .map(|(func, _)| is_fn_unsafe_to_call(db, func))
85 .unwrap_or(false)
86 {
87 unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
88 }
89 }
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 });
93 }
94 }
95 Expr::Unsafe { body: child } => {
96 return walk_unsafe(db, infer, def, body, *child, true, unsafe_expr_cb);
97 }
98 _ => {}
99 }
100
101 expr.walk_child_exprs(|child| {
102 walk_unsafe(db, infer, def, body, child, inside_unsafe_block, unsafe_expr_cb);
103 });
104}