|
3 | 3 |
|
4 | 4 | .. default-domain:: coding-guidelines |
5 | 5 |
|
6 | | -Do not hide unsafe blocks within macro expansions |
7 | | -================================================= |
| 6 | +Do not hide unsafe code in macros |
| 7 | +================================ |
8 | 8 |
|
9 | | -.. guideline:: Do not hide unsafe blocks within macro expansions |
| 9 | +.. guideline:: Do not hide unsafe code in macros |
10 | 10 | :id: gui_FRLaMIMb4t3S |
11 | | - :category: required |
| 11 | + :category: advisory |
12 | 12 | :status: draft |
13 | | - :release: todo |
| 13 | + :release: unclear-latest |
14 | 14 | :fls: fls_4vjbkm4ceymk |
15 | | - :decidability: todo |
16 | | - :scope: todo |
17 | | - :tags: reduce-human-error |
| 15 | + :decidability: decidable |
| 16 | + :scope: module |
| 17 | + :tags: safety, reduce-human-error |
18 | 18 |
|
19 | | - Description of the guideline goes here. |
| 19 | + Do not hide unsafe code in macro definitions. |
| 20 | + Macros that expand to unsafe code should preserve the ``unsafe`` token visibility. |
20 | 21 |
|
21 | 22 | .. rationale:: |
22 | | - :id: rat_WJubG7KuUDLW |
| 23 | + :id: rat_s7ffMlPUFt77 |
23 | 24 | :status: draft |
24 | 25 |
|
25 | | - Explanation of why this guideline is important. |
| 26 | + Macros that generate unsafe code obscure safety-critical code, making the code more difficult to review and audit. |
26 | 27 |
|
27 | 28 | .. non_compliant_example:: |
28 | | - :id: non_compl_ex_AyFnP0lJLHxi |
| 29 | + :id: non_compl_ex_YNX7AnWENTu7 |
29 | 30 | :status: draft |
30 | 31 |
|
31 | | - Explanation of code example. |
| 32 | + Macros that generate unsafe code without preserving the ``unsafe`` token visibility obscure safety-critical code. |
| 33 | + This noncompliant example defines a ``deref_ptr`` macro that performs an unsafe pointer dereference. |
32 | 34 |
|
33 | 35 | .. rust-example:: |
34 | 36 |
|
35 | | - #[allow(dead_code)] |
36 | | - fn example_function() { |
37 | | - // Non-compliant implementation |
| 37 | + // This macro hides the unsafe token from callers - noncompliant |
| 38 | + macro_rules! deref_ptr { |
| 39 | + ($ptr:expr) => { |
| 40 | + unsafe { *$ptr } |
| 41 | + }; |
| 42 | + } |
| 43 | +
|
| 44 | + fn main() { |
| 45 | + let x = 42; |
| 46 | + let ptr = &x as *const i32; |
| 47 | + // The unsafe operation is hidden from the caller |
| 48 | + println!("val = {}", deref_ptr!(ptr)); |
38 | 49 | } |
39 | | - # |
40 | | - # fn main() {} |
41 | 50 |
|
42 | 51 | .. compliant_example:: |
43 | | - :id: compl_ex_pO5gP1aj2v4F |
| 52 | + :id: compl_ex_nBkfzueTWvTA |
44 | 53 | :status: draft |
45 | 54 |
|
46 | | - Explanation of code example. |
| 55 | + This compliant example requires the caller to wrap the macro invocation in an ``unsafe`` block, |
| 56 | + making the use of unsafe code obvious at the call site. |
| 57 | + Visibility can be further improved by prefixing the identifier for each unsafe macro with the string "unsafe_". |
47 | 58 |
|
48 | 59 | .. rust-example:: |
49 | 60 |
|
50 | | - #[allow(dead_code)] |
51 | | - fn example_function() { |
52 | | - // Compliant implementation |
| 61 | + // This macro requires the caller to acknowledge the unsafe operation - compliant |
| 62 | + macro_rules! unsafe_deref_ptr { |
| 63 | + ($ptr:expr) => { |
| 64 | + *$ptr |
| 65 | + }; |
| 66 | + } |
| 67 | +
|
| 68 | + fn main() { |
| 69 | + let x = 42; |
| 70 | + let ptr = &x as *const i32; |
| 71 | + // The unsafe operation is visible at the call site |
| 72 | + let val = unsafe { unsafe_deref_ptr!(ptr) }; |
| 73 | + println!("val = {}", val); |
53 | 74 | } |
54 | | - # |
55 | | - # fn main() {} |
|
0 commit comments