12

I'd like to use a macro variable in the macro-generated documentation:

macro_rules! impl_foo {
    ($name:ident) => {
        /// Returns a new `$name`.
        fn myfoo() -> $name {

        }
    };
}

However, the variable won't be substituted. I also tried using the #[doc] attribute:

macro_rules! impl_foo {
    ($name:ident) => {
        #[doc = concat!("Returns a new `", $name, "`.")]
        fn myfoo() -> $name {

        }
    };
}

This one even fails to parse: unexpected token: 'concat'

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
torkleyy
  • 1,137
  • 9
  • 26
  • @Shepmaster I couldn't find it, but I wonder why the second one fails to parse. – torkleyy Apr 11 '17 at 18:21
  • The set of allowed tokens inside an attribute is probably pretty limited. – Shepmaster Apr 11 '17 at 18:24
  • 1
    @torkleyy Macros are not expanded when parsing (which is why there is [RFC 1628 "Eager expansion of macros"](https://github.com/rust-lang/rfcs/pull/1628)) – kennytm Apr 11 '17 at 18:34
  • @Shepmaster I though procedural macros allowed any token in attributes now (but the `#[doc]` attribute itself won't know what to do with those). – mcarton Apr 11 '17 at 18:42

2 Answers2

19

This can be done using a recursive macro:

macro_rules! impl_foo {
    ($name:ident, $sname:expr) => {
        #[doc = "Returns a new `"]
        #[doc = $sname]
        #[doc = "`."]
        pub fn myfoo() -> $name {
            42
        }
    };

    ($name:tt) => {
        impl_foo!($name, stringify!($name));
    };
}

impl_foo!(u32);


fn main() {
    println!("Hello, world!");
}

Which renders as:

Example from rustdoc

mcarton
  • 27,633
  • 5
  • 85
  • 95
6

While the answer @mcarton gave does work perfectly fine for simple examples, it breaks a bit for more complicated ones. Rustdoc seems to insert spaces between the different doc attributes. The markdown processor strips them out most of the time, but sometimes, it transform them to spaces instead. Consider this example:

macro_rules! impl_foo {
    ($name:ident, $sname:expr) => {
        #[doc = "You can call this as `myfoo("]
        #[doc = $sname]
        #[doc = ")`."]
        pub fn myfoo(_: $name) -> $name {
            42
        }
    };

    ($name:tt) => {
        impl_foo!($name, stringify!($name));
    };
}

impl_foo!(i32);


fn main() {
    println!("Hello, world!");
}

This should generate the documentation "You can call this as myfoo(i32).", but in reality, it results in "You can call this as myfoo( i32 )." (note the additional spaces):Screenshot from the rustdoc page showing the additional spaces

I'm not too sure whether my solution would have worked with the 2017 rustc back when the question was asked, but in modern Rust, this can be done by combining stringify! with concat!:

macro_rules! impl_foo {
    ($name:tt) => {
        #[doc = concat!("You can call this as `myfoo(", stringify!($name), ")`.")]
        pub fn myfoo(_: $name) -> $name {
            42
        }
    };
}

impl_foo!(i32);


fn main() {
    println!("Hello, world!");
}

This results in the documentation you want (so, without superfluous spaces):

Screenshot from the fixed rustdoc page

Elias Holzmann
  • 3,216
  • 2
  • 17
  • 33