rustrust-macrosrust-decl-macros

Is there a way to "do macro stuff" in an attribute without a procedural macro?


Specifically, I'm trying to put macro output into a doc comment. I was excited that this does exactly what I want:

/// foo
///
#[doc="bar\n\nbaz"]
///
/// quux
struct Dummy;

The next step would be to replace that string with my content. According to this, I can't write #[doc=my_content!()], and attribute macros are procedural, so I need another crate for that, plus (I think) my content could be generated without the need for any procedural macro features.

Is there a way to do this with "conventional macros" in some way, or am I out of luck?


Solution

  • Edit: starting with 1.54.0, Attributes can invoke function-like macros, enabling the code proposed in the question. Original answer below :


    The answer seems to be no.

    Looking at the grammar for attributes, apart from parentheses, commas and equals signs, attributes can ultimately only contain literals. So on this level, there is no way Rust allows more here.


    However, inverting the structure enables something like this, and the doc-comment crate does this for doc comments. Instead of calling a macro from inside an attribute, use a macro to create an attribute; that macro is then not constrained to only taking literals*. The downside is, the item that the attribute should apply to must be part of the macro call. So this

    #[doc=my_content!()]
    struct Foo;
    

    becomes this:

    doc_comment!(
        my_content!(),
        struct Foo;
    );
    

    The definition of the macro is straight-forward:

    #[macro_export]
    macro_rules! doc_comment {
        ($x:expr, $($tt:tt)*) => {
            #[doc = $x]
            $($tt)*
        };
    }
    

    (omitted a branch of the original macro that is not part of the core pattern)

    (thanks to jonas-schlevink for pointing me to this)


    *except for that last part (getting macro content into the attribute), the linked question's answer already does exactly that.