Capturing doc comments in declarative macros

# rust # macros

Sometimes, when writing declarative macros in Rust, we may want to capture not only the code itself, but also doc comments for that code. Intuitively one could write something like:

macro_rules! create (($name:ident, $bytes:expr) => (
pub struct $name(pub [u8; $bytes]);
));
/// Really important comment.
create!(SomeType, 42);

Unfortunately, it’s not gonna work, and the compiler will promptly warn us:

$ cargo check
8 | /// Really important comment.
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rustdoc does not generate documentation for macro invocations
|
= help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion
= note: `#[warn(unused_doc_comments)]` on by default

As rustc suggests, the solution is to move the doc comment into macro body and capture it. This will work because doc comments desugar into attributes. So the example above can be rewritten as:

#[doc="Really important comment."]
create!(SomeType, 42);

And, as you may have guessed, it is possible to match against attributes in macros. In fact, many libraries leverage that fact, e.g. quick_error or bitflags. With that in mind, we can rewrite our macro to capture doc comments.

To match against the attributes, we can use the meta metavariable:

macro_rules! create {
($(#[$attr:meta])* ($name:ident, $bytes:expr)) => {
$(#[$attr])*
pub struct $name(pub [u8; $bytes]);
};
}
create! {
/// Really important comment.
(SomeType, 42)
}

Now the SomeType struct will have the doc comment we defined in macro body.