Capturing doc comments in declarative macros

2 mins to read

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 compiler will even warn us:

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 the compiler suggests (thanks, rustc!), 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.