Do not hide unsafe code in macros

Guideline: Do not hide unsafe code in macros gui_FRLaMIMb4t3S
status: draft
tags: safety, reduce-human-error
category: advisory
decidability: decidable
scope: module
release: unclear-latest

Do not hide unsafe code in macro definitions. Macros that expand to unsafe code should preserve the unsafe token visibility.

Rationale: rat_s7ffMlPUFt77
status: draft
parent needs: gui_FRLaMIMb4t3S

Macros that generate unsafe code obscure safety-critical code, making the code more difficult to review and audit.

Non-Compliant Example: non_compl_ex_YNX7AnWENTu7
status: draft
parent needs: gui_FRLaMIMb4t3S

Macros that generate unsafe code without preserving the unsafe token visibility obscure safety-critical code. This noncompliant example defines a deref_ptr macro that performs an unsafe pointer dereference.

miri
// This macro hides the unsafe token from callers - noncompliant
macro_rules! deref_ptr {
    ($ptr:expr) => {
        unsafe { *$ptr }
    };
}

fn main() {
    let x = 42;
    let ptr = &x as *const i32;
    // The unsafe operation is hidden from the caller
    println!("val = {}", deref_ptr!(ptr));
}
Compliant Example: compl_ex_nBkfzueTWvTA
status: draft
parent needs: gui_FRLaMIMb4t3S

This compliant example requires the caller to wrap the macro invocation in an unsafe block, making the use of unsafe code obvious at the call site. Visibility can be further improved by prefixing the identifier for each unsafe macro with the string unsafe_.

miri
// This macro requires the caller to acknowledge the unsafe operation - compliant
macro_rules! unsafe_deref_ptr {
    ($ptr:expr) => {
        *$ptr
    };
}

fn main() {
    let x = 42;
    let ptr = &x as *const i32;
    // The unsafe operation is visible at the call site
    let val = unsafe { unsafe_deref_ptr!(ptr) };
    println!("val = {}", val);
}