Skip to main content

wowlab_tidy/languages/rust/rules/safety/
unsafe_comment.rs

1use ra_ap_syntax::ast::{self};
2
3use crate::{AstCtx, Example, Violation};
4
5#[rustfmt::skip]
6const EXAMPLES: &[Example] = &[
7    Example {
8        label: "unsafe without safety comment",
9        code: "fn f() { unsafe { std::ptr::null::<u8>().read() }; }",
10        pass: false,
11    },
12    Example {
13        label: "unsafe with safety comment",
14        code: "fn f() {\n    // SAFETY: pointer is valid and aligned\n    unsafe { std::ptr::null::<u8>().read() };\n}",
15        pass: true,
16    },
17    Example {
18        label: "unsafe in test module",
19        code: "#[cfg(test)]\nmod tests {\n    fn t() { unsafe { std::ptr::null::<u8>().read() }; }\n}",
20        pass: true,
21    },
22    Example {
23        label: "safety comment with blank line",
24        code: "fn f() {\n    // SAFETY: guaranteed valid\n\n    unsafe { std::ptr::null::<u8>().read() };\n}",
25        pass: true,
26    },
27];
28
29crate::ast_rule!(
30    unsafe_comment,
31    "Require `// SAFETY:` comment on `unsafe` blocks.",
32    "Every unsafe block must document why it is sound. Without a SAFETY comment, reviewers cannot verify correctness.",
33    High,
34);
35
36fn check_unsafe_comment(ctx: &AstCtx<'_>) -> Vec<Violation> {
37    ctx.nodes::<ast::BlockExpr>()
38        .filter(|block| block.unsafe_token().is_some() && !ctx.is_in_test(block))
39        .filter_map(|block| {
40            let unsafe_token = block.unsafe_token()?;
41            let line = ctx
42                .line_index
43                .line_col(unsafe_token.text_range().start())
44                .line as usize
45                + 1;
46
47            (!crate::infra::helpers::has_preceding_comment(ctx.file.lines, line, &["SAFETY:"]))
48                .then(|| {
49                    ctx.violation(
50                        &block,
51                        "unsafe block without // SAFETY: comment on a preceding line",
52                    )
53                })
54        })
55        .collect()
56}
57
58crate::tidy_ast_test!(check_unsafe_comment, {
59    crate::example_tests!(EXAMPLES, check_unsafe_comment);
60});