Unnamed repository; edit this file 'description' to name the repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! Provides validations for unsafe code. Currently checks if unsafe functions are missing
//! unsafe blocks.

use hir_def::{
    body::Body,
    hir::{Expr, ExprId, UnaryOp},
    resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
    DefWithBodyId,
};

use crate::{
    db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TyExt, TyKind,
};

pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
    let _p = tracing::info_span!("missing_unsafe").entered();

    let mut res = Vec::new();
    let is_unsafe = match def {
        DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
        DefWithBodyId::StaticId(_)
        | DefWithBodyId::ConstId(_)
        | DefWithBodyId::VariantId(_)
        | DefWithBodyId::InTypeConstId(_) => false,
    };
    if is_unsafe {
        return res;
    }

    let body = db.body(def);
    let infer = db.infer(def);
    unsafe_expressions(db, &infer, def, &body, body.body_expr, &mut |expr| {
        if !expr.inside_unsafe_block {
            res.push(expr.expr);
        }
    });

    res
}

pub struct UnsafeExpr {
    pub expr: ExprId,
    pub inside_unsafe_block: bool,
}

// FIXME: Move this out, its not a diagnostic only thing anymore, and handle unsafe pattern accesses as well
pub fn unsafe_expressions(
    db: &dyn HirDatabase,
    infer: &InferenceResult,
    def: DefWithBodyId,
    body: &Body,
    current: ExprId,
    unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
) {
    walk_unsafe(
        db,
        infer,
        body,
        &mut resolver_for_expr(db.upcast(), def, current),
        def,
        current,
        false,
        unsafe_expr_cb,
    )
}

fn walk_unsafe(
    db: &dyn HirDatabase,
    infer: &InferenceResult,
    body: &Body,
    resolver: &mut Resolver,
    def: DefWithBodyId,
    current: ExprId,
    inside_unsafe_block: bool,
    unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
) {
    let expr = &body.exprs[current];
    match expr {
        &Expr::Call { callee, .. } => {
            if let Some(func) = infer[callee].as_fn_def(db) {
                if is_fn_unsafe_to_call(db, func) {
                    unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
                }
            }
        }
        Expr::Path(path) => {
            let g = resolver.update_to_inner_scope(db.upcast(), def, current);
            let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
            if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
                if db.static_data(id).mutable {
                    unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
                }
            }
            resolver.reset_to_guard(g);
        }
        Expr::MethodCall { .. } => {
            if infer
                .method_resolution(current)
                .map(|(func, _)| is_fn_unsafe_to_call(db, func))
                .unwrap_or(false)
            {
                unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
            }
        }
        Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
            if let TyKind::Raw(..) = &infer[*expr].kind(Interner) {
                unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
            }
        }
        Expr::Unsafe { .. } => {
            return expr.walk_child_exprs(|child| {
                walk_unsafe(db, infer, body, resolver, def, child, true, unsafe_expr_cb);
            });
        }
        _ => {}
    }

    expr.walk_child_exprs(|child| {
        walk_unsafe(db, infer, body, resolver, def, child, inside_unsafe_block, unsafe_expr_cb);
    });
}
"repo": "flake-utils", "rev": "bba5dcc8e0b20ab664967ad83d24d64cb64ec4f4", "type": "github" }, "original": { "owner": "numtide", "repo": "flake-utils", "type": "github" } }, "nixCargoIntegration": { "inputs": { "devshell": "devshell", "dream2nix": "dream2nix", "nixpkgs": [ "nixpkgs" ], "rust-overlay": [ "rust-overlay" ] }, "locked": { "lastModified": 1656453541, "narHash": "sha256-ZCPVnS6zJOZJvIlwU3rKR8MBVm6A3F4/0mA7G1lQ3D0=", "owner": "yusdacra", "repo": "nix-cargo-integration", "rev": "9eb74345b30cd2e536d9dac9d4435d3c475605c7", "type": "github" }, "original": { "owner": "yusdacra", "repo": "nix-cargo-integration", "type": "github" } }, "nixpkgs": { "locked": { "lastModified": 1655624069, "narHash": "sha256-7g1zwTdp35GMTERnSzZMWJ7PG3QdDE8VOX3WsnOkAtM=", "owner": "nixos", "repo": "nixpkgs", "rev": "0d68d7c857fe301d49cdcd56130e0beea4ecd5aa", "type": "github" }, "original": { "owner": "nixos", "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } }, "root": { "inputs": { "nixCargoIntegration": "nixCargoIntegration", "nixpkgs": "nixpkgs", "rust-overlay": "rust-overlay" } }, "rust-overlay": { "inputs": { "flake-utils": "flake-utils", "nixpkgs": [ "nixpkgs" ] }, "locked": { "lastModified": 1655779671, "narHash": "sha256-6feeiGa6fb7ZPVHR71uswkmN1701TAJpwYQA8QffmRk=", "owner": "oxalica", "repo": "rust-overlay", "rev": "8159585609a772b041cce6019d5c21d240709244", "type": "github" }, "original": { "owner": "oxalica", "repo": "rust-overlay", "type": "github" } } }, "root": "root", "version": 7 }