Generating documentation in macros

2019-01-12 02:32发布

问题:

I have a couple of macros to reduce boilerplate when defining certain tuple-structs of the form:

macro_rules! new_type (($name:ident, $bytes:expr) => (
    pub struct $name(pub [u8; $bytes]);
    // some common operations on $name
));

However, I would also like to document these new structs. The best thing would be if I could write my documentation right before my macro invocation.

/// A certain type
new_type!(CertainType, 42);

However, Rust won't generate documentation for CertainType when this happens.

Another (not as flexible) alternative would be to do something like:

macro_rules! new_type (($name:ident, $bytes:expr) => (
    /// Some more generic documentation for $name 
    pub struct $name(pub [u8; $bytes]);
    // some common operations on $name
));

However, when doing that the Rust macro system doesn't expand the token $name in the documentation comment. The only alternative left is to write very generic documentation in the macro, but that would lead to my library being a lot worse documented than it could be.

What are your recommendations for handling this? The best solution for me would be to be able to write specific documentation for each macro invocation, but if that's not possible I would be grateful for hints on how to expand tokens in documentation comments.

回答1:

It is possible to capture doc comments in macro invocations. It is not widely-known, but Rust documentation is actually represented as a special kind of attribute on an item. For example:

/// Some documentation comment
pub fn function() {}

// is equivalent to

#[doc="Some documentation comment"]
pub fn function() {}

And it is possible to capture attributes in macros. There are already several macros which use this ability, the most used probably being bitflags!:

macro_rules! bitflags {
    (
        $(#[$outer:meta])*
        pub struct $BitFlags:ident: $T:ty {
            $(
                $(#[$inner:ident $($args:tt)*])*
                const $Flag:ident = $value:expr;
            )+
        }
    ) => { /* ... */ };
    // ...
}

Note the $(#[$outer:meta])* and $(#[$inner:meta])* parts of the pattern. These capture all attributes placed before the respective item in the pattern. If you write a doc comment there, it will be converted to the doc attribute and will be passed to rustdoc, as usual.

The following is an example from the quick_error crate which also uses this approach:

quick_error! {
    #[derive(Debug)]
    pub enum SomeError {
        /// IO Error
        Io(err: io::Error) {}
        /// Arbitrary system error
        Sys(errno: nix::Errno) {}
    }
}

It does work — here is an example of the structure generated by quick_error macro, and here is its definition.



标签: macros rust