Imagine you're writing this macro:
/// Types that can be frobnicated.
///
/// # Safety
///
/// It must be sound to frobnicate `Self`.
unsafe
/// Implements `Frobnicatable` for `$t`.
///
/// # Safety
///
/// `$t` must satisfy the safety invariant of `Frobnicatable`.
Of course, you'd never actually write a macro to emit code this simple, but it's a meant as a stand-in for macros that emit more complex code.
A caller might write this:
;
// SAFETY: It is sound to frobnicate `Foo`.
unsafe_impl_frobnicatable!;
But imagine that your caller wants to practice good safety hygeine, and they use
#![deny(clippy::undocumented_unsafe_blocks)].
Because Clippy doesn't realize that the macro call requires a safety comment, it
won't complain if your caller forgets their safety comment:
;
// Clippy: 🙈
unsafe_impl_frobnicatable!;
Instead, you should write your macro so that it invokes unsafe code. This also
means that we can drop the unsafe_ prefix from the macro's name – it was just
there to make it less likely for your callers to miss the safety requirement,
but that's no longer a concern. Also, make sure to have the macro's right-hand
side contain a block – ie, {{ ... }} – see the appendix for an explanation of
why this is important.
/// Implements `Frobnicatable` for `$t`.
///
/// # Safety
///
/// `$t` must satisfy the safety invariant of `Frobnicatable`.
pub const unsafe
Now, even when Clippy isn't used, Rust will force your caller to put the macro
invocation in an unsafe block. In other words, this will no longer compile:
// SAFETY: It is sound to frobnicate `Foo`.
impl_frobnicatable!;
To fix this, your caller must wrap the invocation in an unsafe block, for
example:
// SAFETY: It is sound to frobnicate `Foo`.
const _: = unsafe ;
And now Clippy will catch a missing safety comment since your caller is using
a normal unsafe block.
Appendix: Macro expansion
Note one subtlety: You're going to want to have your macro emit a block (ie, use
(...) => {{ ... }} rather than (...) => { ... }). You're now requiring your
callers to call your macro in expression or statement position (ie, inside of a
block) rather than in item position (ie, at the top-level, outside of a block).
Thus, it has to play nicely with code before and after it in the same block.
Expanding to a self-contained block is a good way of ensuring that this will
happen.