wowlab_tidy/languages/rust/rules/correctness/
mem_forget.rs1use ra_ap_syntax::{AstNode, ast};
2
3use crate::{AstCtx, Example, Violation};
4
5#[rustfmt::skip]
6const EXAMPLES: &[Example] = &[
7 Example {
8 label: "bare forget without comment",
9 code: "fn f() { std::mem::forget(String::new()); }",
10 pass: false,
11 },
12 Example {
13 label: "forget with LEAK comment",
14 code: "fn f() {\n // LEAK: intentionally leaked for static lifetime\n std::mem::forget(String::new());\n}",
15 pass: true,
16 },
17 Example {
18 label: "forget with SAFETY comment",
19 code: "fn f() {\n // SAFETY: ownership transferred to FFI\n std::mem::forget(String::new());\n}",
20 pass: true,
21 },
22 Example {
23 label: "forget in test module",
24 code: "#[cfg(test)]\nmod tests {\n fn t() { std::mem::forget(String::new()); }\n}",
25 pass: true,
26 },
27 Example {
28 label: "unrelated forget function",
29 code: "fn f() { cache::forget(key); }",
30 pass: true,
31 },
32];
33
34crate::ast_rule!(
35 mem_forget,
36 "Require `LEAK` or `SAFETY` comment on `std::mem::forget()` calls.",
37 "mem::forget permanently leaks memory. A justification comment proves the leak is intentional, not a bug.",
38 High,
39);
40
41fn check_mem_forget(ctx: &AstCtx<'_>) -> Vec<Violation> {
42 ctx.nodes::<ast::CallExpr>()
43 .filter(|call| !ctx.is_in_test(call))
44 .filter_map(|call| {
45 let ast::Expr::PathExpr(path_expr) = call.expr()? else {
46 return None;
47 };
48 let path = path_expr.path()?;
49 let source = path.syntax().text().to_string();
50 let is_forget = path
51 .segment()
52 .and_then(|segment| segment.name_ref())
53 .is_some_and(|name| name.text() == "forget")
54 && source.split("::").any(|segment| segment == "mem");
55
56 if !is_forget {
57 return None;
58 }
59
60 let line = ctx.line_of(&path);
61
62 (!crate::infra::helpers::has_preceding_comment(
63 ctx.file.lines,
64 line,
65 &["LEAK:", "SAFETY:"],
66 ))
67 .then(|| {
68 ctx.violation(
69 &path,
70 "std::mem::forget without // LEAK: or // SAFETY: comment on a preceding line",
71 )
72 })
73 })
74 .collect()
75}
76
77crate::tidy_ast_test!(check_mem_forget, {
78 crate::example_tests!(EXAMPLES, check_mem_forget);
79});