Skip to main content

wowlab_tidy/languages/rust/rules/correctness/
transmute_usage.rs

1use ra_ap_syntax::ast;
2
3use crate::{AstCtx, Example, Violation};
4
5#[rustfmt::skip]
6const EXAMPLES: &[Example] = &[
7    Example {
8        label: "bare transmute without safety comment",
9        code: "unsafe fn f() { let _x: u32 = std::mem::transmute(1.0f32); }",
10        pass: false,
11    },
12    Example {
13        label: "transmute with safety comment",
14        code: "unsafe fn f() {\n    // SAFETY: f32 and u32 have the same size\n    let _x: u32 = std::mem::transmute(1.0f32);\n}",
15        pass: true,
16    },
17    Example {
18        label: "transmute in test module",
19        code: "#[cfg(test)]\nmod tests {\n    unsafe fn t() { let _x: u32 = std::mem::transmute(1.0f32); }\n}",
20        pass: true,
21    },
22];
23
24crate::ast_rule!(
25    transmute_usage,
26    "Require `SAFETY` comment on `std::mem::transmute` calls.",
27    "transmute reinterprets raw bytes and can cause UB if the types are incompatible. A SAFETY comment proves correctness.",
28    High,
29);
30
31fn check_transmute_usage(ctx: &AstCtx<'_>) -> Vec<Violation> {
32    ctx.nodes::<ast::CallExpr>()
33        .filter(|call| !ctx.is_in_test(call))
34        .filter_map(|call| {
35            let ast::Expr::PathExpr(path_expr) = call.expr()? else {
36                return None;
37            };
38            let path = path_expr.path()?;
39
40            if path
41                .segment()
42                .and_then(|segment| segment.name_ref())
43                .is_none_or(|name| name.text() != "transmute")
44            {
45                return None;
46            }
47
48            let line = ctx.line_of(&path);
49
50            (!crate::infra::helpers::has_preceding_comment(ctx.file.lines, line, &["SAFETY:"]))
51                .then(|| {
52                    ctx.violation(
53                        &path,
54                        "std::mem::transmute without // SAFETY: comment on a preceding line",
55                    )
56                })
57        })
58        .collect()
59}
60
61crate::tidy_ast_test!(check_transmute_usage, {
62    crate::example_tests!(EXAMPLES, check_transmute_usage);
63});